เกณฑ์มาตรฐาน
การทดสอบผู้สมัครที่น่าสนใจมากที่สุดกับ Postgres 9.4และ9.5พร้อมโต๊ะจริงครึ่งหนึ่งของ200k แถวในpurchases
และ10k ที่แตกต่างกันcustomer_id
( เฉลี่ย. 20 แถวต่อลูกค้า )
สำหรับ Postgres 9.5 ฉันทำการทดสอบครั้งที่ 2 กับลูกค้าที่แตกต่าง 86446 ราย ดูด้านล่าง ( เฉลี่ย 2.3 แถวต่อลูกค้า )
ติดตั้ง
ตารางหลัก
CREATE TABLE purchases (
id serial
, customer_id int -- REFERENCES customer
, total int -- could be amount of money in Cent
, some_column text -- to make the row bigger, more realistic
);
ฉันใช้serial
(เพิ่มข้อ จำกัด PK ด้านล่าง) และจำนวนเต็มcustomer_id
เนื่องจากเป็นการตั้งค่าทั่วไป เพิ่มsome_column
ไปยังทำขึ้นสำหรับคอลัมน์เพิ่มเติมโดยทั่วไป
ข้อมูลจำลอง, PK, ดัชนี - ตารางทั่วไปยังมีสิ่งอันดับที่ตายแล้ว:
INSERT INTO purchases (customer_id, total, some_column) -- insert 200k rows
SELECT (random() * 10000)::int AS customer_id -- 10k customers
, (random() * random() * 100000)::int AS total
, 'note: ' || repeat('x', (random()^2 * random() * random() * 500)::int)
FROM generate_series(1,200000) g;
ALTER TABLE purchases ADD CONSTRAINT purchases_id_pkey PRIMARY KEY (id);
DELETE FROM purchases WHERE random() > 0.9; -- some dead rows
INSERT INTO purchases (customer_id, total, some_column)
SELECT (random() * 10000)::int AS customer_id -- 10k customers
, (random() * random() * 100000)::int AS total
, 'note: ' || repeat('x', (random()^2 * random() * random() * 500)::int)
FROM generate_series(1,20000) g; -- add 20k to make it ~ 200k
CREATE INDEX purchases_3c_idx ON purchases (customer_id, total DESC, id);
VACUUM ANALYZE purchases;
customer
ตาราง - สำหรับการสืบค้นที่เหนือกว่า
CREATE TABLE customer AS
SELECT customer_id, 'customer_' || customer_id AS customer
FROM purchases
GROUP BY 1
ORDER BY 1;
ALTER TABLE customer ADD CONSTRAINT customer_customer_id_pkey PRIMARY KEY (customer_id);
VACUUM ANALYZE customer;
ในของฉันทดสอบครั้งที่สอง 9.5 ผมใช้การตั้งค่าเดียวกัน แต่มีrandom() * 100000
การสร้างที่จะได้รับเพียงไม่กี่แถวต่อcustomer_id
customer_id
ขนาดวัตถุสำหรับตาราง purchases
สร้างด้วยแบบสอบถามนี้
what | bytes/ct | bytes_pretty | bytes_per_row
-----------------------------------+----------+--------------+---------------
core_relation_size | 20496384 | 20 MB | 102
visibility_map | 0 | 0 bytes | 0
free_space_map | 24576 | 24 kB | 0
table_size_incl_toast | 20529152 | 20 MB | 102
indexes_size | 10977280 | 10 MB | 54
total_size_incl_toast_and_indexes | 31506432 | 30 MB | 157
live_rows_in_text_representation | 13729802 | 13 MB | 68
------------------------------ | | |
row_count | 200045 | |
live_tuples | 200045 | |
dead_tuples | 19955 | |
แบบสอบถาม
WITH cte AS (
SELECT id, customer_id, total
, row_number() OVER(PARTITION BY customer_id ORDER BY total DESC) AS rn
FROM purchases
)
SELECT id, customer_id, total
FROM cte
WHERE rn = 1;
2. row_number()
ในแบบสอบถามย่อย (การเพิ่มประสิทธิภาพของฉัน)
SELECT id, customer_id, total
FROM (
SELECT id, customer_id, total
, row_number() OVER(PARTITION BY customer_id ORDER BY total DESC) AS rn
FROM purchases
) sub
WHERE rn = 1;
SELECT DISTINCT ON (customer_id)
id, customer_id, total
FROM purchases
ORDER BY customer_id, total DESC, id;
4. rCTE พร้อมLATERAL
แบบสอบถามย่อย ( ดูที่นี่ )
WITH RECURSIVE cte AS (
( -- parentheses required
SELECT id, customer_id, total
FROM purchases
ORDER BY customer_id, total DESC
LIMIT 1
)
UNION ALL
SELECT u.*
FROM cte c
, LATERAL (
SELECT id, customer_id, total
FROM purchases
WHERE customer_id > c.customer_id -- lateral reference
ORDER BY customer_id, total DESC
LIMIT 1
) u
)
SELECT id, customer_id, total
FROM cte
ORDER BY customer_id;
5. customer
ตารางที่มีLATERAL
( ดูที่นี่ )
SELECT l.*
FROM customer c
, LATERAL (
SELECT id, customer_id, total
FROM purchases
WHERE customer_id = c.customer_id -- lateral reference
ORDER BY total DESC
LIMIT 1
) l;
6. array_agg()
กับORDER BY
( ดูคำตอบอื่น ๆ )
SELECT (array_agg(id ORDER BY total DESC))[1] AS id
, customer_id
, max(total) AS total
FROM purchases
GROUP BY customer_id;
ผล
เวลาดำเนินการสำหรับการค้นหาข้างต้นด้วยEXPLAIN ANALYZE
(และตัวเลือกทั้งหมดออก ) ที่ดีที่สุดของ 5 ลาน
ทุกคำสั่งที่ใช้เฉพาะดัชนีสแกนบนpurchases2_3c_idx
(ในขั้นตอนอื่น ๆ ) บางส่วนใช้สำหรับดัชนีที่มีขนาดเล็กลง แต่บางรายการก็มีประสิทธิภาพมากกว่า
A. Postgres 9.4 กับ 200k แถวและ ~ 20 ต่อ customer_id
1. 273.274 ms
2. 194.572 ms
3. 111.067 ms
4. 92.922 ms
5. 37.679 ms -- winner
6. 189.495 ms
B. เช่นเดียวกับ Postgres 9.5
1. 288.006 ms
2. 223.032 ms
3. 107.074 ms
4. 78.032 ms
5. 33.944 ms -- winner
6. 211.540 ms
C. เหมือนกับ B. แต่มี ~ 2.3 แถวต่อ customer_id
1. 381.573 ms
2. 311.976 ms
3. 124.074 ms -- winner
4. 710.631 ms
5. 311.976 ms
6. 421.679 ms
มาตรฐานที่เกี่ยวข้อง
นี่คือการทดสอบ "ogr" ใหม่ด้วยแถว 10M และลูกค้า "60" ที่ไม่ซ้ำ 60kในPostgres 11.5 (ปัจจุบัน ณ วันที่ 25 ก.ย. 2019) ผลลัพธ์ยังคงสอดคล้องกับสิ่งที่เราได้เห็น:
มาตรฐานดั้งเดิม (ล้าสมัย) จาก 2011
ฉันรันการทดสอบสามรายการกับ PostgreSQL 9.1บนตารางชีวิตจริงของ 65579 แถวและดัชนี btree แบบคอลัมน์เดียวในแต่ละคอลัมน์สามคอลัมน์ที่เกี่ยวข้องและใช้เวลาดำเนินการที่ดีที่สุด5 ครั้ง
การเปรียบเทียบข้อความค้นหาแรกของ @OMGPonies ( A
) กับโซลูชันด้านบนDISTINCT ON
( B
):
เลือกทั้งตารางผลลัพธ์ใน 5958 แถวในกรณีนี้
A: 567.218 ms
B: 386.673 ms
ใช้เงื่อนไขWHERE customer BETWEEN x AND y
ส่งผลให้ 1,000 แถว
A: 249.136 ms
B: 55.111 ms
WHERE customer = x
เลือกลูกค้าเดียวกับ
A: 0.143 ms
B: 0.072 ms
การทดสอบเดียวกันซ้ำกับดัชนีที่อธิบายในคำตอบอื่น ๆ
CREATE INDEX purchases_3c_idx ON purchases (customer, total DESC, id);
1A: 277.953 ms
1B: 193.547 ms
2A: 249.796 ms -- special index not used
2B: 28.679 ms
3A: 0.120 ms
3B: 0.048 ms
MAX(total)
?