Décalage de ligne dans SQL Server

133

Existe-t-il un moyen dans SQL Server d'obtenir les résultats à partir d'un décalage donné? Par exemple, dans un autre type de base de données SQL, il est possible de faire:

SELECT * FROM MyTable OFFSET 50 LIMIT 25

pour obtenir des résultats 51-75. Cette construction ne semble pas exister dans SQL Server.

Comment puis-je accomplir cela sans charger toutes les lignes dont je ne me soucie pas? Merci!

Alex
la source
Vous pouvez utiliser offset et récupérer l'instruction suivante. youtu.be/EqHkAiiBwPc
Amresh Kumar Singh

Réponses:

152

J'éviterais d'utiliser SELECT *. Spécifiez les colonnes que vous voulez réellement, même si elles peuvent être toutes.

SQL Server 2005+

SELECT col1, col2 
FROM (
    SELECT col1, col2, ROW_NUMBER() OVER (ORDER BY ID) AS RowNum
    FROM MyTable
) AS MyDerivedTable
WHERE MyDerivedTable.RowNum BETWEEN @startRow AND @endRow

SQL Server 2000

Pagination efficace de grands ensembles de résultats dans SQL Server 2000

Une méthode plus efficace pour la pagination de grands ensembles de résultats

Brian Kim
la source
6
Pourquoi suggérez-vous d'éviter SELECT même si vous sélectionnez toutes les colonnes?
Adam Ness
12
Je suis sûr qu'il a utilisé "*" parce qu'il était plus simple à taper et a mieux fait passer le message que "col1, col2, ... colN"
gillonba
9
Quant à savoir pourquoi ne pas l'utiliser, cela SELECT *signifie que si la structure de la table change, votre requête s'exécute toujours, mais donne des résultats différents. Si une colonne est ajoutée, cela peut être utile (même si vous devez toujours l'utiliser par son nom quelque part); si une colonne est supprimée ou renommée, il est préférable pour votre SQL de casser visiblement que le code plus bas se comportant bizarrement car une variable n'est pas initialisée.
IMSoP
5
sélectionner toutes les données de la table et couper? si avoir 5000000000 lignes? sélectionner 5000000000 lignes et couper pour chaque requête? ce n'est pas efficace pour le processeur et la mémoire du serveur.
e-info128
3
Veuillez noter que 2012+ l'a mis en œuvre bien mieux. Voir la réponse de + Martin Smith
meridius
100

Si vous traitez toutes les pages dans l'ordre, alors simplement se souvenir de la dernière valeur de clé vue sur la page précédente et utiliser TOP (25) ... WHERE Key > @last_key ORDER BY Keypeut être la méthode la plus performante si des index appropriés existent pour permettre une recherche efficace - ou un curseur API si ce n'est pas le cas. .

Pour sélectionner une page arbitraire, la meilleure solution pour SQL Server 2005 - 2008 R2 est probablement ROW_NUMBERetBETWEEN

Pour SQL Server 2012+, vous pouvez utiliser la clause ORDER BY améliorée pour ce besoin.

SELECT  *
FROM     MyTable 
ORDER BY OrderingColumn ASC 
OFFSET  50 ROWS 
FETCH NEXT 25 ROWS ONLY 

Bien qu'il reste à voir dans quelle mesure cette option sera performante .

Martin Smith
la source
2
Il est maintenant disponible dans SQL Server Compact 4.0 -> msdn.microsoft.com/en-us/library/gg699618(v=sql.110).aspx
Bart Verkoeijen
13
Il est temps qu'ils ajoutent ceci à tSQL
JohnFx
3
Uniquement pour Sql Server 2012 :(
e-info128
22

C'est un moyen (SQL2000)

SELECT * FROM
(
    SELECT TOP (@pageSize) * FROM
    (
        SELECT TOP (@pageNumber * @pageSize) *
        FROM tableName 
        ORDER BY columnName ASC
    ) AS t1 
    ORDER BY columnName DESC
) AS t2 
ORDER BY columnName ASC

et c'est une autre façon (SQL 2005)

;WITH results AS (
    SELECT 
        rowNo = ROW_NUMBER() OVER( ORDER BY columnName ASC )
        , *
    FROM tableName 
) 
SELECT * 
FROM results
WHERE rowNo between (@pageNumber-1)*@pageSize+1 and @pageNumber*@pageSize
leoinfo
la source
Juste pour clarifier le premier ... (@pageSize) est un espace réservé ici pour la valeur réelle. Vous devrez faire «TOP 25» spécifiquement; SQL Server 2000 ne prend pas en charge les variables dans une clause TOP. Cela en fait une douleur impliquant SQL dynamique.
Cowan
5
Cette solution pour SQL2000 ne fonctionne pas pour la dernière page du jeu de résultats, à moins que le nombre total de lignes ne soit un multiple de la taille de la page.
Bill Karwin
10

Vous pouvez utiliser la ROW_NUMBER()fonction pour obtenir ce que vous voulez:

SELECT *
FROM (SELECT ROW_NUMBER() OVER(ORDER BY id) RowNr, id FROM tbl) t
WHERE RowNr BETWEEN 10 AND 20
Matthias Meid
la source
7

Il existe OFFSET .. FETCHdans SQL Server 2012, mais vous devrez spécifier une ORDER BYcolonne.

Si vous n'avez vraiment pas de colonne explicite que vous pourriez passer comme ORDER BYcolonne (comme d'autres l'ont suggéré), vous pouvez utiliser cette astuce:

SELECT * FROM MyTable 
ORDER BY @@VERSION 
OFFSET 50 ROWS FETCH NEXT 25 ROWS ONLY

... ou

SELECT * FROM MyTable 
ORDER BY (SELECT 0)
OFFSET 50 ROWS FETCH NEXT 25 ROWS ONLY

Nous l'utilisons dans jOOQ lorsque les utilisateurs ne spécifient pas explicitement une commande. Cela produira alors une commande assez aléatoire sans aucun coût supplémentaire.

Lukas Eder
la source
6

Pour les tables avec des colonnes de données plus nombreuses et volumineuses, je préfère:

SELECT 
  tablename.col1,
  tablename.col2,
  tablename.col3,
  ...
FROM
(
  (
    SELECT
      col1
    FROM 
    (
      SELECT col1, ROW_NUMBER() OVER (ORDER BY col1 ASC) AS RowNum
      FROM tablename
      WHERE ([CONDITION])
    )
    AS T1 WHERE T1.RowNum BETWEEN [OFFSET] AND [OFFSET + LIMIT]
  )
  AS T2 INNER JOIN tablename ON T2.col1=tablename.col1
);

-

[CONDITION] can contain any WHERE clause for searching.
[OFFSET] specifies the start,
[LIMIT] the maximum results.

Elle offre de bien meilleures performances sur les tables contenant des données volumineuses telles que les BLOB, car la fonction ROW_NUMBER n'a qu'à parcourir une colonne et seules les lignes correspondantes sont renvoyées avec toutes les colonnes.

Arthur van Dijk
la source
5

Voir ma sélection pour paginateur

SELECT TOP @limit * FROM (
   SELECT ROW_NUMBER() OVER (ORDER BY colunx ASC) offset, * FROM (

     -- YOU SELECT HERE
     SELECT * FROM mytable


   ) myquery
) paginator
WHERE offset > @offset

Cela résout la pagination;)

PerfectLion
la source
3
SELECT TOP 75 * FROM MyTable
EXCEPT 
SELECT TOP 50 * FROM MyTable
Jithin Shaji
la source
Les performances ne semblent pas optimales car la requête est ensuite exécutée deux fois inutilement. Surtout lorsque l'utilisateur va aux pages supérieures, la requête pour supprimer les lignes, c'est-à-dire la partie ci-dessous SAUF, prendra de plus en plus de temps.
vanval
2

Selon votre version, vous ne pouvez pas le faire directement, mais vous pouvez faire quelque chose de piraté comme

select top 25 *
from ( 
  select top 75 *
  from   table 
  order by field asc
) a 
order by field desc 

où «champ» est la clé.

Non tranché
la source
4
Cette solution pour SQL2000 ne fonctionne pas pour la dernière page du jeu de résultats, à moins que le nombre total de lignes ne soit un multiple de la taille de la page.
Bill Karwin
2

Ce qui suit affichera 25 enregistrements à l'exclusion des 50 premiers enregistrements fonctionne dans SQL Server 2012.

SELECT * FROM MyTable ORDER BY ID OFFSET 50 ROWS FETCH NEXT 25 ROWS ONLY;

vous pouvez remplacer l'ID en tant que votre condition

Shb
la source
Veuillez également ajouter que cela est possible dans SQL SERVER 2012
Usman Younas
2

Vous devez être prudent lorsque vous utilisez l' ROW_NUMBER() OVER (ORDER BY)instruction car les performances sont assez médiocres. Il en va de même pour l'utilisation d'expressions de table communes avec ROW_NUMBER()c'est encore pire. J'utilise l'extrait de code suivant qui s'est avéré légèrement plus rapide que l'utilisation d'une variable de table avec une identité pour fournir le numéro de page.

DECLARE @Offset INT = 120000
DECLARE @Limit INT = 10

DECLARE @ROWCOUNT INT = @Offset+@Limit
SET ROWCOUNT @ROWCOUNT

SELECT * FROM MyTable INTO #ResultSet
WHERE MyTable.Type = 1

SELECT * FROM
(
    SELECT *, ROW_NUMBER() OVER(ORDER BY SortConst ASC) As RowNumber FROM
    (
        SELECT *, 1 As SortConst FROM #ResultSet
    ) AS ResultSet
) AS Page
WHERE RowNumber BETWEEN @Offset AND @ROWCOUNT

DROP TABLE #ResultSet
Patrik Melander
la source
Cela renverra 11 lignes, pas 10.
Aaron Bertrand
1

J'utilise cette technique pour la pagination. Je ne récupère pas toutes les lignes. Par exemple, si ma page doit afficher les 100 premières lignes, je ne récupère que les 100 avec la clause where. La sortie du SQL doit avoir une clé unique.

Le tableau contient les éléments suivants:

ID, KeyId, Rank

Le même rang sera attribué à plus d'un KeyId.

SQL est select top 2 * from Table1 where Rank >= @Rank and ID > @Id

Pour la première fois, je passe 0 pour les deux. Le deuxième temps passe 1 & 14. 3e temps passe 2 et 6 ....

La valeur du 10ème enregistrement Rank & Id est transmise au suivant

11  21  1
14  22  1
7   11  1
6   19  2
12  31  2
13  18  2

Cela aura le moins de stress sur le système

Ravi Ramaswamy
la source
1

Dans SqlServer2005, vous pouvez effectuer les opérations suivantes:

DECLARE @Limit INT
DECLARE @Offset INT
SET @Offset = 120000
SET @Limit = 10

SELECT 
    * 
FROM
(
   SELECT 
       row_number() 
   OVER 
      (ORDER BY column) AS rownum, column2, column3, .... columnX
   FROM   
     table
) AS A
WHERE 
 A.rownum BETWEEN (@Offset) AND (@Offset + @Limit-1) 
Aheho
la source
Cela ne devrait-il pas être le cas @Offset + @Limit - 1? Si @Limit vaut 10, cela renverra 11 lignes.
Aaron Bertrand
1

La meilleure façon de le faire sans perdre de temps à commander des disques est la suivante:

select 0 as tmp,Column1 from Table1 Order by tmp OFFSET 5000000 ROWS FETCH NEXT 50 ROWS ONLY

cela prend moins d'une seconde!
meilleure solution pour les grandes tables.

Pishgaman.org
la source
0

Cela fait un moment que je cherche cette réponse (pour les requêtes génériques) et j'ai découvert une autre façon de le faire sur SQL Server 2000+ en utilisant ROWCOUNT et des curseurs et sans TOP ni aucune table temporaire.

En utilisant le, SET ROWCOUNT [OFFSET+LIMIT]vous pouvez limiter les résultats, et avec les curseurs, allez directement à la ligne que vous souhaitez, puis bouclez jusqu'à la fin.

Donc, votre requête serait comme ceci:

SET ROWCOUNT 75 -- (50 + 25)
DECLARE MyCursor SCROLL CURSOR FOR SELECT * FROM pessoas
OPEN MyCursor
FETCH ABSOLUTE 50 FROM MyCursor -- OFFSET
WHILE @@FETCH_STATUS = 0 BEGIN
    FETCH next FROM MyCursor
END
CLOSE MyCursor
DEALLOCATE MyCursor
SET ROWCOUNT 0
Capilé
la source
Je détesterais voir la performance de cela quand vous arrivez vers la fin de la table ...
Aaron Bertrand
0

Avec SQL Server 2012 (11.x) et versions ultérieures et Azure SQL Database, vous pouvez également avoir "fetch_row_count_expression", vous pouvez également avoir la clause ORDER BY avec cela.

USE AdventureWorks2012;  
GO  
-- Specifying variables for OFFSET and FETCH values    
DECLARE @skip int = 0  , @take int = 8;  
SELECT DepartmentID, Name, GroupName  
FROM HumanResources.Department  
ORDER BY DepartmentID ASC   
    OFFSET @skip ROWS   
    FETCH NEXT @take ROWS ONLY; 

https://docs.microsoft.com/en-us/sql/t-sql/queries/select-order-by-clause-transact-sql?view=sql-server-ver15

Remarque OFFSET Spécifie le nombre de lignes à ignorer avant de commencer à renvoyer des lignes à partir de l'expression de requête. Ce n'est PAS le numéro de la ligne de départ. Donc, il doit être 0 pour inclure le premier enregistrement.

Tejasvi Hegde
la source