select *
from records
where id in (select max(id) from records group by option_id)
Questa query funziona bene anche su milioni di righe. Tuttavia come si può vedere dal risultato di spiegare dichiarazione:Ottimizza query massima groupwise
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------
Nested Loop (cost=30218.84..31781.62 rows=620158 width=44) (actual time=1439.251..1443.458 rows=1057 loops=1)
-> HashAggregate (cost=30218.41..30220.41 rows=200 width=4) (actual time=1439.203..1439.503 rows=1057 loops=1)
-> HashAggregate (cost=30196.72..30206.36 rows=964 width=8) (actual time=1438.523..1438.807 rows=1057 loops=1)
-> Seq Scan on records records_1 (cost=0.00..23995.15 rows=1240315 width=8) (actual time=0.103..527.914 rows=1240315 loops=1)
-> Index Scan using records_pkey on records (cost=0.43..7.80 rows=1 width=44) (actual time=0.002..0.003 rows=1 loops=1057)
Index Cond: (id = (max(records_1.id)))
Total runtime: 1443.752 ms
(cost=0.00..23995.15 rows=1240315 width=8)
< - Qui si dice che è la scansione tutte le righe e che è ovviamente inefficiente.
Ho provato anche riordinando la query:
select r.* from records r
inner join (select max(id) id from records group by option_id) r2 on r2.id= r.id;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------
Nested Loop (cost=30197.15..37741.04 rows=964 width=44) (actual time=835.519..840.452 rows=1057 loops=1)
-> HashAggregate (cost=30196.72..30206.36 rows=964 width=8) (actual time=835.471..835.836 rows=1057 loops=1)
-> Seq Scan on records (cost=0.00..23995.15 rows=1240315 width=8) (actual time=0.336..348.495 rows=1240315 loops=1)
-> Index Scan using records_pkey on records r (cost=0.43..7.80 rows=1 width=44) (actual time=0.003..0.003 rows=1 loops=1057)
Index Cond: (id = (max(records.id)))
Total runtime: 840.809 ms
(cost=0.00..23995.15 rows=1240315 width=8)
< - Ancora la scansione di tutte le righe.
ho provato con e senza indice su (option_id)
, (option_id, id)
, (option_id, id desc)
, nessuno di loro ha avuto alcun effetto sul piano di query.
Esiste un modo per eseguire una query massima di gruppo in Postgres senza eseguire la scansione di tutte le righe?
Quello che sto cercando, a livello di programmazione, è un indice che memorizza l'ID massimo per ogni option_id
quando vengono inseriti nella tabella dei record. In questo modo, quando chiedo il massimo di option_ids, dovrei solo eseguire la scansione dei record dell'indice tante volte quante sono gli option_id diversi.
Ho visto select distinct on
risposte in tutto SO da utenti di alto livello (grazie a @Clodoaldo Neto per avermi fornito le parole chiave da cercare). Ecco perché non funziona:
create index index_name on records(option_id, id desc)
select distinct on (option_id) *
from records
order by option_id, id desc
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------------------------------
Unique (cost=0.43..76053.10 rows=964 width=44) (actual time=0.049..1668.545 rows=1056 loops=1)
-> Index Scan using records_option_id_id_idx on records (cost=0.43..73337.25 rows=1086342 width=44) (actual time=0.046..1368.300 rows=1086342 loops=1)
Total runtime: 1668.817 ms
È fantastico, sta utilizzando un indice. Tuttavia, usare un indice per scansionare tutti gli ID non ha molto senso. Secondo le mie esecuzioni, è in realtà più lento di una semplice scansione sequenziale.
Abbastanza interessante, MySQL 5.5 è in grado di ottimizzare la query semplicemente utilizzando un indice su records(option_id, id)
mysql> select count(1) from records;
+----------+
| count(1) |
+----------+
| 1086342 |
+----------+
1 row in set (0.00 sec)
mysql> explain extended select * from records
inner join (select max(id) max_id from records group by option_id) mr
on mr.max_id= records.id;
+------+----------+--------------------------+
| rows | filtered | Extra |
+------+----------+--------------------------+
| 1056 | 100.00 | |
| 1 | 100.00 | |
| 201 | 100.00 | Using index for group-by |
+------+----------+--------------------------+
3 rows in set, 1 warning (0.02 sec)
"Tuttavia utilizzando un indice per la scansione di tutte le righe in realtà non ha molto senso "--- lo fa. Gli indici sono più piccoli dell'intero set di dati ed è più probabile che si trovino in una cache. Tuttavia, non esegue la scansione delle righe effettive, ma dell'indice. – zerkms
Qual è il piano per la query * original * con indice creato? – zerkms
L'indicizzazione @zerkms option_id non ha fatto alcuna differenza (come ho affermato nella domanda) L'indicizzazione option_id_id_desc o option_id_id non fa alcuna differenza nel piano di query. – nurettin