Nó rất nhiều phụ thuộc vào hoàn cảnh và yêu cầu chính xác. Hãy xem xét nhận xét của tôi cho câu hỏi .
Giải pháp đơn giản
Với DISTINCT ON
trong Postgres:
SELECT DISTINCT ON (i.good, i.the_date)
i.the_date, p.the_date AS pricing_date, i.good, p.price
FROM inventory i
LEFT JOIN price p ON i.good = p.good AND i.the_date >= p.the_date
ORDER BY i.good, i.the_date, p.the_date DESC;
Kết quả đặt hàng.
Hoặc với NOT EXISTS
SQL tiêu chuẩn (hoạt động với mọi RDBMS mà tôi biết):
SELECT i.the_date, p.the_date AS pricing_date, i.good, i.quantity, p.price
FROM inventory i
LEFT JOIN price p ON p.good = i.good AND p.the_date <= i.the_date
WHERE NOT EXISTS (
SELECT 1 FROM price p1
WHERE p1.good = p.good
AND p1.the_date <= i.the_date
AND p1.the_date > p.the_date
);
Kết quả tương tự, nhưng với thứ tự sắp xếp tùy ý - trừ khi bạn thêm ORDER BY
.
Tùy thuộc vào phân phối dữ liệu, yêu cầu chính xác và chỉ số, một trong những điều này có thể nhanh hơn.
Nói chung, DISTINCT ON
là người chiến thắng và bạn nhận được một kết quả được sắp xếp trên đầu trang của nó. Nhưng đối với một số trường hợp, các kỹ thuật truy vấn khác nhanh hơn (nhiều). Xem bên dưới.
Các giải pháp với các truy vấn con để tính giá trị tối đa / tối thiểu thường chậm hơn. Các biến thể với CTE thường chậm hơn.
Chế độ xem đơn giản (như được đề xuất bởi một câu trả lời khác) không giúp ích gì cho hiệu suất trong Postgres.
Câu đố SQL.
Giải pháp đúng
Chuỗi và đối chiếu
Trước hết, bạn phải chịu một bố trí bảng phụ tối ưu. Nó có vẻ tầm thường, nhưng bình thường hóa lược đồ của bạn có thể đi một chặng đường dài.
Sắp xếp theo loại ký tự ( text
,, varchar
...) phải được thực hiện theo ngôn ngữ địa phương - cụ thể là THU THẬP . Nhiều khả năng DB của bạn sử dụng một số bộ quy tắc cục bộ (như, trong trường hợp của tôi de_AT.UTF-8
:). Tìm hiểu với:
SHOW lc_collate;
Điều này làm cho việc sắp xếp và tìm kiếm chỉ mục chậm hơn . Chuỗi của bạn càng dài (tên hàng hóa) càng tệ. Nếu bạn không thực sự quan tâm đến các quy tắc đối chiếu trong đầu ra của mình (hoặc thứ tự sắp xếp nào cả), điều này có thể nhanh hơn nếu bạn thêm COLLATE "C"
:
SELECT DISTINCT ON (i.good COLLATE "C", i.the_date)
i.the_date, p.the_date AS pricing_date, i.good, p.price
FROM inventory i
LEFT JOIN price p ON i.good = p.good AND i.the_date >= p.the_date
ORDER BY i.good COLLATE "C", i.the_date, p.the_date DESC;
Lưu ý cách tôi thêm đối chiếu ở hai nơi.
Hai lần nhanh nhất trong thử nghiệm của tôi với mỗi hàng 20 nghìn và các tên rất cơ bản ('good123').
Mục lục
Nếu truy vấn của bạn được cho là sử dụng một chỉ mục, các cột có dữ liệu ký tự phải sử dụng đối chiếu phù hợp ( good
trong ví dụ):
CREATE INDEX inventory_good_date_desc_collate_c_idx
ON price(good COLLATE "C", the_date DESC);
Hãy chắc chắn đọc hai chương cuối của câu trả lời liên quan này trên SO:
Bạn thậm chí có thể có nhiều chỉ mục với các đối chiếu khác nhau trên cùng một cột - nếu bạn cũng cần hàng hóa được sắp xếp theo đối chiếu khác (hoặc mặc định) trong các truy vấn khác.
Bình thường hóa
Các chuỗi dự phòng (tên của hàng hóa) cũng làm mờ các bảng và chỉ mục của bạn, điều này làm cho mọi thứ thậm chí chậm hơn. Với cách bố trí bảng thích hợp, bạn có thể tránh được hầu hết các vấn đề để bắt đầu. Có thể trông như thế này:
CREATE TABLE good (
good_id serial PRIMARY KEY
, good text NOT NULL
);
CREATE TABLE inventory (
good_id int REFERENCES good (good_id)
, the_date date NOT NULL
, quantity int NOT NULL
, PRIMARY KEY(good_id, the_date)
);
CREATE TABLE price (
good_id int REFERENCES good (good_id)
, the_date date NOT NULL
, price numeric NOT NULL
, PRIMARY KEY(good_id, the_date));
Các khóa chính tự động cung cấp (gần như) tất cả các chỉ số chúng ta cần.
Tùy thuộc vào thiếu chi tiết, một chỉ số multicolumn trên price
với thứ tự giảm dần trên cột thứ hai có thể cải thiện hiệu suất:
CREATE INDEX price_good_date_desc_idx ON price(good, the_date DESC);
Một lần nữa, đối chiếu phải phù hợp với truy vấn của bạn (xem bên trên).
Trong Postgres 9.2 trở lên, "các chỉ số che phủ" cho các lần quét chỉ mục có thể giúp nhiều hơn - đặc biệt là nếu các bảng của bạn giữ các cột bổ sung, làm cho bảng lớn hơn đáng kể so với chỉ mục che phủ.
Các truy vấn kết quả này nhanh hơn nhiều:
KHÔNG TỒN TẠI
SELECT i.the_date, p.the_date AS pricing_date, g.good, i.quantity, p.price
FROM inventory i
JOIN good g USING (good_id)
LEFT JOIN price p ON p.good_id = i.good_id AND p.the_date <= i.the_date
AND NOT EXISTS (
SELECT 1 FROM price p1
WHERE p1.good_id = p.good_id
AND p1.the_date <= i.the_date
AND p1.the_date > p.the_date
);
KHOẢNG CÁCH TRÊN
SELECT DISTINCT ON (i.the_date)
i.the_date, p.the_date AS pricing_date, g.good, i.quantity, p.price
FROM inventory i
JOIN good g USING (good_id)
LEFT JOIN price p ON p.good_id = i.good_id AND p.the_date <= i.the_date
ORDER BY i.the_date, p.the_date DESC;
Câu đố SQL.
Giải pháp nhanh hơn
Nếu điều đó vẫn chưa đủ nhanh, có thể có giải pháp nhanh hơn.
JOIN LATERAL
Truy vấn CTE / / tương quan
Đặc biệt đối với phân phối dữ liệu với nhiều mức giá cho mỗi hàng hóa :
Quan điểm cụ thể hóa
Nếu bạn cần chạy cái này thường xuyên và nhanh chóng, tôi khuyên bạn nên tạo một cái nhìn cụ thể hóa. Tôi nghĩ thật an toàn khi giả định rằng giá cả và hàng tồn kho cho những ngày qua hiếm khi thay đổi. Tính kết quả một lần và lưu trữ ảnh chụp nhanh dưới dạng xem cụ thể.
Postgres 9.3+ có hỗ trợ tự động cho các khung nhìn cụ thể hóa. Bạn có thể dễ dàng thực hiện một phiên bản cơ bản trong các phiên bản cũ hơn.