ฉันอยากรู้อยากเห็น และอย่างที่เราทราบกันดีว่าความอยากรู้อยากเห็นมีชื่อเสียงในการฆ่าแมว
ดังนั้นวิธีใดเป็นวิธีที่เร็วที่สุดในการถลกหนังแมว?
สภาพแวดล้อมที่แม่นยำสำหรับการทดสอบนี้:
- PostgreSQL 9.0บน Debian Squeeze พร้อม RAM และการตั้งค่าที่เหมาะสม
- นักเรียน 6.000 คนสมาชิกชมรม 24.000 คน (ข้อมูลคัดลอกจากฐานข้อมูลที่คล้ายกันกับข้อมูลชีวิตจริง)
- ความแตกต่างเล็กน้อยจากสคีมาการตั้งชื่อในคำถาม:
student.id
is student.stud_id
and club.id
is club.club_id
here
- ฉันตั้งชื่อการสืบค้นตามผู้เขียนในชุดข้อความนี้โดยมีดัชนีที่มีสองรายการ
- ฉันเรียกใช้แบบสอบถามทั้งหมดสองสามครั้งเพื่อเติมข้อมูลแคชจากนั้นฉันเลือกสิ่งที่ดีที่สุดจาก 5 รายการด้วยการวิเคราะห์อธิบาย
ดัชนีที่เกี่ยวข้อง (ควรเหมาะสมที่สุด - ตราบใดที่เราขาดความรู้ล่วงหน้าว่าจะสอบถามสโมสรใดบ้าง):
ALTER TABLE student ADD CONSTRAINT student_pkey PRIMARY KEY(stud_id );
ALTER TABLE student_club ADD CONSTRAINT sc_pkey PRIMARY KEY(stud_id, club_id);
ALTER TABLE club ADD CONSTRAINT club_pkey PRIMARY KEY(club_id );
CREATE INDEX sc_club_id_idx ON student_club (club_id);
club_pkey
คำถามส่วนใหญ่ไม่จำเป็นต้องใช้ที่นี่
คีย์หลักใช้ดัชนีเฉพาะโดยอัตโนมัติใน PostgreSQL
ดัชนีสุดท้ายคือการชดเชยข้อบกพร่องที่ทราบกันดีของดัชนีหลายคอลัมน์บน PostgreSQL:
ดัชนี B-tree แบบหลายคอลัมน์สามารถใช้กับเงื่อนไขการสืบค้นที่เกี่ยวข้องกับส่วนย่อยของคอลัมน์ของดัชนี แต่ดัชนีจะมีประสิทธิภาพสูงสุดเมื่อมีข้อ จำกัด ในคอลัมน์นำหน้า (ซ้ายสุด)
ผล:
เวลาทำงานทั้งหมดจาก EXPLAIN ANALYZE
1) Martin 2: 44.594 มิลลิวินาที
SELECT s.stud_id, s.name
FROM student s
JOIN student_club sc USING (stud_id)
WHERE sc.club_id IN (30, 50)
GROUP BY 1,2
HAVING COUNT(*) > 1;
2) เออร์วิน 1: 33.217 มิลลิวินาที
SELECT s.stud_id, s.name
FROM student s
JOIN (
SELECT stud_id
FROM student_club
WHERE club_id IN (30, 50)
GROUP BY 1
HAVING COUNT(*) > 1
) sc USING (stud_id);
3) Martin 1: 31.735 มิลลิวินาที
SELECT s.stud_id, s.name
FROM student s
WHERE student_id IN (
SELECT student_id
FROM student_club
WHERE club_id = 30
INTERSECT
SELECT stud_id
FROM student_club
WHERE club_id = 50);
4) Derek: 2.287 มิลลิวินาที
SELECT s.stud_id, s.name
FROM student s
WHERE s.stud_id IN (SELECT stud_id FROM student_club WHERE club_id = 30)
AND s.stud_id IN (SELECT stud_id FROM student_club WHERE club_id = 50);
5) เออร์วิน 2: 2.181 มิลลิวินาที
SELECT s.stud_id, s.name
FROM student s
WHERE EXISTS (SELECT * FROM student_club
WHERE stud_id = s.stud_id AND club_id = 30)
AND EXISTS (SELECT * FROM student_club
WHERE stud_id = s.stud_id AND club_id = 50);
6) ฌอน: 2.043 มิลลิวินาที
SELECT s.stud_id, s.name
FROM student s
JOIN student_club x ON s.stud_id = x.stud_id
JOIN student_club y ON s.stud_id = y.stud_id
WHERE x.club_id = 30
AND y.club_id = 50;
สามคนสุดท้ายทำหน้าที่เหมือนกัน 4) และ 5) ส่งผลให้เกิดแผนการสืบค้นเดียวกัน
การเพิ่มเติมล่าช้า:
แฟนซี SQL แต่ประสิทธิภาพไม่สามารถทำได้
7) ypercube 1: 148.649 มิลลิวินาที
SELECT s.stud_id, s.name
FROM student AS s
WHERE NOT EXISTS (
SELECT *
FROM club AS c
WHERE c.club_id IN (30, 50)
AND NOT EXISTS (
SELECT *
FROM student_club AS sc
WHERE sc.stud_id = s.stud_id
AND sc.club_id = c.club_id
)
);
8) ypercube 2: 147.497 มิลลิวินาที
SELECT s.stud_id, s.name
FROM student AS s
WHERE NOT EXISTS (
SELECT *
FROM (
SELECT 30 AS club_id
UNION ALL
SELECT 50
) AS c
WHERE NOT EXISTS (
SELECT *
FROM student_club AS sc
WHERE sc.stud_id = s.stud_id
AND sc.club_id = c.club_id
)
);
ตามที่คาดไว้ทั้งสองคนแสดงเกือบจะเหมือนกัน แผนแบบสอบถามส่งผลให้เกิดการสแกนตารางผู้วางแผนไม่พบวิธีใช้ดัชนีที่นี่
9) wildplasser 1: 49.849 มิลลิวินาที
WITH RECURSIVE two AS (
SELECT 1::int AS level
, stud_id
FROM student_club sc1
WHERE sc1.club_id = 30
UNION
SELECT two.level + 1 AS level
, sc2.stud_id
FROM student_club sc2
JOIN two USING (stud_id)
WHERE sc2.club_id = 50
AND two.level = 1
)
SELECT s.stud_id, s.student
FROM student s
JOIN two USING (studid)
WHERE two.level > 1;
แฟนซี SQL ประสิทธิภาพที่ดีสำหรับ CTE แผนการสืบค้นที่แปลกใหม่มาก
อีกครั้งจะน่าสนใจว่า 9.1 จัดการสิ่งนี้อย่างไร ฉันจะอัพเกรดคลัสเตอร์ db ที่ใช้ที่นี่เป็น 9.1 เร็ว ๆ นี้ บางทีฉันอาจจะเรียกใช้ Shebang อีกครั้ง ...
10) wildplasser 2: 36.986 มิลลิวินาที
WITH sc AS (
SELECT stud_id
FROM student_club
WHERE club_id IN (30,50)
GROUP BY stud_id
HAVING COUNT(*) > 1
)
SELECT s.*
FROM student s
JOIN sc USING (stud_id);
แบบสอบถามตัวแปร CTE 2) น่าแปลกที่อาจส่งผลให้แผนการสืบค้นข้อมูลแตกต่างกันเล็กน้อยโดยมีข้อมูลเดียวกัน ฉันพบการสแกนตามลำดับstudent
โดยที่ตัวแปรย่อยใช้ดัชนี
11) ypercube 3: 101.482 มิลลิวินาที
อีกอย่างตอนปลาย @ypercube เป็นเรื่องที่น่าอัศจรรย์มากว่ามีกี่วิธี
SELECT s.stud_id, s.student
FROM student s
JOIN student_club sc USING (stud_id)
WHERE sc.club_id = 10 -- member in 1st club ...
AND NOT EXISTS (
SELECT *
FROM (SELECT 14 AS club_id) AS c -- can't be excluded for missing the 2nd
WHERE NOT EXISTS (
SELECT *
FROM student_club AS d
WHERE d.stud_id = sc.stud_id
AND d.club_id = c.club_id
)
)
12) เออร์วิน 3: 2.377 มิลลิวินาที
@ ypercube's 11) เป็นเพียงแนวทางการย้อนกลับของตัวแปรที่ง่ายกว่านี้ซึ่งยังขาดหายไป ทำความเร็วได้เกือบเท่าแมวอันดับต้น ๆ
SELECT s.*
FROM student s
JOIN student_club x USING (stud_id)
WHERE sc.club_id = 10 -- member in 1st club ...
AND EXISTS ( -- ... and membership in 2nd exists
SELECT *
FROM student_club AS y
WHERE y.stud_id = s.stud_id
AND y.club_id = 14
)
13) เออร์วิน 4: 2.375 มิลลิวินาที
ยากที่จะเชื่อ แต่นี่เป็นอีกหนึ่งตัวแปรใหม่ที่แท้จริง ฉันเห็นว่ามีศักยภาพในการเป็นสมาชิกมากกว่าสองคน แต่ก็ติดอันดับหนึ่งในแมวอันดับต้น ๆ ที่มีเพียงสองตัว
SELECT s.*
FROM student AS s
WHERE EXISTS (
SELECT *
FROM student_club AS x
JOIN student_club AS y USING (stud_id)
WHERE x.stud_id = s.stud_id
AND x.club_id = 14
AND y.club_id = 10
)
จำนวนสมาชิกแบบไดนามิกของสโมสร
กล่าวอีกนัยหนึ่ง: จำนวนตัวกรองที่แตกต่างกัน คำถามนี้ถามสำหรับสมาชิกคลับสองคน แต่กรณีการใช้งานจำนวนมากต้องเตรียมสำหรับจำนวนที่แตกต่างกัน
การอภิปรายโดยละเอียดในคำตอบในภายหลังที่เกี่ยวข้อง: