Comment écrire .Skip (1000) .Take (100) de LINQ en SQL pur?

93

Quel est l'équivalent SQL de la .Skip()méthode dans LINQ?

Par exemple: je voudrais sélectionner les lignes 1000-1100 à partir d'une table de base de données spécifique.

Est-ce possible avec juste SQL? Ou dois-je sélectionner la table entière, puis trouver les lignes en mémoire? Dans l'idéal, j'aimerais éviter cela, si possible, car la table peut être assez grande.

Rayon
la source

Réponses:

78

Dans SQL Server 2005 et au-dessus, vous pouvez utiliser la fonction ROW_NUMBER . par exemple.

USE AdventureWorks;
GO
WITH OrderedOrders AS
(
    SELECT SalesOrderID, OrderDate,
    ROW_NUMBER() OVER (ORDER BY OrderDate) AS 'RowNumber'
    FROM Sales.SalesOrderHeader 
) 
SELECT * 
FROM OrderedOrders 
WHERE RowNumber BETWEEN 51 AND 60; --BETWEEN is inclusive
Dan Diplo
la source
Voir le lien dans ma réponse pour un peu plus de détails. stackoverflow.com/questions/1744802/…
Mike Atlas
ENTRE 51 et 60 - c'est inclusif.
Drew Miller le
1
Mais cela sélectionnera d'abord tout, puis à partir de cette sélection, n'en prendra que 10, non? Ou la première requête / vue n'en aura-t-elle déjà que 10?
Tadej
139

SQL Server 2012 et supérieur ont ajouté cette syntaxe:

SELECT *
FROM Sales.SalesOrderHeader 
ORDER BY OrderDate
OFFSET (@Skip) ROWS FETCH NEXT (@Take) ROWS ONLY
John Gietzen
la source
11
Notez que vous devez utiliser ORDER BY ___ pour utiliser la commande OFFSET .... pas que vous ne devriez jamais essayer de paginer sans ordre.
James Haug
Notez également que la «nouvelle» syntaxe a étrangement une pénalité de performance linéaire avec le @skip! L'approche row_number ne l'a PAS (testée uniquement dans l'ordre indexé). Pour lo @Skip moins d'environ 20, la nouvelle syntaxe est plus rapide que l'approche row_number.
Eske Rahn
22

LINQ to SQL le fait en utilisant une fonction de fenêtrage ROW_NUMBER:

  SELECT a,b,c FROM 
   (SELECT a,b,c, ROW_NUMBER() OVER (ORDER BY ...) as row_number
    FROM Table) t0
   WHERE to.row_number BETWEEN 1000 and 1100;

Cela fonctionne, mais la nécessité de fabriquer le row_number à partir de ORDER BY peut entraîner le tri de votre requête côté serveur et entraîner des problèmes de performances. Même lorsqu'un index peut satisfaire l'exigence ORDER BY, la requête doit toujours compter 1 000 lignes avant de commencer à renvoyer des résultats. Trop souvent, les développeurs oublient cela et lancent simplement un contrôle de pagination sur une table de 5 millions de lignes et se demandent pourquoi la première page est renvoyée beaucoup plus rapidement que la dernière ...

Néanmoins, l'utilisation de ROW_NUMBER () est probablement le meilleur équilibre entre facilité d'utilisation et bonnes performances, à condition d'éviter le tri (la condition ORDER BY peut être satisfaite par un index).

Remus Rusanu
la source
1
Merci pour les informations de performance supplémentaires, devra être prudent et le tester.
Ray
Testé et pour ma table d'un demi-million de lignes, cette dernière page est environ 7 fois plus lente que la première page. Pas idéal, mais acceptable pour moi.
Ray
6

Essaye celui-là:

select * from [Table-Name] order by [Column-Name] 
offset [Skip-Count] rows
FETCH NEXT [Take-Count] rows only

Exemple:

select * from Personals order by Id
offset 10 rows            --------->Skip 10
FETCH NEXT 15 rows only   --------->Take 15
Fereydoon Barikzehy
la source
4

Faites ceci:

Exécutez .Skip (1000) .Take (100) sur un datacontext LINQ to SQL et regardez la sortie SQL. Il générera pour vous une instruction SQL qui fera ce que vous décrivez.

Ce ne sera pas aussi élégant, mais il fait le travail.

Joseph
la source
2
Pas ce qui était demandé.
RayLoveless