SQL Server SELECT DERNIERS N lignes

139

C'est une question connue mais la meilleure solution que j'ai trouvée est quelque chose comme:

SELECT TOP N *
FROM MyTable
ORDER BY Id DESC

J'ai une table avec beaucoup de lignes. Il n'est pas possible d'utiliser cette requête car cela prend beaucoup de temps. Alors, comment puis-je faire pour sélectionner les N dernières lignes sans utiliser ORDER BY?

ÉDITER

Désolé, question dupliquée de celle-ci

Diego
la source
Qu'entend-on par "dernier N"? Sans ordre, "dernier N" n'a pas beaucoup de sens. Si vous voulez dire «dernier N à être inséré», vous ne pouvez pas compter sur SQL Server pour vous le donner - vous devez utiliser une clause ORDER BY.
Daniel Renshaw
@Daniel Renshaw: Le dernier N de la table sans forcer SQL Server à ordonner toutes les tables car cela devient vraiment lent
Diego
La requête dans votre question est le meilleur moyen. Si idest indexé, il analysera simplement cet index en sens inverse et s'arrêtera après les 5 premières lignes. S'il n'est pas indexé, il devra effectuer un TOP Ntri. Ce ne sera pas pire que toute autre façon de le faire. Il ne trie pas la table entière (bien qu'il faudrait scanner la table entière)
Martin Smith

Réponses:

38

Vous pouvez le faire en utilisant également la fonction NUMÉRO DE RANGÉE PAR PARTITION. Un bon exemple peut être trouvé ici :

J'utilise la table Orders de la base de données Northwind ... Récupérons maintenant les 5 dernières commandes passées par l'employé 5:

SELECT ORDERID, CUSTOMERID, OrderDate
FROM
(
    SELECT ROW_NUMBER() OVER (PARTITION BY EmployeeID ORDER BY OrderDate DESC) AS OrderedDate,*
    FROM Orders
) as ordlist

WHERE ordlist.EmployeeID = 5
AND ordlist.OrderedDate <= 5
JonVD
la source
1
La fonction NUMÉRO DE RANGÉE PAR PARTITION utilise également un tri .. vous devez trier le tableau pour attribuer des numéros de ligne à chaque enregistrement ...
Sadhir
C'est vrai, mais sans une sorte de nature, cela ne fonctionnera tout simplement pas, la meilleure solution consiste à indexer les principales colonnes en cours d'exécution et à les exécuter avec quelque chose comme la requête ci-dessus.
JonVD
101

Vous pouvez faire en sorte que le serveur SQL sélectionne les N dernières lignes à l'aide de ce SQL:

select * from tbl_name order by id desc limit N;
Niru Mukund Shah
la source
2
Qu'en est-il de la compatibilité des versions?
Fractaliste
63
Cela ne fonctionne pas dans SQL Server. Cela ressemble à une fonctionnalité MySQL, PostgreSQL et SQLite.
Tim Friesen
3
Tous les produits énumérés sont définitivement des serveurs SQL. Si vous voulez parler du serveur MS SQL, pourquoi ne pas le nommer ainsi?
gena2x
4
je suis confus, la question demande comment créer une requête de sélection "sans utiliser ORDER BY" et la requête de sélection dans votre réponse a "order by". Est-ce une sorte de «ordre par» sans «ordre par»?
Robert Sinclair
5
@ gena2x cette question est étiquetée SQL Server. Cette balise fait référence à Microsoft SQL Server.
Martin Smith
51

J'ai testé le code de JonVD, mais j'ai trouvé qu'il était très lent, 6s.

Ce code a pris 0s.

SELECT TOP(5) ORDERID, CUSTOMERID, OrderDate    
FROM Orders where EmployeeID=5    
Order By OrderDate DESC
ABI
la source
4
Combien de lignes ?? Quand vous avez beaucoup de lignes qui peuvent être VRAIMENT lentes
Diego
@Diego Pourquoi ça? Si vous êtes OrderDateindexé, il devrait être tout aussi rapide de sélectionner la première ou la dernière N lignes d'une requête. Je me rends compte qu'il y a une chance que la OrderDatecommande insérée soit bien corrélée, mais c'est au mieux un effet secondaire, et cela nécessite toujours une analyse de la table, non? (Et je ne pense pas que cela réponde à ce que l'OP indique comme une dupe mieux formulée de leur question : c'est-à-dire sans trier)
ruffin
1
@Diego - Pourquoi pensez-vous que ce sera plus lent que la réponse que vous avez acceptée?
Martin Smith
2
Cela renvoie les lignes à l'envers. Vous devez ensuite commander à nouveau par eux pour récupérer la commande d'origine.
Mark
15

Si vous souhaitez sélectionner les derniers nombres de lignes d'une table.

La syntaxe sera comme

 select * from table_name except select top 
 (numbers of rows - how many rows you want)* from table_name

Ces déclarations fonctionnent mais de manières différentes. Merci les gars.

 select * from Products except select top (77-10) * from Products

de cette façon, vous pouvez obtenir les 10 dernières lignes mais l'ordre montrera le chemin de la description

select top 10 * from products
 order by productId desc 

 select * from products
 where productid in (select top 10 productID from products)
 order by productID desc

 select * from products where productID not in 
 (select top((select COUNT(*) from products ) -10 )productID from products)
Prafulla Sutradhar
la source
7

D'une manière très générale et pour prendre en charge le serveur SQL, voici

SELECT TOP(N) *
FROM tbl_name
ORDER BY tbl_id DESC

et pour les performances, c'est pas mal (moins d'une seconde pour plus de 10000 enregistrements sur machine serveur)

Hakan Fıstık
la source
1
Eh bien, 10'000 disques, ce n'est rien là où vous devriez vous soucier des performances. Lorsque vous commencez à parler de millions de disques, vous pouvez commencer à penser à la performance
Dom84
6

"Id" est-il indexé? Sinon, c'est une chose importante à faire (je soupçonne qu'elle est déjà indexée).

De plus, devez-vous renvoyer TOUTES les colonnes? Vous pourrez peut-être obtenir une amélioration substantielle de la vitesse si vous n'avez réellement besoin que d'un sous-ensemble plus petit de colonnes qui peut être COMPLÈTEMENT pris en charge par l'index sur la colonne ID - par exemple si vous avez un index NON CLUSTERED sur la colonne Id, sans autre champs inclus dans l'index, il faudrait alors effectuer une recherche sur l'index clusterisé pour obtenir le reste des colonnes à renvoyer et cela pourrait représenter une grande partie du coût de la requête. S'il s'agit d'un index CLUSTERED ou d'un index NONCLUSTERED qui inclut tous les autres champs que vous souhaitez renvoyer dans la requête, tout devrait bien se passer.

AdaTheDev
la source
6

D'abord, vous obtenez le plus grand nombre de records

 Declare @TableRowsCount Int
 select @TableRowsCount= COUNT(*) from <Your_Table>

Puis :

Dans SQL Server 2012

SELECT *
FROM  <Your_Table> As L
ORDER BY L.<your Field>
OFFSET <@TableRowsCount-@N> ROWS
FETCH NEXT @N ROWS ONLY;

Dans SQL Server 2008

SELECT *
FROM 
(
SELECT ROW_NUMBER() OVER(ORDER BY ID) AS sequencenumber, *
FROM  <Your_Table>
    Order By <your Field>
) AS TempTable
WHERE sequencenumber > @TableRowsCount-@N 
Ardalan Shahgholi
la source
4

Voici quelque chose que vous pouvez essayer sans order bymais je pense que cela nécessite que chaque ligne soit unique. Nest le nombre de lignes que vous voulez, Lest le nombre de lignes du tableau.

select * from tbl_name except select top L-N * from tbl_name

Comme indiqué précédemment, les lignes renvoyées ne sont pas définies.

EDIT: c'est en fait un chien lent. Vraiment sans valeur.

Dzamo Norton
la source
4
select * from (select top 6 * from vwTable order by Hours desc) T order by Hours
cinquième
la source
2

Cette requête renvoie les N dernières lignes dans le bon ordre, mais ses performances sont médiocres

select *
from (
    select top N *
    from TableName t
    order by t.[Id] desc
) as temp
order by temp.[Id]
Timberhill
la source
2

utilisez desc avec orderby à la fin de la requête pour obtenir les dernières valeurs.

Sara
la source
1

Cela ne correspond peut-être pas tout à fait à la question, mais…

Clause OFFSET

La OFFSET numberclause vous permet de sauter un nombre de lignes, puis de renvoyer des lignes après cela.

Ce lien doc est vers Postgres; Je ne sais pas si cela s'applique à Sybase / MS SQL Server.

Basil Bourque
la source
1
DECLARE @MYVAR  NVARCHAR(100)
DECLARE @step  int
SET @step = 0;


DECLARE MYTESTCURSOR CURSOR
DYNAMIC 
FOR
SELECT col FROM [dbo].[table]
OPEN MYTESTCURSOR
FETCH LAST FROM MYTESTCURSOR INTO @MYVAR
print @MYVAR;


WHILE @step < 10
BEGIN   
    FETCH PRIOR FROM MYTESTCURSOR INTO @MYVAR
        print @MYVAR;
        SET @step = @step + 1;
END   
CLOSE MYTESTCURSOR
DEALLOCATE MYTESTCURSOR
Slava
la source
1

MS ne prend pas en charge LIMIT dans t-sql. La plupart du temps, j'obtiens simplement MAX (ID), puis je soustrais.

select * from ORDERS where ID >(select MAX(ID)-10 from ORDERS)

Cela renverra moins de 10 enregistrements lorsque l'ID n'est pas séquentiel.

Olafk
la source
0

Une technique que j'utilise pour interroger les lignes LES PLUS RÉCENTES dans les très grandes tables (100+ millions ou 1+ milliard de lignes) limite la requête à "lire" uniquement le pourcentage "N" le plus récent de RANGÉES RÉCENTES. Il s'agit d'applications du monde réel, par exemple, je le fais pour des données météorologiques récentes non historiques, des recherches récentes de fil d'actualité ou des données de point de données de localisation GPS récentes.

Il s'agit d'une énorme amélioration des performances si vous savez avec certitude que vos lignes sont dans les 5% les plus récents du tableau par exemple. De telle sorte que même s'il y a des index sur les tables, cela limite encore les possibilités à seulement 5% des lignes dans les tables qui ont 100+ millions ou 1+ milliard de lignes. C'est particulièrement le cas lorsque des données plus anciennes nécessitent des lectures de disque physique et pas seulement des lectures logiques en mémoire .

C'est bien plus efficace que SELECT TOP | PERCENT | LIMIT car il ne sélectionne pas les lignes, mais limite simplement la partie des données à rechercher.

DECLARE @RowIdTableA BIGINT
DECLARE @RowIdTableB BIGINT
DECLARE @TopPercent FLOAT

-- Given that there is an Sequential Identity Column
-- Limit query to only rows in the most recent TOP 5% of rows
SET @TopPercent = .05
SELECT @RowIdTableA = (MAX(TableAId) - (MAX(TableAId) * @TopPercent)) FROM TableA
SELECT @RowIdTableB = (MAX(TableBId) - (MAX(TableBId) * @TopPercent)) FROM TableB

SELECT *
FROM TableA a
INNER JOIN TableB b ON a.KeyId = b.KeyId
WHERE a.Id > @RowIdTableA AND b.Id > @RowIdTableB AND
      a.SomeOtherCriteria = 'Whatever'
CodeCowboyOrg
la source
-1

Pour afficher les 3 dernières lignes sans utiliser order by:

select * from Lms_Books_Details where Book_Code not in 
 (select top((select COUNT(*) from Lms_Books_Details ) -3 ) book_code from Lms_Books_Details) 
abhinay
la source
1
Cela ne fournira pas de résultats prévisibles. Selon la documentation MSDN de Sql Server ( msdn.microsoft.com/en-us/library/ms189463.aspx ): «Lorsque TOP est utilisé conjointement avec la clause ORDER BY, le jeu de résultats est limité au premier nombre N de lignes; sinon, il renvoie le premier nombre de N lignes dans un ordre non défini. "
caveman_dick
-1

Essayez d'utiliser la EXCEPTsyntaxe.
Quelque chose comme ça:

   SELECT * 
    FROM   clientDetails 
    EXCEPT 
    (SELECT TOP (numbers of rows - how many rows you want) * 
     FROM   clientDetails) 
mayur godhani
la source
Même réponse que @Prafulla Sutradhar
DMK
-1

Peut-être un peu tard, mais voici une simple sélection qui résout votre question.

SELECT * FROM "TABLE" T ORDER BY "T.ID_TABLE" DESC LIMIT 5;
Lucas Orso
la source