Điểm chuẩn
Kiểm tra hầu hết các thí sinh thú vị với Postgres 9.4 và 9,5 với một bảng nửa đường thực tế của 200k hàng trong purchases
và 10k biệtcustomer_id
( trung bình. 20 hàng mỗi khách hàng ).
Đối với Postgres 9.5, tôi đã chạy thử nghiệm lần 2 với 86446 khách hàng khác biệt. Xem bên dưới ( trung bình 2,3 hàng cho mỗi khách hàng ).
Thiết lập
Bàn chính
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
);
Tôi sử dụng một serial
(ràng buộc PK được thêm vào bên dưới) và một số nguyên customer_id
vì đó là một thiết lập điển hình hơn. Cũng được thêm vào some_column
để bù cho các cột thường hơn.
Dữ liệu giả, PK, chỉ mục - một bảng thông thường cũng có một số bộ dữ liệu chết:
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
bảng - cho truy vấn cấp trên
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;
Trong thử nghiệm thứ hai của tôi cho 9.5 tôi đã sử dụng cùng một thiết lập, nhưng với random() * 100000
để tạo ra customer_id
chỉ nhận được vài hàng mỗi customer_id
.
Kích thước đối tượng cho bảng purchases
Tạo với truy vấn này .
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 | |
Truy vấn
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()
trong truy vấn con (tối ưu hóa của tôi)
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 với LATERAL
truy vấn con ( xem tại đây )
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
bảng với LATERAL
( xem tại đây )
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;
SELECT (array_agg(id ORDER BY total DESC))[1] AS id
, customer_id
, max(total) AS total
FROM purchases
GROUP BY customer_id;
Các kết quả
Thời gian thực hiện cho các truy vấn trên với EXPLAIN ANALYZE
(và tất cả các tùy chọn tắt ), tốt nhất trong 5 lần chạy .
Tất cả các truy vấn đã sử dụng một Index Chỉ Quét trên purchases2_3c_idx
(trong số những bước khác). Một số trong số họ chỉ cho kích thước nhỏ hơn của chỉ số, những người khác hiệu quả hơn.
A. Postgres 9,4 với 200k hàng và ~ 20 mỗi 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. Tương tự với 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. Giống như B., nhưng với ~ 2,3 hàng mỗi 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
Điểm chuẩn liên quan
Dưới đây là một thử nghiệm mới bằng cách "ogr" thử nghiệm với 10 triệu hàng và 60k "khách hàng" duy nhất trên Postgres 11,5 (hiện tại kể từ tháng 9 năm 2019). Kết quả vẫn phù hợp với những gì chúng ta đã thấy cho đến nay:
Điểm chuẩn gốc (lỗi thời) từ năm 2011
Tôi đã chạy ba thử nghiệm với PostgreSQL 9.1 trên bảng thực tế gồm 65579 hàng và chỉ mục btree cột đơn trên mỗi ba cột có liên quan và mất thời gian thực hiện tốt nhất trong 5 lần chạy.
So sánh truy vấn đầu tiên của @OMGPonies ( A
) với giải pháp trênDISTINCT ON
( B
):
Chọn toàn bộ bảng, kết quả trong 5958 hàng trong trường hợp này.
A: 567.218 ms
B: 386.673 ms
Sử dụng điều kiện WHERE customer BETWEEN x AND y
dẫn đến 1000 hàng.
A: 249.136 ms
B: 55.111 ms
Chọn một khách hàng duy nhất với WHERE customer = x
.
A: 0.143 ms
B: 0.072 ms
Kiểm tra tương tự lặp lại với chỉ số được mô tả trong câu trả lời khác
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)
?