สำหรับ 400 สถานีเท่านั้นการค้นหานี้จะเร็วขึ้นอย่างมาก :
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 ที่นี่
(เปรียบเทียบแผนสำหรับแบบสอบถามนี้ทางเลือกของ Abelisto กับต้นฉบับของคุณ)
ผลEXPLAIN ANALYZE
ที่ได้รับจาก OP:
วนซ้ำ (ต้นทุน = 0.56..356.65 แถว = ความกว้าง 102 = 20) (เวลาจริง = 0.034..0.979 แถว = 98 ลูป = 1)
-> Seq สแกนสถานี s (ราคา = 0.00..3.02 แถว = 102 กว้าง = 4) (เวลาจริง = 0.009..0.016 แถว = 102 ลูป = 1)
-> จำกัด (ค่า = 0.56..3.45 แถว = 1 ความกว้าง = 16) (เวลาจริง = 0.009..0.009 แถว = 1 ห่วง = 102)
-> การสแกนดัชนีโดยใช้ station_id__submitted_at บน station_logs (ราคา = 0.56..664062.38 แถว = 230223 กว้าง = 16) (เวลาจริง = 0.009 $
ดัชนี Cond: (station_id = s.id)
เวลาในการวางแผน: 0.542 ms
เวลาดำเนินการ: 1.013 ms - !!
เพียงดัชนีstation_id__submitted_at
ที่คุณต้องมีหนึ่งคนที่คุณสร้าง: UNIQUE
จำกัดuniq_sid_sat
ยังไม่ทำงานโดยทั่วไป การบำรุงรักษาทั้งคู่ดูเหมือนว่าเป็นการสิ้นเปลืองพื้นที่ดิสก์และประสิทธิภาพการเขียน
ฉันจะเพิ่มNULLS LAST
ไปORDER BY
ในแบบสอบถามเพราะไม่ได้กำหนดไว้submitted_at
NOT NULL
เป็นการดีถ้ามีให้เพิ่มNOT NULL
ข้อ จำกัด ในคอลัมน์submitted_at
ให้ลบดัชนีเพิ่มเติมและลบออกNULLS LAST
จากแบบสอบถาม
หากsubmitted_at
ทำได้ให้NULL
สร้างUNIQUE
ดัชนีนี้เพื่อแทนที่ทั้งดัชนีปัจจุบันและข้อ จำกัด ที่ไม่ซ้ำกัน:
CREATE UNIQUE INDEX station_logs_uni ON station_logs(station_id, submitted_at DESC NULLS LAST);
พิจารณา:
นี่คือสมมติว่าตารางแยกต่างหากที่station
มีหนึ่งแถวต่อเกี่ยวข้องstation_id
(โดยทั่วไปคือ PK) - ซึ่งคุณควรมีวิธีใดวิธีหนึ่ง ถ้าคุณไม่มีมันสร้างมันขึ้นมา อีกครั้งอย่างรวดเร็วด้วยเทคนิค rCTE นี้:
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;
ฉันใช้มันในซอเช่นกัน คุณสามารถใช้แบบสอบถามที่คล้ายกันเพื่อแก้ไขงานของคุณโดยตรงโดยไม่ต้องมีstation
ตาราง - หากคุณไม่สามารถสร้างมันได้
คำแนะนำโดยละเอียดคำอธิบายและทางเลือก:
ปรับดัชนีให้เหมาะสม
คำถามของคุณน่าจะเร็วมาก เฉพาะในกรณีที่คุณยังต้องการเพิ่มประสิทธิภาพการอ่าน ...
มันอาจจะทำให้ความรู้สึกที่จะเพิ่มlevel_sensor
เป็นคอลัมน์สุดท้ายเพื่อจัดทำดัชนีเพื่อให้การสแกนดัชนีเท่านั้นเช่นความเห็น joanolo
คอนดิชั่น:มันทำให้ดัชนีมีขนาดใหญ่ขึ้นซึ่งเพิ่มค่าใช้จ่ายเล็กน้อยสำหรับการสืบค้นทั้งหมดที่ใช้
Pro:หากคุณได้รับการสแกนดัชนีเท่านั้นจริง ๆ แล้วแบบสอบถามในมือไม่จำเป็นต้องไปที่หน้าฮีปเลยซึ่งทำให้เร็วขึ้นเป็นสองเท่า แต่นั่นอาจเป็นกำไรที่ไร้สาระสำหรับการค้นหาที่รวดเร็วมากในขณะนี้
อย่างไรก็ตามฉันไม่คาดหวังว่าจะทำงานให้กับกรณีของคุณ คุณพูดถึง:
... รอบ 20k station_id
แถวต่อวันต่อ
โดยทั่วไปแล้วจะระบุการโหลดการเขียนที่ไม่หยุดหย่อน (1 ต่อstation_id
ทุก 5 วินาที) และคุณมีความสนใจในแถวล่าสุด การสแกนแบบดัชนีเท่านั้นทำงานได้เฉพาะกับเพจฮีพที่สามารถเห็นได้กับธุรกรรมทั้งหมด (บิตในแผนที่การมองเห็นถูกตั้งค่าไว้) คุณจะต้องเรียกใช้การVACUUM
ตั้งค่าที่ก้าวร้าวอย่างมากสำหรับตารางเพื่อให้ทันกับการโหลดการเขียนและมันจะยังไม่ทำงานส่วนใหญ่ หากสมมติฐานของฉันถูกต้องการสแกนเฉพาะดัชนีจะไม่เพิ่มลงlevel_sensor
ในดัชนี
OTOH ถ้าสมมติฐานของฉันค้างไว้และตารางของคุณมีการเจริญเติบโตที่ใหญ่มากเป็นดัชนี Brinอาจช่วย ที่เกี่ยวข้อง:
หรือยิ่งเชี่ยวชาญและมีประสิทธิภาพยิ่งขึ้น: ดัชนีบางส่วนสำหรับเฉพาะส่วนเพิ่มเติมล่าสุดที่จะตัดแถวจำนวนมากที่ไม่เกี่ยวข้องออก:
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';
เลือกเวลาประทับที่คุณรู้ว่าต้องมีแถวที่อายุน้อยกว่า คุณต้องเพิ่มWHERE
เงื่อนไขการจับคู่ให้กับแบบสอบถามทั้งหมดเช่น:
...
WHERE station_id = s.station_id
AND submitted_at > '2017-06-24 00:00'
...
คุณต้องปรับดัชนีและแบบสอบถามเป็นครั้งคราว
คำตอบที่เกี่ยวข้องพร้อมรายละเอียดเพิ่มเติม: