Il existe un moyen simple d’obtenir les N premières lignes d’une table:
SELECT TOP 10 * FROM MyTable ORDER BY MyColumn
Existe-t-il un moyen efficace d'interroger M lignes à partir de la ligne N?
Par exemple,
Id Value
1 a
2 b
3 c
4 d
5 e
6 f
Et requête comme ça
SELECT [3,2] * FROM MyTable ORDER BY MyColumn /* hypothetical syntax */
interroge 2 lignes à partir de la ligne 3d, c’est-à-dire que les lignes 3d et 4 sont renvoyées.
UPDATE Si vous utilisez SQL 2012, une nouvelle syntaxe a été ajoutée pour simplifier les choses. Voir Implémenter la fonctionnalité de pagination (sauter/prendre) avec cette requête
Je suppose que le plus élégant consiste à utiliser la fonction ROW_NUMBER (disponible à partir de MS SQL Server 2005):
WITH NumberedMyTable AS
(
SELECT
Id,
Value,
ROW_NUMBER() OVER (ORDER BY Id) AS RowNumber
FROM
MyTable
)
SELECT
Id,
Value
FROM
NumberedMyTable
WHERE
RowNumber BETWEEN @From AND @To
Le problème avec les suggestions dans ce fil et ailleurs sur le Web est que toutes les solutions proposées fonctionnent en temps linéaire par rapport au nombre d'enregistrements. Par exemple, considérons une requête comme celle-ci.
select *
from
(
select
Row_Number() over (order by ClusteredIndexField) as RowNumber,
*
from MyTable
) as PagedTable
where RowNumber between @LowestRowNumber and @HighestRowNumber;
Lors de l'obtention de la page 1, la requête prend 0,577 seconde. Toutefois, lors de l'obtention de la page 15 619, cette même requête prend plus de 2 minutes et 55 secondes.
Nous pouvons grandement améliorer cela en créant un numéro d'enregistrement, une table croisée d'index, comme indiqué dans la requête suivante. Le tableau croisé s'appelle PagedTable et n'est pas persistant.
select *
from
(
select
Row_Number() over (order by Field1 asc, Field2 asc, Field3 asc) as RowNumber,
ClusteredIndexField
from MyTable
) as PagedTable
left join MyTable on MyTable.ClusteredIndexField = PagedTable.ClusteredIndexField
where RowNumber between @LowestRowNumber and @HighestRowNumber;
Comme dans l'exemple précédent, j'ai testé cela sur une très grande table avec 780 928 enregistrements. J'ai utilisé une taille de page de 50, ce qui a abouti à 15 619 pages.
Le temps total pris pour la page 1 (la première page) est de 0,413 seconde. Le temps total pris pour la page 15 619 (la dernière page) est de 0,987 secondes, soit deux fois plus que la page 1. Ces temps ont été mesurés à l'aide de SQL Server Profiler et le SGBD était SQL Server 2008 R2.
Cette solution fonctionne dans tous les cas lorsque vous triez votre table par un index. L'index n'a pas besoin d'être en cluster ou simple. Dans mon cas, l'index était composé de trois champs: varchar (50) asc, varchar (15) asc, numérique (19,0) asc. Le fait que les performances aient été excellentes malgré la lourdeur de l’indice montre bien que cette approche fonctionne.
Cependant, il est essentiel que la clause order by de la fonction de fenêtrage Row_Number corresponde à un index. Sinon, les performances se dégradent au même niveau que dans le premier exemple.
Cette approche nécessite toujours une opération linéaire pour générer le tableau croisé non persistant, mais comme il ne s'agit que d'un index avec un numéro de ligne ajouté, cela se produit très rapidement. Dans mon cas, cela a pris 0,347 secondes, mais mon cas avait varchars qui devaient être copiés. Un seul index numérique prendrait beaucoup moins de temps.
À toutes fins pratiques, cette conception réduit la mise à l'échelle de la pagination côté serveur d'une opération linéaire à une opération logarithmique permettant la mise à l'échelle de tables volumineuses. Vous trouverez ci-dessous la solution complète.
-- For a sproc, make these your input parameters
declare
@PageSize int = 50,
@Page int = 15619;
-- For a sproc, make these your output parameters
declare @RecordCount int = (select count(*) from MyTable);
declare @PageCount int = ceiling(convert(float, @RecordCount) / @PageSize);
declare @Offset int = (@Page - 1) * @PageSize;
declare @LowestRowNumber int = @Offset;
declare @HighestRowNumber int = @Offset + @PageSize - 1;
select
@RecordCount as RecordCount,
@PageCount as PageCount,
@Offset as Offset,
@LowestRowNumber as LowestRowNumber,
@HighestRowNumber as HighestRowNumber;
select *
from
(
select
Row_Number() over (order by Field1 asc, Field2 asc, Field3 asc) as RowNumber,
ClusteredIndexField
from MyTable
) as PagedTable
left join MyTable on MyTable.ClusteredIndexField = PagedTable.ClusteredIndexField
where RowNumber between @LowestRowNumber and @HighestRowNumber;
Dans SQL 2012 vous pouvez utiliser OFFSET
et FETCH
:
SELECT *
FROM MyTable
ORDER BY MyColumn
OFFSET @N ROWS
FETCH NEXT @M ROWS ONLY;
DECLARE @CurrentSetNumber int = 0;
DECLARE @NumRowsInSet int = 2;
SELECT *
FROM MyTable
ORDER BY MyColumn
OFFSET @NumRowsInSet * @CurrentSetNumber ROWS
FETCH NEXT @NumRowsInSet ROWS ONLY;
SET @CurrentSetNumber = @CurrentSetNumber + 1;
où @NumRowsInSet
est le nombre de lignes que vous voulez renvoyer et @CurrentSetNumber
est le nombre de @NumRowsInSet
à ignorer.
Si vous souhaitez sélectionner 100 enregistrements à partir du 25ème enregistrement:
select TOP 100 * from TableName
where PrimaryKeyField
NOT IN(Select TOP 24 PrimaryKeyField from TableName);
Moche, hackish, mais devrait fonctionner:
select top(M + N - 1) * from TableName
except
select top(N - 1) * from TableName
Probablement bon pour les petits résultats, fonctionne dans toutes les versions de TSQL:
SELECT
*
FROM
(SELECT TOP (N) *
FROM
(SELECT TOP (M + N - 1)
FROM
Table
ORDER BY
MyColumn) qasc
ORDER BY
MyColumn DESC) qdesc
ORDER BY
MyColumn
-- *some* implementations may support this syntax (mysql?)
SELECT Id,Value
FROM xxx
ORDER BY Id
LIMIT 2 , 0
;
-- Separate LIMIT, OFFSET
SELECT Id,Value
FROM xxx
ORDER BY Id
LIMIT 2 OFFSET 2
;
-- SQL-2008 syntax
SELECT Id,Value
FROM xxx
ORDER BY Id
OFFSET 4
FETCH NEXT 2 ROWS ONLY
;
Pour ce faire dans SQL Server, vous devez classer la requête par une colonne afin de pouvoir spécifier les lignes souhaitées.
Vous ne pouvez pas utiliser le mot clé "TOP" pour cela. Vous devez utiliser le décalage N lignes pour rechercher les M lignes suivantes.
Exemple:
select * from table order by [some_column]
offset 10 rows
FETCH NEXT 10 rows only
Vous pouvez en apprendre plus ici: https://technet.Microsoft.com/pt-br/library/gg699618%28v=sql.110%29.aspx
@start = 3
@records = 2
Select ID, Value
From
(SELECT ROW_NUMBER() OVER(ORDER BY ID) AS RowNum, ID,Value
From MyTable) as sub
Where sub.RowNum between @start and @start+@records
C'est une façon. il y en a beaucoup d'autres si vous utilisez Google SQL Paging.
Ce fil est assez ancien, mais vous pouvez actuellement le faire: Beaucoup plus propre à mon humble avis
SELECT *
FROM Sales.SalesOrderDetail
ORDER BY SalesOrderDetailID
OFFSET 20 ROWS
FETCH NEXT 10 ROWS ONLY;
GO
Et voici comment vous pouvez atteindre le même objectif sur des tables sans clé primaire:
select * from
(
select row_number() over(order by (select 0)) rowNum,*
from your_table
) tmp
where tmp.rowNum between 20 and 30 -- any numbers you need
La requête suivante répertorie N lignes de M + 1 ligne du tableau. Remplacez M et N par vos numéros préférés.
Select Top N B.PrimaryKeyColumn from
(SELECT
top M PrimaryKeyColumn
FROM
MyTable
) A right outer join MyTable B
on
A.PrimaryKeyColumn = B.PrimaryKeyColumn
where
A.PrimaryKeyColumn IS NULL
S'il vous plaît laissez-moi savoir si cela est utile pour votre situation.
J'ai lu toutes les réponses ici et j'ai finalement trouvé une solution simple et utilisable. Les problèmes de performances proviennent de l'instruction BETWEEN et non de la génération des numéros de ligne eux-mêmes. J'ai donc utilisé un algorithme pour effectuer une pagination dynamique en transmettant le numéro de page et le nombre d'enregistrements.
Les passes ne sont ni la ligne de départ ni le nombre de lignes, mais plutôt les "lignes par page (500)" et le "numéro de page (4)", qui correspondent aux lignes 1501 à 2000. Ces valeurs peuvent être remplacées par des variables de procédure stockées. verrouillé en utilisant un montant de pagination spécifique.
select * from (
select
(((ROW_NUMBER() OVER(ORDER BY MyField) - 1) / 500) + 1) AS PageNum
, *
from MyTable
) as PagedTable
where PageNum = 4;
SELECT * FROM (
SELECT
Row_Number() Over (Order by (Select 1)) as RawKey,
*
FROM [Alzh].[dbo].[DM_THD_TRANS_FY14]
) AS foo
WHERE RawKey between 17210400 and 17210500
Recherchez l'identifiant de la ligne N Puis, obtenez les M premières lignes dont l'identifiant est supérieur ou égal à celui
déclarer @N en tant qu'int set @N = 2 déclarer @M en tant qu'int set @M = 3 déclarer @Nid en tant qu'int set @Nid = max (id) À partir de (sélectionnez top @N * dans MyTable par ordre) sélectionnez top @M * dans MyTable où id> = @Nid order by id
Quelque chose comme ça ... mais j'ai fait certaines hypothèses ici (par exemple, vous voulez commander par ID)
Il existe une méthode assez simple pour T-SQL
, bien que je ne sois pas sûr que ce soit efficace si vous sautez un grand nombre de lignes.
SELECT TOP numberYouWantToTake
[yourColumns...]
FROM yourTable
WHERE yourIDColumn NOT IN (
SELECT TOP numberYouWantToSkip
yourIDColumn
FROM yourTable
ORDER BY yourOrderColumn
)
ORDER BY yourOrderColumn
Si vous utilisez .Net, vous pouvez utiliser les éléments suivants, par exemple, un IEnumerable avec vos résultats de données:
IEnumerable<yourDataType> yourSelectedData = yourDataInAnIEnumerable.Skip(nubmerYouWantToSkip).Take(numberYouWantToTake);
Cela signifie que vous obtenez toutes les données du stockage de données.
Pourquoi ne pas faire deux requêtes:
select top(M+N-1) * from table into temp tmp_final with no log;
select top(N-1) * from tmp_final order by id desc;