Questa domanda è relativa a this. Ho una tabella che contiene i valori di alimentazione per i dispositivi e ho bisogno di calcolare il consumo di energia per un determinato intervallo di tempo e restituire 10 dispositivi più consumanti. Ho generato 192 dispositivi e 7742208 record di misurazione (40324 per ciascuno). Questo è all'incirca quanto i dispositivi di registrazione produrranno in un mese.Come ottimizzare la query SQL con le funzioni della finestra
Per questa quantità di dati la mia query corrente richiede più di 40 secondi per l'esecuzione, il che è eccessivo perché l'intervallo di tempo e la quantità di dispositivi e misurazioni potrebbero essere molto più elevati. Dovrei provare a risolvere questo problema con un approccio diverso da lag() OVER PARTITION e quali altre ottimizzazioni possono essere fatte? Apprezzerei molto i suggerimenti con esempi di codice.
PostgreSQL versione 9.4
query con valori di esempio:
SELECT
t.device_id,
sum(len_y*(extract(epoch from len_x))) AS total_consumption
FROM (
SELECT
m.id,
m.device_id,
m.power_total,
m.created_at,
m.power_total+lag(m.power_total) OVER (
PARTITION BY device_id
ORDER BY m.created_at
) AS len_y,
m.created_at-lag(m.created_at) OVER (
PARTITION BY device_id
ORDER BY m.created_at
) AS len_x
FROM
measurements AS m
WHERE m.created_at BETWEEN '2015-07-30 13:05:24.403552+00'::timestamp
AND '2015-08-27 12:34:59.826837+00'::timestamp
) AS t
GROUP BY t.device_id
ORDER BY total_consumption
DESC LIMIT 10;
informazioni Classifica:
Column | Type | Modifiers
--------------+--------------------------+----------------------------------------------------------
id | integer | not null default nextval('measurements_id_seq'::regclass)
created_at | timestamp with time zone | default timezone('utc'::text, now())
power_total | real |
device_id | integer | not null
Indexes:
"measurements_pkey" PRIMARY KEY, btree (id)
"measurements_device_id_idx" btree (device_id)
"measurements_created_at_idx" btree (created_at)
Foreign-key constraints:
"measurements_device_id_fkey" FOREIGN KEY (device_id) REFERENCES devices(id)
piano di query:
Limit (cost=1317403.25..1317403.27 rows=10 width=24) (actual time=41077.091..41077.094 rows=10 loops=1)
-> Sort (cost=1317403.25..1317403.73 rows=192 width=24) (actual time=41077.089..41077.092 rows=10 loops=1)
Sort Key: (sum((((m.power_total + lag(m.power_total) OVER (?))) * date_part('epoch'::text, ((m.created_at - lag(m.created_at) OVER (?)))))))
Sort Method: top-N heapsort Memory: 25kB
-> GroupAggregate (cost=1041700.67..1317399.10 rows=192 width=24) (actual time=25361.013..41076.562 rows=192 loops=1)
Group Key: m.device_id
-> WindowAgg (cost=1041700.67..1201314.44 rows=5804137 width=20) (actual time=25291.797..37839.727 rows=7742208 loops=1)
-> Sort (cost=1041700.67..1056211.02 rows=5804137 width=20) (actual time=25291.746..30699.993 rows=7742208 loops=1)
Sort Key: m.device_id, m.created_at
Sort Method: external merge Disk: 257344kB
-> Seq Scan on measurements m (cost=0.00..151582.05 rows=5804137 width=20) (actual time=0.333..5112.851 rows=7742208 loops=1)
Filter: ((created_at >= '2015-07-30 13:05:24.403552'::timestamp without time zone) AND (created_at <= '2015-08-27 12:34:59.826837'::timestamp without time zone))
Planning time: 0.351 ms
Execution time: 41114.883 ms
query per generare tabella di prova e dati:
CREATE TABLE measurements (
id serial primary key,
device_id integer,
power_total real,
created_at timestamp
);
INSERT INTO measurements(
device_id,
created_at,
power_total
)
SELECT
device_id,
now() + (i * interval '1 minute'),
random()*(50-1)+1
FROM (
SELECT
DISTINCT(device_id),
generate_series(0,10) AS i
FROM (
SELECT
generate_series(1,5) AS device_id
) AS dev_ids
) AS gen_table;
Che ne dici di un indice composito su (id_dispositivo, creato_at)? BTW, IMHO dovresti dividere 'm.power_total + lag (m.power_total)' di due prima dell'uso. (O prendi semplicemente la media) – joop
+1 La migliore domanda che ho visto da molto tempo. Campione molto ben scritto e corretto. Creo il campione db in un secondo. Ora quali valori devo inserire nella serie per generare un db simile alla tua dimensione attuale? –
La condizione 'where' non rimuove alcuna riga. È destinato? L'ordinamento viene fatto anche su disco: 'external merge Disk: 257344kB' che richiede abbastanza tempo (il piano di esecuzione ha perso l'indentazione, quindi è un po 'difficile da leggere).Se aumenti il 'work_mem' per la sessione fino a quando l'ordinamento è fatto in memoria, dovresti vedere prestazioni migliori. –