Équivalent de LIMIT et OFFSET pour SQL Server?


172

Dans PostgreSQL, il existe les mots Limit- Offsetclés et qui permettront une pagination très simple des ensembles de résultats.

Quelle est la syntaxe équivalente pour SQL Server?


Pour SQL Server 2012, cette fonctionnalité est implémentée de manière simple. Voir ma réponse
Somnath Muluk

Merci d'avoir posé cette question, nous sommes obligés de passer de MySQL à MsSQL :(
tempcke

Vous pouvez utiliser offset et récupérer l'instruction suivante dans le serveur SQL avec une clause order by. Essayez-le youtu.be/EqHkAiiBwPc
Amresh Kumar Singh

Réponses:


139

L'équivalent de LIMITis SET ROWCOUNT, mais si vous voulez une pagination générique, il est préférable d'écrire une requête comme celle-ci:

;WITH Results_CTE AS
(
    SELECT
        Col1, Col2, ...,
        ROW_NUMBER() OVER (ORDER BY SortCol1, SortCol2, ...) AS RowNum
    FROM Table
    WHERE <whatever>
)
SELECT *
FROM Results_CTE
WHERE RowNum >= @Offset
AND RowNum < @Offset + @Limit

L'avantage ici est le paramétrage de l'offset et de la limite au cas où vous décideriez de modifier vos options de pagination (ou autoriser l'utilisateur à le faire).

Remarque: le @Offsetparamètre doit utiliser pour cela l'indexation basée sur un plutôt que l'indexation de base zéro normale.


22
Vieux maintenant. Sql Server 2012 et versions ultérieures prennent en charge OFFSET / FETCH
Joel Coehoorn

31
@JoelCoehoorn Pas vieux. Je viens d'être affecté à un projet en utilisant SLQ Server 2008 après avoir utilisé uniquement mysql dans le passé ...
Cthulhu

C'est assez bon mais a besoin d'être ajusté un peuWHERE RowNum >= (@Offset + 1)
Eric Herlitz

5
The ORDER BY clause is invalid in views, inline functions, derived tables, subqueries, and common table expressions, unless TOP or FOR XML is also specified. MSSQL2008 R2.
Paul

2
@Aaronaught Si mon Tablea 200k enregistrements, il va tout d'abord récupérer, puis appliquer la limite? Cette requête est-elle efficace?
Jigar

231

Cette fonctionnalité est désormais simplifiée dans SQL Server 2012. Cela fonctionne à partir de SQL Server 2012.

Limiter avec décalage pour sélectionner 11 à 20 lignes dans SQL Server:

SELECT email FROM emailTable 
WHERE user_id=3
ORDER BY Id
OFFSET 10 ROWS
FETCH NEXT 10 ROWS ONLY;
  • OFFSET: nombre de lignes ignorées
  • NEXT: nombre requis de lignes suivantes

Référence: https://docs.microsoft.com/en-us/sql/t-sql/queries/select-order-by-clause-transact-sql?view=sql-server-2017


4
Y a-t-il un équivoque SQL_CALC_FOUND_ROWSlors de son utilisation?
Petah

1
@Petah @@ Rowcount vous donnera ce que je pense
Rob Sedgwick

GOTCHA: Vous ne pouvez pas utiliser cela à partir d'un CTE. Il doit être utilisé dans la requête principale. Je voulais limiter le nombre de lignes renvoyées (pagination), puis effectuer un calcul coûteux aux quelque 10 lignes renvoyées, plutôt que de déterminer les lignes, d'effectuer le calcul coûteux, puis de sauter / prendre ce dont j'avais besoin. La réponse de @ Aaronaught fonctionnera pour ceux qui ont besoin de restreindre les lignes dans un CTE.
Derreck Dean

@Somnath Muluk Cet exemple de décalage et de récupération prend beaucoup de temps pour un volume plus élevé de données avec un décalage de 1000000. Comment puis-je gérer cela.
Saroj Shrestha

1
@SarojShrestha: Il ne s'agit pas d'un problème de décalage et de récupération. Vous devriez revoir l'architecture de votre table maintenant. Envisagez le partitionnement des tables, votre ligne de données et ses différents types de colonnes et la taille totale de la table, envisagez d'archiver certaines lignes si cela n'est pas nécessaire régulièrement, vérifiez les spécifications de votre serveur.
Somnath Muluk

23
select top {LIMIT HERE} * from (
      select *, ROW_NUMBER() over (order by {ORDER FIELD}) as r_n_n 
      from {YOUR TABLES} where {OTHER OPTIONAL FILTERS}
) xx where r_n_n >={OFFSET HERE}

Remarque: cette solution ne fonctionnera que dans SQL Server 2005 ou supérieur, car c'était à ce moment-là qu'elle a ROW_NUMBER()été implémentée.


J'utilise cette requête depuis un petit moment maintenant et cela fonctionne très bien, merci pour cela. Je me demande simplement ce que représente le «xx»?
Urbley

la sous-requête nécessite un nom. car je ne l'utilise pas, il suffit de mettre xx là
jorgeu

2
Le xx n'est qu'un alias de table. Ce serait peut-être un peu plus clair si vous disiezAS xx
Concrete Gannet

quelqu'un sait comment faire la jointure gauche sur cette requête?
Drenyl

12

Vous pouvez utiliser ROW_NUMBER dans une expression de table commune pour y parvenir.

;WITH My_CTE AS
(
     SELECT
          col1,
          col2,
          ROW_NUMBER() OVER(ORDER BY col1) AS row_number
     FROM
          My_Table
     WHERE
          <<<whatever>>>
)
SELECT
     col1,
     col2
FROM
     My_CTE
WHERE
     row_number BETWEEN @start_row AND @end_row

4

Pour moi, l'utilisation de OFFSET et FETCH ensemble était lente, j'ai donc utilisé une combinaison de TOP et OFFSET comme celle-ci (ce qui était plus rapide):

SELECT TOP 20 * FROM (SELECT columname1, columname2 FROM tablename
    WHERE <conditions...> ORDER BY columname1 OFFSET 100 ROWS) aliasname

Remarque: Si vous utilisez TOP et OFFSET ensemble dans la même requête comme:

SELECT TOP 20 columname1, columname2 FROM tablename
    WHERE <conditions...> ORDER BY columname1 OFFSET 100 ROWS

Ensuite, vous obtenez une erreur, donc pour utiliser TOP et OFFSET ensemble, vous devez le séparer avec une sous-requête.

Et si vous devez utiliser SELECT DISTINCT, la requête est comme:

SELECT TOP 20 FROM (SELECT DISTINCT columname1, columname2
    WHERE <conditions...> ORDER BY columname1 OFFSET 100 ROWS) aliasname

Remarque: l'utilisation de SELECT ROW_NUMBER avec DISTINCT n'a pas fonctionné pour moi.


1
J'obtiens "Un TOP ne peut pas être utilisé dans la même requête ou sous-requête qu'un OFFSET."
MichaelRushton

Vous avez raison @MichaelRushton, ne peut pas être utilisé dans la même requête ou dans la même sous-requête, alors vous devez utiliser une sous-requête pour la séparer. Donc, si vous avez le SQL comme SELECT TOP 20 id FROM table1 where id > 10 order by date OFFSET 20 rows, vous devez le transformer comme SELECT TOP 20 * FROM (SELECT id FROM table1 where id > 10 order by date OFFSET 20 ROWS) t1. Je modifierai ma réponse. Merci et excusez-moi mon anglais.
sebasdev

2

Un autre échantillon:

declare @limit int 
declare @offset int 
set @offset = 2;
set @limit = 20;
declare @count int
declare @idxini int 
declare @idxfim int 
select @idxfim = @offset * @limit
select @idxini = @idxfim - (@limit-1);
WITH paging AS
    (
        SELECT 
             ROW_NUMBER() OVER (order by object_id) AS rowid, *
        FROM 
            sys.objects 
    )
select *
    from 
        (select COUNT(1) as rowqtd from paging) qtd, 
            paging 
    where 
        rowid between @idxini and @idxfim
    order by 
        rowid;

15
J'ai supprimé votre discours de haine anti-Microsoft. Ne discutez pas des guerres saintes ici; il suffit de répondre et de poser des questions d'une manière non subjective.
Earlz le

2

Il y a ici quelqu'un qui parle de cette fonctionnalité dans sql 2011, c'est triste qu'ils choisissent un mot-clé un peu différent "OFFSET / FETCH" mais ce n'est pas standart alors ok.


2

En ajoutant une légère variation à la solution d'Aaronaught, je paramètre généralement le numéro de page (@PageNum) et la taille de la page (@PageSize). De cette façon, chaque événement de clic de page envoie simplement le numéro de page demandé avec une taille de page configurable:

begin
    with My_CTE  as
    (
         SELECT col1,
              ROW_NUMBER() OVER(ORDER BY col1) AS row_number
     FROM
          My_Table
     WHERE
          <<<whatever>>>
    )
    select * from My_CTE
            WHERE RowNum BETWEEN (@PageNum - 1) * (@PageSize + 1) 
                              AND @PageNum * @PageSize

end

2

Le plus proche que je pourrais faire est

select * FROM( SELECT *, ROW_NUMBER() over (ORDER BY ID ) as ct from [db].[dbo].[table] ) sub where ct > fromNumber  and ct <= toNumber

Ce qui je suppose similaire à select * from [db].[dbo].[table] LIMIT 0, 10


2
-- @RowsPerPage  can be a fixed number and @PageNumber number can be passed 
DECLARE @RowsPerPage INT = 10, @PageNumber INT = 2

SELECT *

FROM MemberEmployeeData

ORDER BY EmployeeNumber

OFFSET @PageNumber*@RowsPerPage ROWS

FETCH NEXT 10 ROWS ONLY

Fonctionne pour Microsoft SQL Server 13.x Merci beaucoup.
Shubham Arya

1
select top (@TakeCount) * --FETCH NEXT
from(
    Select  ROW_NUMBER() OVER (order by StartDate) AS rowid,*
    From YourTable
)A
where Rowid>@SkipCount --OFFSET

1
@nombre_row :nombre ligne par page  
@page:numero de la page

//--------------code sql---------------

declare  @page int,@nombre_row int;
    set @page='2';
    set @nombre_row=5;
    SELECT  *
FROM    ( SELECT    ROW_NUMBER() OVER ( ORDER BY etudiant_ID ) AS RowNum, *
      FROM      etudiant

    ) AS RowConstrainedResult
WHERE   RowNum >= ((@page-1)*@nombre_row)+1
    AND RowNum < ((@page)*@nombre_row)+1
ORDER BY RowNum

1

Puisque personne n'a encore fourni ce code:

SELECT TOP @limit f1, f2, f3...
FROM t1
WHERE c1 = v1, c2 > v2...
AND
    t1.id NOT IN
        (SELECT TOP @offset id
         FROM t1
         WHERE c1 = v1, c2 > v2...
         ORDER BY o1, o2...)
ORDER BY o1, o2...

Les points importants:

  • ORDER BY doit être identique
  • @limit peut être remplacé par le nombre de résultats à récupérer,
  • @offset est le nombre de résultats à ignorer
  • Veuillez comparer les performances avec les solutions précédentes car elles peuvent être plus efficaces
  • cette solution duplique whereet order byclause, et fournira des résultats incorrects s'ils ne sont pas synchronisés
  • d'autre part order byest-il explicitement si c'est ce qu'il faut

1

En particulier pour SQL-SERVER, vous pouvez y parvenir de différentes manières. Pour un exemple concret, nous avons pris la table Customer ici.

Exemple 1: avec "SET ROWCOUNT"

SET ROWCOUNT 10
SELECT CustomerID, CompanyName from Customers
ORDER BY CompanyName

Pour renvoyer toutes les lignes, définissez ROWCOUNT sur 0

SET ROWCOUNT 0  
SELECT CustomerID, CompanyName from Customers
    ORDER BY CompanyName

Exemple 2: avec "ROW_NUMBER et OVER"

With Cust AS
( SELECT CustomerID, CompanyName,
ROW_NUMBER() OVER (order by CompanyName) as RowNumber 
FROM Customers )
select *
from Cust
Where RowNumber Between 0 and 10

Exemple 3: Avec "OFFSET et FETCH", mais avec ce "ORDER BY" est obligatoire

SELECT CustomerID, CompanyName FROM Customers
ORDER BY CompanyName
OFFSET 0 ROWS
FETCH NEXT 10 ROWS ONLY

J'espère que cela vous aide.


-1

Dans le serveur SQL, vous utiliseriez TOP avec ROW_NUMBER ()


-1

Depuis, je teste plus de fois ce script plus utile de 1 million d'enregistrements chaque page 100 enregistrements avec pagination fonctionnent plus rapidement mon PC exécute ce script 0 sec alors que le comparatif avec mysql a sa propre limite et un décalage d'environ 4,5 sec pour obtenir le résultat.

Quelqu'un peut manquer de comprendre Row_Number () trier toujours par champ spécifique. Au cas où nous aurions besoin de définir uniquement la ligne dans la séquence, utilisez:

ROW_NUMBER () OVER (ORDER BY (SELECT NULL))

SELECT TOP {LIMIT} * FROM (
      SELECT TOP {LIMIT} + {OFFSET} ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS ROW_NO,*
      FROM  {TABLE_NAME}
) XX WHERE ROW_NO > {OFFSET}

Explique:

  • {LIMIT}: nombre d'enregistrements pour chaque page
  • {OFFSET}: nombre d'enregistrements ignorés

2
Bien que ce code puisse résoudre la question, inclure une explication sur comment et pourquoi cela résout le problème aiderait vraiment à améliorer la qualité de votre publication et entraînerait probablement plus de votes à la hausse. N'oubliez pas que vous répondez à la question des lecteurs à l'avenir, pas seulement à la personne qui la pose maintenant. Veuillez modifier votre réponse pour ajouter des explications et donner une indication des limites et des hypothèses applicables.
Brian le
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.