Yalnızca 400 istasyon için bu sorgu çok daha hızlı olacaktır :
SELECT s.station_id, l.submitted_at, l.level_sensor
FROM station s
CROSS JOIN LATERAL (
SELECT submitted_at, level_sensor
FROM station_logs
WHERE station_id = s.station_id
ORDER BY submitted_at DESC NULLS LAST
LIMIT 1
) l;
dbfiddle burada
(bu sorgu için planları, Abelis'in alternatifini ve orijinalinizi karşılaştırarak)
EXPLAIN ANALYZE
OP tarafından sağlanan sonuç :
İç İçe Döngü (maliyet = 0.56..356.65 satır = 102 genişlik = 20) (gerçek zaman = 0.034..0.979 satır = 98 döngü = 1)
-> İstasyonlarda Seq Scan (s = maliyet = 0.00..3.02 satır = 102 genişlik = 4) (gerçek zaman = 0.009..0.016 satır = 102 döngü = 1)
-> Limit (maliyet = 0.56..3.45 satır = 1 genişlik = 16) (gerçek zaman = 0.009..0.009 satır = 1 döngü = 102)
-> station_logs üzerinde station_id__subored_at kullanılarak Dizin Tarama (maliyet = 0.56..664062.38 satır = 230223 genişlik = 16) (gerçek zaman = 0.009 $
Endeks Koşulu: (station_id = s.id)
Planlama süresi: 0.542 ms
Yürütme süresi: 1,013 ms - !!
Sadece endeks ihtiyacınız oluşturduğunuz biridir: station_id__submitted_at
. UNIQUE
Kısıt uniq_sid_sat
da temelde iş yapar. Her ikisini de korumak, disk alanı ve yazma performansı kaybı gibi görünüyor.
Eklediğim NULLS LAST
için ORDER BY
çünkü sorguda submitted_at
tanımlı değil NOT NULL
. İdeal olarak, varsa! Sütununa bir NOT NULL
sınırlama ekleyin submitted_at
, ek dizini bırakın NULLS LAST
ve sorgudan kaldırın .
Eğer submitted_at
olabilir NULL
, bu oluşturmak UNIQUE
geçerli dizini her ikisinin yerine indeksi ve benzersiz kısıtlama:
CREATE UNIQUE INDEX station_logs_uni ON station_logs(station_id, submitted_at DESC NULLS LAST);
Düşünmek:
Bu, her bir şekilde olması gereken ilgili her satırda (genellikle PK) bir satır içeren ayrı bir tablostation
olduğunu varsayar station_id
. Elinizde yoksa oluşturun. Yine, bu rCTE tekniği ile çok hızlı:
CREATE TABLE station AS
WITH RECURSIVE cte AS (
(
SELECT station_id
FROM station_logs
ORDER BY station_id
LIMIT 1
)
UNION ALL
SELECT l.station_id
FROM cte c
, LATERAL (
SELECT station_id
FROM station_logs
WHERE station_id > c.station_id
ORDER BY station_id
LIMIT 1
) l
)
TABLE cte;
Bunu kemanda da kullanıyorum. Görevinizi station
tablo olmadan doğrudan çözmek için benzer bir sorgu kullanabilirsiniz - eğer bunu oluşturmak için ikna edilemiyorsanız.
Ayrıntılı talimatlar, açıklama ve alternatifler:
Dizini optimize et
Sorgunuz şimdi çok hızlı olmalı. Yalnızca yine de okuma performansını optimize etmeniz gerekiyorsa ...
Joanolo'nun yorumladığı gibi yalnızca dizin taramasınalevel_sensor
izin vermek için dizine son sütun olarak eklemek mantıklı olabilir . Con: Dizini büyütür - bu, onu kullanan tüm sorgulara biraz maliyet katar. Pro: Eğer aslında sadece taramalar indeks alırsanız, eldeki sorgu yığın sayfaları hiç ziyaret etmek zorunda değilsiniz, bu da yaklaşık iki kat daha hızlı yapar. Ancak bu, şimdi çok hızlı bir sorgu için asılsız bir kazanç olabilir.
Ancak bunun davanız için çalışmasını beklemiyorum. Bahsettiniz:
... günde yaklaşık 20 bin satır station_id
.
Tipik olarak, bu, kesintisiz yazma yükünü gösterir ( station_id
her 5 saniyede bir 1). Ve son satırla ilgileniyorsunuz . Yalnızca dizin taramaları yalnızca tüm işlemler tarafından görülebilen yığın sayfaları için çalışır (görünürlük haritasındaki bit ayarlanır). VACUUM
Yazma yüküne ayak uydurmak için tablonun son derece agresif ayarlarını çalıştırmanız gerekir ve çoğu zaman işe yaramaz. Benim varsayımlar doğruysa, endeks salt taramalar, dışarı yok eklemek level_sensor
dizine.
Otoh, benim varsayımlar tutun ve masa büyüyor eğer çok büyük bir BRIN endeksi kudreti yardım. İlişkili:
Veya daha da özel ve daha verimli: Alakasız satırların büyük kısmını kesmek için yalnızca en son eklenenler için kısmi bir dizin:
CREATE INDEX station_id__submitted_at_recent_idx ON station_logs(station_id, submitted_at DESC NULLS LAST)
WHERE submitted_at > '2017-06-24 00:00';
Genç satırların var olması gerektiğini bildiğiniz bir zaman damgası seçin . WHERE
Aşağıdaki gibi tüm sorgulara eşleşen bir koşul eklemeniz gerekir :
...
WHERE station_id = s.station_id
AND submitted_at > '2017-06-24 00:00'
...
Dizini ve sorguyu zaman zaman uyarlamanız gerekir.
Daha fazla ayrıntı ile ilgili cevaplar: