2008-10-19 9 views
37

Lorsque j'ai travaillé sur le Zend Framework's database component, nous avons essayé de résumer la fonctionnalité de la clause LIMIT supportée par MySQL, PostgreSQL et SQLite. Cela est, la création d'une requête pourrait se faire de cette façon:Emulation de la clause MySQL LIMIT dans Microsoft SQL Server 2000

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

Lorsque la base de données prend en charge LIMIT, cela produit une requête SQL comme suit:

SELECT * FROM mytable ORDER BY somecolumn LIMIT 10, 20 

Cela a été plus complexe pour les marques de base de données ne supporte pas LIMIT (cette clause ne fait pas partie du langage SQL standard, d'ailleurs). Si vous pouvez générer des numéros de ligne, rendez l'ensemble de la requête 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 dispose d'une fonction ligne numéro similaire, on peut donc é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 @[email protected]; 

Cependant, Microsoft SQL Server 2000 ne dispose pas de 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 compte et décalage. Les solutions utilisant une table temporaire ne sont pas non plus acceptables.

Edit:

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

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

Cependant, ce n » t travailler si le résultat total est de, disons 60 lignes. La requête interne renvoie 60 lignes parce que c'est dans le top 75. Ensuite, la requête externe renvoie les lignes 35-60, qui ne rentre pas dans la "page" souhaitée de 50-75. Fondamentalement, cette solution fonctionne à moins que vous ayez besoin de la dernière "page" d'un ensemble de résultats qui ne soit pas un multiple de la taille de la page.

Edit:

Une autre solution fonctionne mieux, mais seulement si vous pouvez supposer 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:

Aucun général solution semble exister pour émuler LIMIT dans MS SQL Server 2000. Une bonne solution existe si vous pouvez utiliser la fonct ROW_NUMBER() ion dans MS SQL Server 2005.

Répondre

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

Oui, c'est proche, mais il ne fonctionne que quand il y a une clé unique dans le résultat de la requête provisoire. Comment le feriez-vous dans une requête GROUP BY ou pour une requête qui rejoint plusieurs tables? –

4

Lorsque vous avez besoin de LIMIT seulement, ms sql a le mot-clé TOP équivalent, donc 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 un peu de frais généraux, c'est-à-dire pour commander d'une manière et puis l'autre. Je pense que toutes ces cascades ne sont pas nécessaires. La solution la plus propre dans mon oppinion serait juste utiliser TOP sans décalage sur le côté SQL, puis chercher à l'enregistrement de départ 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 parce qu'elle n'ajoute aucun frais supplémentaire (les enregistrements ignorés ne seront pas transférés sur le réseau quand vous les cherchez, si c'est ce qui vous inquiète).

+0

Je suis d'accord avec votre recommandation lors de la conception d'une application. Dans mon cas, j'étais en train de concevoir un framework d'accès à la base de données supposé avoir une API cohérente mais produisant des SQL différents selon les besoins pour différentes marques de bases de données. La solution devait être dans la préparation SQL, pas dans la récupération. –

+1

Bonne idée, merci beaucoup. Globalement, je pense que votre solution est la plus viable. – 0plus1

+0

C'est pourquoi je n'aime pas MSSQL! –

5

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

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 
+0

Merci, il semblerait que cela fonctionnerait aussi dans Microsoft SQL Server 2005. Cela ne nous laisse toujours pas de bonne solution pour Microsoft SQL Server 2000. Mais je suppose que depuis 2009, il est finalement raisonnable d'abandonner le support pour 2000 de toute façon. –

+0

Oui, cela fonctionne dans Sql Server 2005. –

+0

C'est intelligent! Je n'avais pas pensé à le faire de cette façon. – jocull

0

Je voudrais essayer de mettre en œuvre dans mon ORM car il est assez là simple. Si cela doit vraiment être dans SQL Server, je regarderais le code généré par linq vers sql pour l'instruction linq to sql suivante et partirais de là. L'ingénieur MSFT qui a implémenté ce code faisait partie de l'équipe SQL depuis de nombreuses années et savait ce qu'il faisait.

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

+0

http://msdn.microsoft.com/en-us/library/bb386988.aspx dit: "La traduction est différente pour SQL Server 2000 et SQL Server 2005. Si vous envisagez d'utiliser Skip (Of TSource) avec une requête de tout la complexité, utilisez SQL Server 2005. " –

+0

Mon point est qu'il ya beaucoup de bonnes solutions pour ce problème si la condition préalable est d'exécuter MS SQL Server 2005. Mais peu d'entre eux fonctionnent dans le cas général sur MS SQL Server 2000. Certes, nous sommes maintenant 2011, et il est de plus en plus difficile pour justifier l'exécution de MS SQL Server 2000 au fil du temps. Pourtant, certaines personnes font encore! :( –

Questions connexes