填寫組內缺少的日期
我有下表,其中包含從
2014-01-01
到的不同站點的值2014-01-04
。數據有一些我想填補的日期空白,留下value
asNULL
,並將缺失的日期分配給每個站點。我正在使用 PostgreSQL 10.9這是我的桌子:
CREATE TABLE stations (station_id text, value integer, date date); INSERT INTO stations (station_id, value, date) VALUES ('001', 10, '2014-01-01'), ('001', 30, '2014-01-03'), ('002', 40, '2014-01-01'), ('002', 50, '2015-01-02'), ('003', 20, '2014-01-01'), ('003', 10, '2015-01-02'), ('003', 70, '2015-01-04');
我還有一張桌子,裡面有帶有標識符的唯一電台。
我想要這樣的東西:
| station | value | date | |---------|-------|------------| | 001 | 10 | 2014-01-01 | | 001 | NULL | 2014-01-02 | | 001 | 30 | 2014-01-03 | | 001 | NULL | 2014-01-04 | | 002 | 40 | 2014-01-01 | | 002 | 50 | 2014-01-02 | | 002 | NULL | 2014-01-03 | | 002 | NULL | 2014-01-04 | | 003 | 20 | 2014-01-01 | | 003 | 10 | 2014-01-02 | | 003 | NULL | 2014-01-03 | | 003 | 70 | 2014-01-04 |
在一些 DBA Exchange (questions) 1之後,我嘗試了 a
LEFT JOIN
與 a的組合LATERAL JOIN
:WITH complete_dates_station AS ( select station_id, generate_series(DATE '2014-01-01', DATE '2014-12-31', INTERVAL '1 day')::DATE as dt FROM stations GROUP by station_id ), temp_join AS ( SELECT station_id, dt, s.value FROM complete_dates_station LEFT JOIN LATERAL ( SELECT s.value FROM stations s WHERE s.station_id = complete_dates_station.station_id AND s.date = complete_dates_station.dt ORDER by s.station_id, date desc LIMIT 1) as s on TRUE ORDER BY station_id, dt ) SELECT * from temp_join
這就像一個魅力,但是對於我的完整表來說,這個連接真的很慢,它有超過 200 萬行並且日期範圍超過 18 年(我在執行 4 小時後停止了)。我通過使用正常嘗試了一種更簡單的方法
LEFT JOIN
,但是該表將未加入的組輸出為缺失:WITH complete_dates_station AS ( SELECT station_id, generate_series(date '2014-01-01', date '2014-12-31', interval '1 day')::date as dt from stations GROUP BY station_id) SELECT s.station_id, c.dt, s.value FROM complete_dates_station c left outer join stations s on c.station_id = s.station_id and c.dt = s.date;
產生以下結果:
| station | value | date | |---------|-------|------------| | 001 | 10 | 2014-01-01 | | NULL | NULL | 2014-01-02 | | 001 | 30 | 2014-01-03 | | NULL | NULL | 2014-01-04 | | 002 | 40 | 2014-01-01 | | 002 | 50 | 2014-01-02 | | NULL | NULL | 2014-01-03 | | NULL | NULL | 2014-01-04 | | 003 | 20 | 2014-01-01 | | 003 | 10 | 2014-01-02 | | NULL | NULL | 2014-01-03 | | 003 | 70 | 2014-01-04 |
有什麼方法可以優化第一個查詢,或者使用更簡單的方法來填補我
station
在第二個查詢中的空白?我已經嘗試在源表中使用多列索引,但查詢仍然需要很多時間。
您還有一個表格,其中包含帶有標識符的唯一電台,可能如下所示:
CREATE TABLE uniq_stations (station_id text); INSERT INTO uniq_stations VALUES ('001'), ('002'), ('003');
會有更多的列,與我們無關。
這應該會快得多:
SELECT station_id, s.value, date FROM uniq_stations u CROSS JOIN ( SELECT generate_series (timestamp '2014-01-01' , timestamp '2014-01-04' , interval '1 day')::date ) d(date) LEFT JOIN stations s USING (station_id, date) ORDER BY station_id, date; -- optional
db<>在這裡擺弄
您根本不需要
LATERAL
加入,每個站點的日期系列都是相同的。只有 aCROSS JOIN
建構完整的站和天數的笛卡爾積,然後 aLEFT [OUTER] JOIN
到表中的現有組合stations
(其內容的不幸表名,順便說一句。)。LATERAL
連接很棒,在需要時。但是普通連接更快。此外,這會填充所有天數缺失的電台,如果沒有
uniq_stations
. 您可能有也可能沒有這種情況。這個難題中昂貴的部分之一是辨識獨特的電台。如果我們可以使用添加的
uniq_stations
提供我們需要的東西,我們可以完全跳過一個任務。否則,我們可能會使用DISTINCT ON
遞歸 CTE 來使用匹配索引。看:仍然比從表中讀取唯一行更昂貴,但已經比你擁有的要快得多*。*坦率地說,這是對 CPU 週期的極大浪費。
最後,即使是大表,多列索引
stations (station_id, date)
也應該提供一流的性能stations
。(使用該表中更高百分比的行會使索引不那麼重要。)我使用是有原因的
generate_series (timestamp, timestamp, interval)
:有關的:
旁白:您
station_id
可能應該輸入integer
. 如果數字超過 999,則比 更快text
,也更小。