Come selezionare la maggior parte delle righe in basso?


100

Posso fare SELECT TOP (200) ... ma perché non BOTTOM (200)?

Beh, per non entrare nella filosofia, quello che intendo è, come posso fare l'equivalente di TOP (200) ma al contrario (dal basso, come ti aspetteresti che facesse BOTTOM ...)?

Risposte:


89
SELECT
    columns
FROM
(
     SELECT TOP 200
          columns
     FROM
          My_Table
     ORDER BY
          a_column DESC
) SQ
ORDER BY
     a_column ASC

2
Perché stai usando una tabella derivata?
RichardOD

14
Se vuoi restituire le righe in ordine A-> Z, ma seleziona le prime 200 in ordine Z-> A, questo è un modo per farlo. Le altre risposte, suggerendo semplicemente di modificare ORDER BY non restituiranno gli stessi risultati descritti nella domanda, poiché saranno fuori servizio (a meno che l'ordine non abbia importanza, cosa che l'OP non ha detto).
Tom H

3
@ Tom H. Ho dovuto pensare per alcuni secondi a cosa intendevi (sono stato in piedi 14 ore). All'inizio non riuscivo a vedere la differenza tra il tuo e l'ordine in base alle risposte, ma ora posso. Quindi +1.
RichardOD

1
Questa e altre risposte funzionano bene quando lavori su tavoli più piccoli. Non penso che valga la pena ordinare l'intera tabella in base a una colonna quando sei interessato solo alle ultime righe.
steadyfish

1
buona risposta, il miglior imho ... il vero problema è che non posso farlo da uno script ASP, quindi penso di aver bisogno di riordinare objRecordset manualmente o con la funzione fornita da ASP ....
Andrea_86

99

Non è necessario. È possibile utilizzare un ORDER BYe modificare l'ordinamento in DESCper ottenere lo stesso effetto.


3
google ha detto la stessa cosa e ora 9 di voi sono d'accordo, va bene per me, grazie: D
CloudMeta

8
L'utilizzo di DESC restituirà le ultime N righe, ma anche le righe restituite saranno in ordine inverso rispetto alle prime N righe.
RickNZ

11
E se non ci fosse un indice sulla tua tabella per ORDER BY?
Protettore uno il

8
@ Justin: Immagina di avere solo una colonna, contenente i valori varchar. ORDER BY ordina alfabeticamente, che (probabilmente) non è quello che vogliamo.
Protector one

3
Tom H. è la risposta corretta, altrimenti le tue righe saranno in ordine inverso.
Pierre-Olivier Goulet

39

Scusa, ma non credo di vedere risposte corrette secondo me.

La TOPfunzione x mostra i record in un ordine indefinito. Da quella definizione segue che una BOTTOMfunzione non può essere definita.

Indipendente da qualsiasi indice o ordinamento. Quando esegui una ORDER BY y DESC, ottieni prima le righe con il valore y più alto. Se si tratta di un ID generato automaticamente, dovrebbe mostrare gli ultimi record aggiunti alla tabella, come suggerito nelle altre risposte. Però:

  • Funziona solo se è presente una colonna id generata automaticamente
  • Ha un impatto significativo sulle prestazioni se confrontato con la TOPfunzione

La risposta corretta dovrebbe essere che non esiste e non può esserci un equivalente TOPper ottenere le righe inferiori.


3
È vero, sembra che non ci siano equivalenti, solo soluzioni alternative.
colpisci il

4
TOP non ha nulla a che fare con l'ordine in cui gli elementi sono stati aggiunti a una tabella, significa semplicemente "Dai i primi X record che corrispondono alla mia query"
Luca

4
Sì, ti fornisce i primi record aggiunti alla tabella che corrispondono alla tua query.
Martijn Burger

4
Sono d'accordo con Luca. Le tabelle del database sono prive di ordine per definizione. Non si dovrebbe mai fare affidamento sull'ordine fornito dall'RDBMS quando l'istruzione select non ha alcuna clausola ORDER BY. leggi qui su wiki Tuttavia, il sistema di database non garantisce alcun ordinamento delle righe a meno che non sia specificata una clausola ORDER BY nell'istruzione SELECT che interroga la tabella.
Zohar Peled

2
Sono d'accordo con questa risposta, ma in pratica la risposta di Tom risolve il problema per tutti gli usi pratici.
Antonio

18

Logicamente,

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

Ad esempio, seleziona gli ultimi 1000 da dipendente:

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
)

1
Ciao shadi2014, senza l'uso di "ORDER BY" il tuo risultato sarà in qualche modo casuale.
bummi

5
bummi, hai ragione, ma questo è ciò che rende corretta questa risposta. Select TOP stesso è "casuale" in teoria e questa è l'implementazione corretta per Select BOTTOM. In una tabella di 5000 record, gli ultimi 1000 sono tutto tranne i primi 4000.
tzachs

9

Sembrerebbe che nessuna delle risposte che implementano una clausola ORDER BY nella soluzione manchi il punto, o non capisca effettivamente cosa ti restituisce TOP.

TOP restituisce un set di risultati della query non ordinato che limita il set di record ai primi N record restituiti. (Dal punto di vista di Oracle, è simile all'aggiunta di un punto in cui ROWNUM <(N + 1).

Qualsiasi soluzione che utilizza un ordine, può restituire righe che vengono anche restituite dalla clausola TOP (poiché tale set di dati non era ordinato in primo luogo), a seconda di quali criteri sono stati utilizzati nell'ordine da

L'utilità di TOP è che una volta che il set di dati raggiunge una certa dimensione N, smette di recuperare le righe. Puoi avere un'idea dell'aspetto dei dati senza doverli recuperare tutti.

Per implementare BOTTOM in modo accurato, è necessario recuperare l'intero set di dati non ordinato e quindi limitare il set di dati agli N record finali. Ciò non sarà particolarmente efficace se hai a che fare con tavoli enormi. Né ti darà necessariamente quello che pensi di chiedere. La fine del set di dati potrebbe non essere necessariamente "le ultime righe inserite" (e probabilmente non lo sarà per la maggior parte delle applicazioni DML intensive).

Allo stesso modo, le soluzioni che implementano un ORDER BY sono, sfortunatamente, potenzialmente disastrose quando si tratta di grandi set di dati. Se ho, diciamo, 10 miliardi di record e voglio gli ultimi 10, è piuttosto sciocco ordinare 10 miliardi di record e selezionare gli ultimi 10.

Il problema qui è che BOTTOM non ha il significato che noi pensiamo quando lo confrontiamo con TOP.

Quando i record vengono inseriti, cancellati, inseriti, cancellati più e più volte, alcuni spazi verranno visualizzati nella memoria e successivamente, se possibile, verranno inserite delle righe. Ma quello che vediamo spesso, quando selezioniamo TOP, appare essere un dato ordinato, perché potrebbe essere stato inserito all'inizio dell'esistenza della tabella. Se la tabella non presenta molte eliminazioni, potrebbe essere visualizzato ordinata. (ad esempio, le date di creazione possono essere indietro nel tempo quanto la creazione della tabella stessa). Ma la realtà è che, se si tratta di una tabella che richiede molte operazioni di eliminazione, le prime N righe potrebbero non essere affatto così.

Quindi, la linea di fondo qui (gioco di parole) è che qualcuno che sta chiedendo i record BOTTOM N in realtà non sa cosa sta chiedendo. O, almeno, quello che stanno chiedendo e quello che in realtà significa BOTTOM non sono la stessa cosa.

Quindi, la soluzione può soddisfare le effettive esigenze aziendali del richiedente ... ma non soddisfa i criteri per essere INFERIORE.


1
Ottima spiegazione. Voto positivo per far luce sull'argomento.
rohrl77

Il mio caso d'uso è questo. Ho eseguito una grande insertistruzione per inserire le righe in una tabella grande e non indicizzata. (Sto popolando la tabella prima di iniziare a indicizzarla.) Ho perso la mia sessione client a causa di un riavvio o altro, e ora voglio vedere se le mie righe appena aggiunte sono lì. Se la riga "inferiore" della tabella è una delle mie recenti, so che l'operazione è stata completata. Se la riga 'inferiore' è qualcos'altro, beh non ci sono garanzie e devo scansionare l'intera tabella per assicurarmene ... ma molto probabilmente potrei risparmiare un po 'di tempo controllando rapidamente la' parte inferiore 'proprio come puoi fare tu' superiore'.
Ed Avis,

Buona spiegazione, ma implica comunque l'esistenza di un fondale, che richiede solo la lettura / recupero dei dati al contrario. Nel caso (ammettibilmente limite) di un inserimento abortito su una nuova tabella, sarebbe utile verificare l'ultimo record inserito (il fondo) senza recuperare tutto. C'è un motivo tecnico per cui i dati della tabella non possono essere recuperati in ordine inverso?
James,

3

La risposta attualmente accettata da "Justin Ethier" non è una risposta corretta come sottolineato da "Protector one".

Per quanto posso vedere, per ora, nessun'altra risposta o commento fornisce l'equivalente di BOTTOM (x) richiesto dall'autore della domanda.

Per prima cosa, consideriamo uno scenario in cui sarebbe necessaria questa funzionalità:

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

Ciò restituisce una tabella di una colonna e cinque record:

  • Mela
  • arancia
  • Banana
  • Mela
  • lime

Come puoi vedere: non abbiamo una colonna ID; non possiamo ordinare in base alla colonna restituita; e non possiamo selezionare gli ultimi due record utilizzando SQL standard come possiamo fare per i primi due record.

Ecco il mio tentativo di fornire una soluzione:

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

Ed ecco una soluzione più completa:

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

Non sto affatto affermando che questa sia una buona idea da usare in tutte le circostanze, ma fornisce i risultati desiderati.


1

Tutto quello che devi fare è invertire il tuo ORDER BY. Aggiungi o rimuovi DESCad esso.


1

Il problema con l'ordinamento nell'altro modo è che spesso non fa buon uso degli indici. Inoltre, non è molto estendibile se è necessario selezionare un numero di righe che non sono all'inizio o alla fine. Un modo alternativo è il seguente.

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;

2
1) Se cambiare la direzione della tua clausola ORDER BY "non fa buon uso degli indici", allora ottieni un RDBMS decente! L'RDBMS non dovrebbe mai preoccuparsi se naviga nell'indice in avanti o indietro. 2) Sei preoccupato per l'uso degli indici, ma la tua soluzione allega una sequenza a ogni riga della tabella ... Questo è un modo per garantire che NON venga utilizzato un indice appropriato.
Disilluso

1

La risposta "Tom H" sopra è corretta e funziona per me nell'ottenere le 5 righe inferiori.

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

Grazie.


0

prova questo.

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

cosa rende il tuo codice per l'OP? Si prega di aggiungere un po 'più di spiegazione su come si tenta di risolvere il problema
techspider

Ho fatto una modifica. Spero che lo spieghi un po 'meglio, aggiungendo altri commenti. L'idea principale è selezionare la x superiore da una tabella, quindi selezionare la x superiore - numero desiderato, quindi utilizzare un'istruzione tranne per escludere i risultati non necessari.
HumbleWebDev

0

Ho trovato una soluzione a questo che non richiede di conoscere il numero di righe restituite.

Ad esempio, se desideri ottenere tutte le posizioni registrate in una tabella, tranne l'ultima 1 (o 2, o 5 o 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

L'interrogazione di una semplice sottoquery ordinata in ordine discendente, seguita dall'ordinamento sulla stessa colonna in ordine crescente, fa il trucco.

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

0
SELECT TOP 10*from TABLE1 ORDER BY ID DESC

Dove ID è la chiave primaria di TABLE1.


0

Innanzitutto, crea un indice in una sottoquery secondo l'ordine originale della tabella utilizzando:

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

Quindi ordina la tabella in modo discendente dalla RowIndexcolonna che hai creato nella query principale:

ORDER BY RowIndex DESC

E infine usa TOPcon la quantità di righe desiderata:

    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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.