Wie wähle ich die untersten Zeilen aus?

100

Ich kann SELECT TOP (200) machen ... aber warum nicht BOTTOM (200)?

Um nicht in die Philosophie einzusteigen, meine ich, wie kann ich das Äquivalent von TOP (200) machen, aber umgekehrt (von unten, wie Sie es von UNTEN erwarten würden ...)?

CloudMeta
quelle

Antworten:

89
SELECT
    columns
FROM
(
     SELECT TOP 200
          columns
     FROM
          My_Table
     ORDER BY
          a_column DESC
) SQ
ORDER BY
     a_column ASC
Tom H.
quelle
2
Warum verwenden Sie eine abgeleitete Tabelle?
RichardOD
14
Wenn Sie Zeilen in der Reihenfolge A-> Z zurückgeben möchten, aber die Top 200 in der Reihenfolge Z-> A auswählen möchten, ist dies eine Möglichkeit, dies zu tun. Die anderen Antworten, die darauf hindeuten, nur ORDER BY zu ändern, liefern nicht die gleichen Ergebnisse wie in der Frage beschrieben, da sie nicht in der richtigen Reihenfolge sind (es sei denn, die Reihenfolge spielt keine Rolle, was das OP nicht gesagt hat).
Tom H
3
@ Tom H. Musste ein paar Sekunden darüber nachdenken, was du meinst (ich bin seit 14 Stunden auf). Zuerst konnte ich den Unterschied zwischen Ihrer und der Reihenfolge anhand der Antworten nicht erkennen, aber jetzt kann ich es. Also +1.
RichardOD
1
Diese und andere Antworten funktionieren gut, wenn Sie an kleineren Tabellen arbeiten. Ich denke nicht, dass es sich lohnt, die gesamte Tabelle nach einer Spalte zu ordnen, wenn Sie nur an den unteren Zeilen interessiert sind.
Steadyfish
1
Gute Antwort, das Beste imho ... das eigentliche Problem ist, dass ich dies nicht über ein ASP-Skript tun kann. Ich denke, ich muss das objRecordset manuell oder mit der von ASP
bereitgestellten
99

Es ist unnötig. Sie können ein verwenden ORDER BYund einfach die Sortierung ändern DESC, um den gleichen Effekt zu erzielen.

Justin Ethier
quelle
3
Google sagte das Gleiche und jetzt stimmen 9 von Ihnen zu, gut genug für mich, danke: D
CloudMeta
8
Bei Verwendung von DESC werden die letzten N Zeilen zurückgegeben, die zurückgegebenen Zeilen werden jedoch auch in umgekehrter Reihenfolge von den ersten N Zeilen angezeigt.
RickNZ
11
Was ist, wenn Ihre Tabelle keinen Index für ORDER BY enthält?
Beschützer ein
7
@ Justin: Stellen Sie sich vor, Sie haben nur eine Spalte mit Varchar-Werten. ORDER BY würde alphabetisch sortieren, was (wahrscheinlich) nicht das ist, was wir wollen.
Beschützer ein
3
Tom H. ist die richtige Antwort, andernfalls sind Ihre Zeilen in umgekehrter Reihenfolge.
Pierre-Olivier Goulet
39

Entschuldigung, aber ich glaube, ich sehe meiner Meinung nach keine richtigen Antworten.

Die TOPx-Funktion zeigt die Datensätze in undefinierter Reihenfolge an. Aus dieser Definition folgt, dass eine BOTTOMFunktion nicht definiert werden kann.

Unabhängig von Index oder Sortierreihenfolge. Wenn Sie dies tun, erhalten ORDER BY y DESCSie zuerst die Zeilen mit dem höchsten y-Wert. Wenn es sich um eine automatisch generierte ID handelt, sollten die zuletzt zur Tabelle hinzugefügten Datensätze angezeigt werden, wie in den anderen Antworten vorgeschlagen. Jedoch:

  • Dies funktioniert nur, wenn eine automatisch generierte ID-Spalte vorhanden ist
  • Wenn Sie dies mit der TOPFunktion vergleichen, hat dies erhebliche Auswirkungen auf die Leistung

Die richtige Antwort sollte sein, dass es kein Äquivalent zum TOPAbrufen der unteren Zeilen gibt und auch nicht geben kann .

Martijn Burger
quelle
3
Es stimmt, es scheint kein Äquivalent zu geben, nur Problemumgehungen.
Poke
4
TOP hat nichts mit der Reihenfolge zu tun, in der Elemente zu einer Tabelle hinzugefügt wurden. Es bedeutet lediglich "Geben Sie die ersten X Datensätze an, die meiner Abfrage entsprechen"
Luke
4
Ja, es gibt Ihnen die ersten Datensätze, die der Tabelle hinzugefügt wurden und Ihrer Abfrage entsprechen.
Martijn Burger
4
Ich stimme Luke zu. Datenbanktabellen sind per Definition auftragslos. Man sollte sich niemals auf die vom RDBMS bereitgestellte Reihenfolge verlassen, wenn die select-Anweisung keine ORDER BY-Klausel enthält. Lesen Sie hier im Wiki Das Datenbanksystem garantiert jedoch keine Reihenfolge der Zeilen, es sei denn, in der SELECT-Anweisung, die die Tabelle abfragt, ist eine ORDER BY-Klausel angegeben.
Zohar Peled
2
Ich stimme dieser Antwort zu, aber in der Praxis löst Toms Antwort das Problem für alle praktischen Zwecke.
Antonio
18

Logisch,

BOTTOM (x) is all the records except TOP (n - x), where n is the count; x <= n

Wählen Sie zB Bottom 1000 vom Mitarbeiter aus:

In T-SQL

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
)
Shadi Namrouti
quelle
1
Hallo Shadi2014, ohne die Verwendung von "ORDER BY" wird Ihr Ergebnis irgendwie zufällig sein.
Bummi
5
bummi, du hast recht, aber das macht diese Antwort richtig. Select TOP selbst ist theoretisch "zufällig", und dies ist die richtige Implementierung für Select BOTTOM. In einer Tabelle mit 5000 Datensätzen ist die untere 1000 alles außer der oberen 4000.
Tzachs
9

Es scheint, dass eine der Antworten, die eine ORDER BY-Klausel in der Lösung implementieren, den Punkt verfehlt oder nicht wirklich versteht, was TOP an Sie zurückgibt.

TOP gibt eine ungeordnete Abfrageergebnismenge zurück, die die Datensatzmenge auf die ersten N zurückgegebenen Datensätze beschränkt. (Aus Oracle-Sicht entspricht dies dem Hinzufügen eines where ROWNUM <(N + 1).

Jede Lösung, die eine Bestellung verwendet, kann Zeilen zurückgeben, die auch von der TOP-Klausel zurückgegeben werden (da dieser Datensatz überhaupt nicht geordnet war), abhängig davon, welche Kriterien in der Bestellung von verwendet wurden

Der Nutzen von TOP besteht darin, dass das Dataset keine Zeilen mehr abruft, sobald es eine bestimmte Größe N erreicht. Sie können ein Gefühl dafür bekommen, wie die Daten aussehen, ohne alle abrufen zu müssen.

Um BOTTOM genau zu implementieren, müsste der gesamte Datensatz ungeordnet abgerufen und dann auf die endgültigen N Datensätze beschränkt werden. Dies ist nicht besonders effektiv, wenn Sie mit großen Tabellen arbeiten. Es wird Ihnen auch nicht unbedingt das geben, wonach Sie zu fragen glauben . Das Ende des Datensatzes muss nicht unbedingt "die zuletzt eingefügten Zeilen" sein (und wird wahrscheinlich nicht für die meisten DML-intensiven Anwendungen sein).

Ebenso sind die Lösungen, die ein ORDER BY implementieren, beim Umgang mit großen Datenmengen leider potenziell katastrophal. Wenn ich beispielsweise 10 Milliarden Datensätze habe und die letzten 10 möchte, ist es ziemlich dumm, 10 Milliarden Datensätze zu bestellen und die letzten 10 auszuwählen.

Das Problem hierbei ist, dass BOTTOM nicht die Bedeutung hat, an die wir denken, wenn wir es mit TOP vergleichen.

Wenn Datensätze immer und immer wieder eingefügt, gelöscht, eingefügt, gelöscht werden, treten einige Lücken im Speicher auf, und später werden nach Möglichkeit Zeilen eingefügt. Was wir jedoch häufig sehen, wenn wir TOP auswählen, scheinen sortierte Daten zu sein, da sie möglicherweise schon früh in die Existenz der Tabelle eingefügt wurden. Wenn die Tabelle nicht viele Löschungen erfährt, kann es erscheinen sie geordnet zu sein. (z. B. können die Erstellungsdaten so weit zurückliegen wie die Tabellenerstellung selbst). Die Realität ist jedoch, dass wenn es sich um eine löschlastige Tabelle handelt, die TOP N-Zeilen möglicherweise überhaupt nicht so aussehen.

Das Fazit hier (Wortspiel beabsichtigt) ist, dass jemand, der nach den UNTEN N-Datensätzen fragt, nicht wirklich weiß, wonach er fragt. Oder zumindest, was sie verlangen und was UNTEN eigentlich bedeutet, ist nicht dasselbe.

Die Lösung erfüllt möglicherweise die tatsächlichen Geschäftsanforderungen des Anforderers ... erfüllt jedoch nicht die Kriterien für die UNTEN.

user9323238
quelle
1
Hervorragende Erklärung. Upvote für mehr Licht in das Thema.
Rohrl77
Mein Anwendungsfall ist dies. Ich habe eine große insertAnweisung ausgeführt, um Zeilen in eine große, nicht indizierte Tabelle einzufügen. (Ich fülle die Tabelle zuerst auf, bevor ich mit der Indizierung beginne.) Ich habe meine Client-Sitzung aufgrund eines Neustarts oder was auch immer verloren und möchte nun sehen, ob sich meine neu hinzugefügten Zeilen dort befinden. Wenn die unterste Zeile der Tabelle eine meiner letzten ist, weiß ich, dass der Vorgang abgeschlossen ist. Wenn die 'untere' Zeile etwas anderes ist, gibt es keine Garantien und ich muss die gesamte Tabelle scannen, um sicherzugehen ... aber höchstwahrscheinlich könnte ich Zeit sparen, indem ich schnell die 'untere' überprüfe, genau wie Sie die ' oben'.
Ed Avis
Gute Erklärung, aber es impliziert immer noch die Existenz eines Bodens, der nur erfordert, dass die Daten in umgekehrter Reihenfolge gelesen / abgerufen werden. Im (zugegebenermaßen Rand-) Fall einer abgebrochenen Einfügung in eine neue Tabelle wäre es nützlich, den zuletzt eingefügten Datensatz (unten) zu überprüfen, ohne alles abzurufen. Gibt es einen technischen Grund, warum die Tabellendaten nicht in umgekehrter Reihenfolge abgerufen werden können?
James
3

Die derzeit akzeptierte Antwort von "Justin Ethier" ist keine korrekte Antwort, wie von "Protector One" hervorgehoben.

Soweit ich sehen kann, entspricht derzeit keine andere Antwort oder kein anderer Kommentar UNTEN (x), um die der Autor der Frage gebeten hat.

Betrachten wir zunächst ein Szenario, in dem diese Funktionalität benötigt wird:

SELECT * FROM Split('apple,orange,banana,apple,lime',',')

Dies gibt eine Tabelle mit einer Spalte und fünf Datensätzen zurück:

  • Apfel
  • Orange
  • Banane
  • Apfel
  • Limette

Wie Sie sehen können: Wir haben keine ID-Spalte; Wir können nicht nach der zurückgegebenen Spalte bestellen. und wir können die beiden unteren Datensätze nicht mit Standard-SQL auswählen, wie wir es für die beiden oberen Datensätze tun können.

Hier ist mein Versuch, eine Lösung zu finden:

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

Und hier ist eine vollständigere Lösung:

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

Ich behaupte keineswegs, dass dies unter allen Umständen eine gute Idee ist, aber es liefert die gewünschten Ergebnisse.

Tomosius
quelle
1

Alles was Sie tun müssen, ist Ihre umzukehren ORDER BY. Hinzufügen oder entfernen DESC.

Justin Swartsel
quelle
1

Das Problem bei der Bestellung in die andere Richtung ist, dass Indizes häufig nicht gut genutzt werden. Es ist auch nicht sehr erweiterbar, wenn Sie jemals eine Anzahl von Zeilen auswählen müssen, die nicht am Anfang oder am Ende stehen. Ein alternativer Weg ist wie folgt.

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;
Paul
quelle
2
1) Wenn Sie die Richtung Ihrer ORDER BY-Klausel ändern, "Indizes werden nicht gut genutzt", erhalten Sie ein anständiges RDBMS! Dem RDBMS sollte es egal sein, ob es den Index vorwärts oder rückwärts navigiert. 2) Sie sind besorgt über die Verwendung von Indizes, aber Ihre Lösung fügt jeder Zeile in der Tabelle eine Sequenz hinzu ... Dies ist eine Möglichkeit, um sicherzustellen, dass kein geeigneter Index verwendet wird.
Desillusioniert
1

Die Antwort "Tom H" oben ist korrekt und funktioniert bei mir, wenn ich die unteren 5 Zeilen erhalte.

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

Vielen Dank.

user3598017
quelle
0

Versuche dies.

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
HumbleWebDev
quelle
Was macht Ihren Code für das OP? Bitte fügen Sie ein wenig mehr Erklärung hinzu, wie Sie versuchen, das Problem zu lösen
Techspider
Ich habe eine Bearbeitung vorgenommen. Ich hoffe, dass es das etwas besser erklärt und mehr Kommentare hinzufügt. Die Hauptidee ist, top x aus einer Tabelle auszuwählen, dann top x - num gewünscht auszuwählen und dann eine Ausnahme-Anweisung zu verwenden, um die nicht benötigten Ergebnisse auszuschließen.
HumbleWebDev
0

Ich habe eine Lösung für dieses Problem gefunden, bei der Sie die Anzahl der zurückgegebenen Zeilen nicht kennen müssen.

Wenn Sie beispielsweise alle Speicherorte in einer Tabelle protokollieren möchten, mit Ausnahme der letzten 1 (oder 2, 5 oder 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
rot
quelle
0

Das Abfragen einer einfachen Unterabfrage, sortiert absteigend, gefolgt von einer Sortierung in derselben Spalte aufsteigend, reicht aus.

SELECT * FROM 
    (SELECT TOP 200 * FROM [table] t2 ORDER BY t2.[column] DESC) t1
    ORDER BY t1.[column]
sheppe
quelle
0
SELECT TOP 10*from TABLE1 ORDER BY ID DESC

Wobei ID der Primärschlüssel der TABELLE1 ist.

Er. Binod Mehta
quelle
0

Erstellen Sie zunächst einen Index in einer Unterabfrage gemäß der ursprünglichen Reihenfolge der Tabelle mit:

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

Ordnen Sie dann die Tabelle nach der RowIndexSpalte ab, die Sie in der Hauptabfrage erstellt haben:

ORDER BY RowIndex DESC

Und schließlich verwenden Sie TOPmit Ihrer gewünschten Anzahl von Zeilen:

    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
Thiago Marques
quelle