web-dev-qa-db-fra.com

Émuler la clause LIMITE MySQL dans Microsoft SQL Server 2000

Lorsque j'ai travaillé sur le composant de base de données de Zend Framework , nous avons essayé d'abstraire la fonctionnalité de la clause LIMIT supportée par MySQL, PostgreSQL et SQLite. Autrement dit, la création d'une requête peut se faire de cette façon:

$select = $db->select();
$select->from('mytable');
$select->order('somecolumn');
$select->limit(10, 20);

Lorsque la base de données prend en charge LIMIT, cela génère une requête SQL comme celle-ci:

SELECT * FROM mytable ORDER BY somecolumn LIMIT 10, 20

C'était plus complexe pour les marques de base de données qui ne prennent pas en charge LIMIT (cette clause ne fait pas partie du langage SQL standard, soit dit en passant). Si vous pouvez générer des numéros de ligne, faites de la requête entière une table dérivée et, dans la requête externe, utilisez BETWEEN. C'était la solution pour Oracle et IBM DB2. Microsoft SQL Server 2005 a une fonction de numéro de ligne similaire, donc on peut écrire la requête de cette façon:

SELECT z2.*
FROM (
    SELECT ROW_NUMBER OVER(ORDER BY id) AS zend_db_rownum, z1.*
    FROM ( ...original SQL query... ) z1
) z2
WHERE z2.zend_db_rownum BETWEEN @offset+1 AND @offset+@count;

Cependant, Microsoft SQL Server 2000 n'a pas la fonction ROW_NUMBER().

Donc ma question est, pouvez-vous trouver un moyen d'émuler la fonctionnalité LIMIT dans Microsoft SQL Server 2000, en utilisant uniquement SQL? Sans utiliser de curseurs ou T-SQL ou une procédure stockée. Il doit prendre en charge les deux arguments pour LIMIT, à la fois count et offset. Les solutions utilisant une table temporaire ne sont pas non plus acceptables.

Modifier:

La solution la plus courante pour MS SQL Server 2000 semble être celle ci-dessous, par exemple pour obtenir les lignes 50 à 75:

SELECT TOP 25 *
FROM ( 
  SELECT TOP 75 *
  FROM   table 
  ORDER BY BY field ASC
) a 
ORDER BY field DESC;

Cependant, cela ne fonctionne pas si l'ensemble de résultats total est, disons 60 lignes. La requête interne renvoie 60 lignes car elle se trouve dans les 75 premiers. Ensuite, la requête externe renvoie les lignes 35 à 60, ce qui ne correspond pas à la "page" souhaitée de 50 à 75. Fondamentalement, cette solution fonctionne sauf si vous avez besoin de la dernière "page" d'un jeu de résultats qui ne se trouve pas être un multiple de la taille de la page.

Modifier:

Une autre solution fonctionne mieux, mais uniquement si vous pouvez supposer que le jeu de résultats comprend une colonne unique:

SELECT TOP n *
FROM tablename
WHERE key NOT IN (
    SELECT TOP x key
    FROM tablename
    ORDER BY key
);

Conclusion:

Aucune solution à usage général ne semble exister pour émuler LIMIT dans MS SQL Server 2000. Une bonne solution existe si vous pouvez utiliser la fonction ROW_NUMBER() dans MS SQL Server 2005.

39
Bill Karwin

Voici une autre solution qui ne fonctionne que dans Sql Server 2005 et plus récent car elle utilise l'instruction except. Mais je le partage quand même. Si vous voulez obtenir les enregistrements 50 - 75, écrivez:

select * from (
    SELECT top 75 COL1, COL2
    FROM MYTABLE order by COL3
) as foo
except
select * from (
    SELECT top 50 COL1, COL2
    FROM MYTABLE order by COL3
) as bar
5
Florian Fankhauser
SELECT TOP n *
FROM tablename
WHERE key NOT IN (
    SELECT TOP x key
    FROM tablename
    ORDER BY key
    DESC
);
5
a0p

Lorsque vous n'avez besoin que de LIMIT, ms sql a le mot-clé TOP équivalent, c'est clair. Lorsque vous avez besoin de LIMIT avec OFFSET, vous pouvez essayer certains hacks comme décrit précédemment, mais ils ajoutent tous une surcharge, c'est-à-dire pour commander dans un sens puis dans l'autre, ou l'opération NOT IN expencive. Je pense que toutes ces cascades ne sont pas nécessaires. La solution la plus propre à mon avis serait simplement d'utiliser TOP sans décalage du côté SQL, puis de rechercher l'enregistrement de démarrage requis avec la méthode client appropriée, comme mssql_data_seek en php. Bien que ce ne soit pas une solution SQL pure, je pense que c'est la meilleure car elle n'ajoute pas de surcharge (les enregistrements ignorés ne seront pas transférés sur le réseau lorsque vous les dépasserez, si c'est ce qui vous inquiète) ).

4
grr

J'essaierais d'implémenter cela dans mon ORM car c'est assez simple là-bas. S'il a vraiment besoin d'être dans SQL Server, je regarderais le code généré par linq to sql pour l'instruction linq to sql suivante et j'irais à partir de là. L'ingénieur MSFT qui a implémenté ce code a fait partie de l'équipe SQL pendant de nombreuses années et savait ce qu'il faisait.

var result = myDataContext.mytable.Skip (pageIndex * pageSize) .Take (pageSize)

0
Barka