PostgreSQL
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> PostgreSQL

Nhận con mới nhất cho mỗi phụ huynh từ bảng lớn - truy vấn quá chậm

Điểm chính rất có thể là bạn JOINGROUP 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 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 :



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Chuyển tùy ý nhiều hàng thành cột trong PostgreSQL

  2. Lỗi khi cố gắng kết nối R với PostgreSQL bằng RODBC

  3. Làm cách nào để thay thế một bảng trong Postgres?

  4. không hiểu json_agg trong ngữ cảnh này

  5. Cách nhóm các hàng sau theo giá trị không phải duy nhất