web-dev-qa-db-fra.com

Comment sélectionner le bas de la plupart des lignes?

Je peux faire SELECT TOP (200) ... mais pourquoi pas BOTTOM (200)?

Bien, pour ne pas entrer dans la philosophie, comment puis-je faire l'équivalent de TOP (200) mais en sens inverse (par le bas, comme vous vous attendriez à ce que Bottom fasse ...)?

85
BigOmega
SELECT
    columns
FROM
(
     SELECT TOP 200
          columns
     FROM
          My_Table
     ORDER BY
          a_column DESC
) SQ
ORDER BY
     a_column ASC
76
Tom H

C'est inutile. Vous pouvez utiliser un ORDER BY et modifier simplement le tri en DESC pour obtenir le même effet.

90
Justin Ethier

Désolé, mais je ne pense pas voir de réponses correctes à mon avis. 

La fonction TOP x affiche les enregistrements dans un ordre indéfini. Il découle de cette définition qu’une fonction BOTTOM ne peut pas être définie.

Indépendant de tout index ou ordre de tri. Lorsque vous faites un ORDER BY y DESC, vous obtenez les lignes avec la valeur y la plus élevée en premier. S'il s'agit d'un identifiant généré automatiquement, il doit afficher les derniers enregistrements ajoutés à la table, comme suggéré dans les autres réponses. Toutefois:

  • Cela ne fonctionne que s'il existe une colonne d'identifiant généré automatiquement
  • Cela a un impact significatif sur les performances si vous comparez cela avec la fonction TOP

La réponse correcte devrait être qu'il n'y a pas, et ne peut pas, être équivalent à TOP pour obtenir les lignes du bas.

30
Martijn Burger

Sélectionnez Bottom 1000 from Employee

DECLARE 
@bottom int,
@count int

SET @bottom = 1000 
SET @count = (select COUNT(*) from Employee)

select * from Employee emp where emp.EmployeeID not in 
(
SELECT TOP (@count-@bottom) Employee.EmployeeID FROM Employee
)
14
Shadi Namrouti

La réponse actuellement acceptée par "Justin Ethier" n’est pas une réponse correcte comme l’a souligné "Protector one".

Pour autant que je sache, pour le moment, aucune autre réponse ni aucun commentaire n'apporte l'équivalent de BOTTOM (x) à la question posée par l'auteur.

Tout d'abord, considérons un scénario dans lequel cette fonctionnalité serait nécessaire:

SELECT * FROM Split('Apple,orange,banana,Apple,Lime',',')

Ceci retourne une table d'une colonne et de cinq enregistrements:

  • Pomme
  • orange
  • banane
  • Pomme
  • Citron vert

Comme vous pouvez le constater: nous n’avons pas de colonne ID; nous ne pouvons pas commander par la colonne retournée; et nous ne pouvons pas sélectionner les deux derniers enregistrements à l'aide de SQL standard comme nous pouvons le faire pour les deux premiers enregistrements.

Voici ma tentative de fournir une solution:

SELECT * INTO #mytemptable FROM Split('Apple,orange,banana,Apple,Lime',',')
ALTER TABLE #mytemptable ADD tempID INT IDENTITY
SELECT TOP 2 * FROM #mytemptable ORDER BY tempID DESC
DROP TABLE #mytemptable

Et voici une solution plus complète:

SELECT * INTO #mytemptable FROM Split('Apple,orange,banana,Apple,Lime',',')
ALTER TABLE #mytemptable ADD tempID INT IDENTITY
DELETE FROM #mytemptable WHERE tempID <= ((SELECT COUNT(*) FROM #mytemptable) - 2)
ALTER TABLE #mytemptable DROP COLUMN tempID
SELECT * FROM #mytemptable
DROP TABLE #mytemptable

Je ne prétends nullement que ce soit une bonne idée à utiliser en toutes circonstances, mais cela donne les résultats souhaités.

2
tomosius

Tout ce que vous avez à faire est d’inverser votre ORDER BY. Ajoutez ou supprimez DESC.

2
Justin Swartsel

"Tom H" la réponse ci-dessus est correcte et cela fonctionne pour moi pour obtenir les 5 derniers rangs.

SELECT [KeyCol1], [KeyCol2], [Col3]
FROM
(SELECT TOP 5 [KeyCol1],
       [KeyCol2],
       [Col3]
  FROM [dbo].[table_name]
  ORDER BY [KeyCol1],[KeyCol2] DESC) SOME_ALAIS
  ORDER BY [KeyCol1],[KeyCol2] ASC

Merci.

1
user3598017
SELECT TOP 10*from TABLE1 ORDER BY ID DESC

Où ID est la clé primaire de la TABLE1.

1
Er. Binod Mehta

Le problème en ordonnant dans l'autre sens est que souvent, les indices ne sont pas utilisés. Il n’est pas non plus très extensible si vous devez sélectionner un nombre de lignes qui ne se trouvent pas au début ou à la fin. Une autre manière est la suivante.

DECLARE @NumberOfRows int;
SET @NumberOfRows = (SELECT COUNT(*) FROM TheTable);

SELECT col1, col2,...
FROM (
    SELECT col1, col2,..., ROW_NUMBER() OVER (ORDER BY col1) AS intRow
    FROM TheTable
) AS T
WHERE intRow > @NumberOfRows - 20;
1
Paul

J'ai mis au point une solution à ce problème qui ne vous oblige pas à connaître le nombre de lignes renvoyées. 

Par exemple, si vous souhaitez que tous les emplacements soient consignés dans une table, à l'exception du dernier 1 (ou 2, 5 ou 34).

SELECT * 
FROM
    (SELECT ROW_NUMBER() OVER (ORDER BY CreatedDate) AS Row, * 
    FROM Locations
    WHERE UserId = 12345) AS SubQuery
WHERE Row > 1 -- or 2, or 5, or 34
0
Red

Commencez par créer un index dans une sous-requête en fonction de l'ordre d'origine de la table à l'aide de: 

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

Ordonnez ensuite la table par ordre décroissant selon la colonne RowIndex que vous avez créée dans la requête principale: 

ORDER BY RowIndex DESC

Et enfin, utilisez TOP avec votre nombre de lignes souhaité:

    SELECT TOP 1 * --(or 2, or 5, or 34)
    FROM   (SELECT ROW_NUMBER() OVER (ORDER BY  (SELECT NULL) ) AS RowIndex, * 
            FROM MyTable) AS SubQuery
    ORDER BY RowIndex DESC
0
Thiago Marques

L'astuce consiste à interroger une simple sous-requête triée par ordre décroissant, suivie d'un tri croissant sur la même colonne.

SELECT * FROM 
    (SELECT TOP 200 * FROM [table] t2 ORDER BY t2.[column] DESC) t1
    ORDER BY t1.[column]
0
sheppe

essaye ça. 

declare @floor int --this is the offset from the bottom, the number of results to exclude
declare @resultLimit int --the number of results actually retrieved for use
declare @total int --just adds them up, the total number of results fetched initially

--following is for gathering top 60 results total, then getting rid of top 50. We only keep the last 10
set @floor = 50 
set @resultLimit = 10
set @total = @floor + @resultLimit

declare @tmp0 table(
    --table body
)

declare @tmp1 table(
    --table body
)

--this line will drop the wanted results from whatever table we're selecting from
insert into @tmp0
select Top @total --what to select (the where, from, etc)

--using floor, insert the part we don't want into the second tmp table
insert into @tmp1
select top @floor * from @tmp0

--using select except, exclude top x results from the query
select * from @tmp0
except 
select * from @tmp1
0
HumbleWebDev