2009-09-28 7 views
45

Ho una tabella in PostgreSQL, eseguo una query su di essa con diverse condizioni che restituisce più righe, ordinate da una delle colonne. In generale è:Come ottenere il primo e l'ultimo record da una query sql?

SELECT <some columns> 
FROM mytable 
<maybe some joins here> 
WHERE <various conditions> 
ORDER BY date DESC 

Ora mi interessa solo ottenere la prima e l'ultima riga da questa query. Potrei trovarli al di fuori del db, all'interno della mia applicazione (e questo è quello che effettivamente faccio) ma mi chiedevo se per prestazioni migliori non dovrei ottenere dal database solo quei 2 record in cui sono effettivamente interessato.

E se sì, come posso modificare la mia query?

+2

Utilizzare le funzioni di aggregazione MIN e MAX: http://www.postgresql.org/docs/8.2/static/tutorial-agg.html –

+2

@rexem: min e max non funzioneranno su più colonne e funzioneranno su singola colonna solo se ordini da questa colonna. –

+0

Si potrebbe anche voler dare un'occhiata a 'SELECT DISTINCT ON (...) ... ORDER BY ...'. Vedi [Documentazione PostgreSQL] (https://www.postgresql.org/docs/current/static/sql-select.html). – RhinoDevel

risposta

55

[Caveat: Potrebbe non essere il modo più efficace per farlo]:

(SELECT <some columns> 
FROM mytable 
<maybe some joins here> 
WHERE <various conditions> 
ORDER BY date DESC 
LIMIT 1) 

UNION ALL 

(SELECT <some columns> 
FROM mytable 
<maybe some joins here> 
WHERE <various conditions> 
ORDER BY date ASC  
LIMIT 1) 
+7

Penso che la parola chiave 'Top' sia solo per SQL server, MySQL/Postgre utilizza 'Limit' – Robo

+1

Sei corretto. Modificherò il mio e voterò il tuo. –

+2

L'utilizzo di UNION ALL lo renderà leggermente più veloce, in quanto rimuove un controllo per i duplicati. Sarà diverso nel modo in cui funziona se la prima e l'ultima riga sono le stesse, ovviamente - UNION restituirà solo una riga, UNION ALL restituirà la stessa riga due volte. –

19

Primo record:

SELECT <some columns> FROM mytable 
<maybe some joins here> 
WHERE <various conditions> 
ORDER BY date ASC 
LIMIT 1 

Ultima annotazione:

SELECT <some columns> FROM mytable 
<maybe some joins here> 
WHERE <various conditions> 
ORDER BY date DESC 
LIMIT 1 
+0

Il metodo UNION ALL menzionato nell'altro commento sarà sicuramente più veloce dell'emissione di due query. –

4
SELECT * FROM TABLE_NAME WHERE ROWID=(SELECT MIN(ROWID) FROM TABLE_NAME) 
UNION 
SELECT * FROM TABLE_NAME WHERE ROWID=(SELECT MAX(ROWID) FROM TABLE_NAME) 

o

SELECT * FROM TABLE_NAME WHERE ROWID=(SELECT MIN(ROWID) FROM TABLE_NAME) 
          OR ROWID=(SELECT MAX(ROWID) FROM TABLE_NAME) 
+7

PostgreSQL non dispone di un 'rowid', si chiama' ctid' lì (e rowid né Oracle né garantisce ctid di PostgreSQL qualsiasi ordinamento) –

+4

Perché non rendere questo più semplice: 'SELECT * FROM nome_tabella WHERE ROWID = (SELECT MIN (rowid) FROM TABLE_NAME) OR rowid = (SELEZIONA MAX (rowid) FROM TABLE_NAME) ' –

18

Si potrebbe desiderare di provare questo, potrebbe potenzialmente essere più veloce di fare due query:

select <some columns> 
from (
    SELECT <some columns>, 
      row_number() over (order by date desc) as rn, 
      count(*) over() as total_count 
    FROM mytable 
    <maybe some joins here> 
    WHERE <various conditions> 
) t 
where rn = 1 
    or rn = total_count 
ORDER BY date DESC 
+0

Interessante. Non me ne sono reso conto. L'ho appena testato ed è, ovviamente, tutto vero. Grazie per l'intuizione. – DrFriedParts

12

ultimo record:

SELECT * FROM `aboutus` order by id desc limit 1 

primo disco:

SELECT * FROM `aboutus` order by id asc limit 1 
+1

Questo è SQL non valido per PostgreSQL (usa le doppie virgolette standard "" 'per la citazione dei nomi degli oggetti - e non sono comunque necessari qui) –

+0

Funziona, grazie! Qualcuno sa quanto sia efficiente? – Souleiman

+0

@souleiman Ogni query è il più veloce possibile. Il pianificatore di query utilizza l'indice appropriato e restituisce il più velocemente possibile O (log (N)) ... tuttavia, eseguendo questa operazione in 2 query separate sarà più lento e/o meno efficiente di una query se * sempre * vuoi * sia * il primo e l'ultimo record come indicato dall'OP. Usa UNION ALL (più veloce) tra le 2 query (o UNION se non vuoi duplicati). – DrFriedParts

1
select * 
from {Table_Name} 
where {x_column_name}=(
    select d.{x_column_name} 
    from (
     select rownum as rno,{x_column_name} 
     from {Table_Name})d 
     where d.rno=(
      select count(*) 
      from {Table_Name})); 
-1

perché non usare l'ordine di asc limite 1 e il retro - ordine dal limite di desc 1 ..

?

0
SELECT 
    MIN(Column), MAX(Column), UserId 
FROM 
    Table_Name 
WHERE 
    (Conditions) 
GROUP BY 
    UserId DESC 

o

SELECT   
    MAX(Column) 
FROM    
    TableName 
WHERE   
    (Filter) 

UNION ALL 

SELECT   
    MIN(Column) 
FROM    
    TableName AS Tablename1 
WHERE   
    (Filter) 
ORDER BY 
    Column 
0
-- Create a function that always returns the first non-NULL item 
CREATE OR REPLACE FUNCTION public.first_agg (anyelement, anyelement) 
RETURNS anyelement LANGUAGE SQL IMMUTABLE STRICT AS $$ 
     SELECT $1; 
$$; 


-- And then wrap an aggregate around it 
CREATE AGGREGATE public.FIRST (
     sfunc = public.first_agg, 
     basetype = anyelement, 
     stype = anyelement 
); 

-- Create a function that always returns the last non-NULL item 
CREATE OR REPLACE FUNCTION public.last_agg (anyelement, anyelement) 
RETURNS anyelement LANGUAGE SQL IMMUTABLE STRICT AS $$ 
     SELECT $2; 
$$; 

-- And then wrap an aggregate around it 
CREATE AGGREGATE public.LAST (
     sfunc = public.last_agg, 
     basetype = anyelement, 
     stype = anyelement 
); 

preso da qui: https://wiki.postgresql.org/wiki/First/last_(aggregate)

3

In tutti i modi esposte di fare fino ad ora, deve passare attraverso di scansione due volte, una per il primo riga e uno per l'ultima riga.

Utilizzando la funzione Finestra "ROW_NUMBER() OVER (...)" più "WITH Queries", è possibile eseguire la scansione solo una volta e ottenere entrambi gli elementi.

finestra Funzione: https://www.postgresql.org/docs/9.6/static/functions-window.html

con le query: https://www.postgresql.org/docs/9.6/static/queries-with.html

Esempio:

WITH scan_plan AS (
SELECT 
    <some columns>, 
    ROW_NUMBER() OVER (ORDER BY date DESC) AS first_row, /*It's logical required to be the same as major query*/ 
    ROW_NUMBER() OVER (ORDER BY date ASC) AS last_row /*It's rigth, needs to be the inverse*/ 
FROM mytable 
<maybe some joins here> 
WHERE <various conditions> 
ORDER BY date DESC) 

SELECT 
    <some columns> 
FROM scan_plan 
WHERE scan_plan.first_row = 1 OR scan_plan.last_row = 1; 

In questo modo si farà relazioni, filtrazioni e la manipolazione dei dati una sola volta.

Prova a SPIEGARE ANALIZZA in entrambi i modi.