Điểm chính rất có thể là bạn JOIN
và GROUP
vượt qua mọi thứ chỉ để có được max(created)
. Nhận giá trị này một cách riêng biệt.
Bạn đã đề cập đến tất cả các chỉ mục cần thiết ở đây:trên report_rank.created
và trên các khóa ngoại. Bạn đang làm ổn ở đó. (Nếu bạn quan tâm đến tốt hơn là "được rồi", hãy tiếp tục đọc !)
LEFT JOIN report_site
sẽ bị buộc phải JOIN
đơn giản bởi WHERE
mệnh đề. Tôi đã thay thế một JOIN
đơn giản . Tôi cũng đã đơn giản hóa cú pháp của bạn rất nhiều.
Cập nhật tháng 7 năm 2015 với các truy vấn đơn giản hơn, nhanh hơn và các chức năng thông minh hơn.
Giải pháp cho nhiều hàng
report_rank.created
không phải là duy nhất và bạn muốn tất cả các hàng mới nhất.
Sử dụng hàm window rank()
trong một truy vấn con.
SELECT r.id, r.keyword_id, r.site_id
, r.rank, r.url, r.competition
, r.source, r.country, r.created -- same as "max"
FROM (
SELECT *, rank() OVER (ORDER BY created DESC NULLS LAST) AS rnk
FROM report_rank r
WHERE EXISTS (
SELECT *
FROM report_site s
JOIN report_profile p ON p.site_id = s.id
JOIN crm_client c ON c.id = p.client_id
JOIN auth_user u ON u.id = c.user_id
WHERE s.id = r.site_id
AND u.is_active
AND c.is_deleted = FALSE
)
) sub
WHERE rnk = 1;
Tại sao DESC NULLS LAST
?
Giải pháp cho một hàng
Nếu report_rank.created
là duy nhất hoặc bạn hài lòng với 1 hàng bất kỳ với max(created)
:
SELECT id, keyword_id, site_id
, rank, url, competition
, source, country, created -- same as "max"
FROM report_rank r
WHERE EXISTS (
SELECT 1
FROM report_site s
JOIN report_profile p ON p.site_id = s.id
JOIN crm_client c ON c.id = p.client_id
JOIN auth_user u ON u.id = c.user_id
WHERE s.id = r.site_id
AND u.is_active
AND c.is_deleted = FALSE
)
-- AND r.created > f_report_rank_cap()
ORDER BY r.created DESC NULLS LAST
LIMIT 1;
Sẽ nhanh hơn, vẫn còn. Các tùy chọn khác:
Tốc độ tối đa với chỉ mục từng phần được điều chỉnh động
Bạn có thể đã nhận thấy phần được nhận xét trong truy vấn cuối cùng:
AND r.created > f_report_rank_cap()
Bạn đã đề cập đến 50 mio. hàng, đó là rất nhiều. Đây là một cách để tăng tốc mọi thứ:
- Tạo một
IMMUTABLE
đơn giản hàm trả về dấu thời gian được đảm bảo cũ hơn các hàng quan tâm trong khi càng trẻ càng tốt. - Tạo chỉ mục một phần chỉ trên các hàng trẻ hơn - dựa trên chức năng này.
- Sử dụng
WHERE
điều kiện trong các truy vấn phù hợp với điều kiện chỉ mục. - Tạo một chức năng khác cập nhật các đối tượng này lên hàng mới nhất với DDL động. (Trừ đi một ký quỹ an toàn trong trường hợp (các) hàng mới nhất bị xóa / hủy kích hoạt - nếu điều đó có thể xảy ra)
- Gọi chức năng phụ này vào những thời điểm không hoạt động với tối thiểu hoạt động đồng thời trên mỗi cronjob hoặc theo yêu cầu. Thường xuyên như bạn muốn, không thể gây hại, nó chỉ cần một khóa ngắn độc quyền trên bàn.
Đây là bản trình diễn hoạt động hoàn chỉnh .
@erikcw, bạn sẽ phải kích hoạt phần nhận xét như hướng dẫn bên dưới.
CREATE TABLE report_rank(created timestamp);
INSERT INTO report_rank VALUES ('2011-11-11 11:11'),(now());
-- initial function
CREATE OR REPLACE FUNCTION f_report_rank_cap()
RETURNS timestamp LANGUAGE sql COST 1 IMMUTABLE AS
$y$SELECT timestamp '-infinity'$y$; -- or as high as you can safely bet.
-- initial index; 1st run indexes whole tbl if starting with '-infinity'
CREATE INDEX report_rank_recent_idx ON report_rank (created DESC NULLS LAST)
WHERE created > f_report_rank_cap();
-- function to update function & reindex
CREATE OR REPLACE FUNCTION f_report_rank_set_cap()
RETURNS void AS
$func$
DECLARE
_secure_margin CONSTANT interval := interval '1 day'; -- adjust to your case
_cap timestamp; -- exclude older rows than this from partial index
BEGIN
SELECT max(created) - _secure_margin
FROM report_rank
WHERE created > f_report_rank_cap() + _secure_margin
/* not needed for the demo; @erikcw needs to activate this
AND EXISTS (
SELECT *
FROM report_site s
JOIN report_profile p ON p.site_id = s.id
JOIN crm_client c ON c.id = p.client_id
JOIN auth_user u ON u.id = c.user_id
WHERE s.id = r.site_id
AND u.is_active
AND c.is_deleted = FALSE)
*/
INTO _cap;
IF FOUND THEN
-- recreate function
EXECUTE format('
CREATE OR REPLACE FUNCTION f_report_rank_cap()
RETURNS timestamp LANGUAGE sql IMMUTABLE AS
$y$SELECT %L::timestamp$y$', _cap);
-- reindex
REINDEX INDEX report_rank_recent_idx;
END IF;
END
$func$ LANGUAGE plpgsql;
COMMENT ON FUNCTION f_report_rank_set_cap()
IS 'Dynamically recreate function f_report_rank_cap()
and reindex partial index on report_rank.';
Gọi:
SELECT f_report_rank_set_cap();
Xem:
SELECT f_report_rank_cap();
Bỏ ghi chú mệnh đề AND r.created > f_report_rank_cap()
trong truy vấn ở trên và quan sát sự khác biệt. Xác minh rằng chỉ mục được sử dụng với EXPLAIN ANALYZE
.
Hướng dẫn về đồng thời và REINDEX
: