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

Bảng lịch sử chứng khoán trung bình

Khó khăn đặc biệt của nhiệm vụ này:bạn không thể chỉ chọn các điểm dữ liệu trong phạm vi thời gian của mình mà phải xem xét mới nhất điểm dữ liệu trước phạm vi thời gian và sớm nhất điểm dữ liệu sau phạm vi thời gian bổ sung. Điều này thay đổi đối với mỗi hàng và mỗi điểm dữ liệu có thể tồn tại hoặc không. Yêu cầu một truy vấn phức tạp và khó sử dụng các chỉ mục.

Bạn có thể sử dụng loại phạm vi toán tử (Postgres 9.2+ ) để đơn giản hóa tính toán:

WITH input(a,b) AS (SELECT '2013-01-01'::date  -- your time frame here
                         , '2013-01-15'::date) -- inclusive borders
SELECT store_id, product_id
     , sum(upper(days) - lower(days))                    AS days_in_range
     , round(sum(value * (upper(days) - lower(days)))::numeric
                    / (SELECT b-a+1 FROM input), 2)      AS your_result
     , round(sum(value * (upper(days) - lower(days)))::numeric
                    / sum(upper(days) - lower(days)), 2) AS my_result
FROM (
   SELECT store_id, product_id, value, s.day_range * x.day_range AS days
   FROM  (
      SELECT store_id, product_id, value
           , daterange (day, lead(day, 1, now()::date)
             OVER (PARTITION BY store_id, product_id ORDER BY day)) AS day_range 
      FROM   stock
      ) s
   JOIN  (
      SELECT daterange(a, b+1) AS day_range
      FROM   input
      ) x ON s.day_range && x.day_range
   ) sub
GROUP  BY 1,2
ORDER  BY 1,2;

Lưu ý, tôi sử dụng tên cột day thay vì date . Tôi không bao giờ sử dụng tên kiểu cơ bản làm tên cột.

Trong truy vấn con sub Tôi tìm nạp ngày từ hàng tiếp theo cho từng mục bằng hàm cửa sổ lead() , bằng cách sử dụng tùy chọn tích hợp để cung cấp "hôm nay" làm mặc định khi không có hàng tiếp theo.
Với điều này, tôi tạo một daterange và khớp nó với đầu vào bằng toán tử chồng chéo && , tính toán phạm vi ngày kết quả bằng toán tử giao nhau * .

Tất cả các phạm vi ở đây đều có độc quyền viền trên. Đó là lý do tại sao tôi thêm một ngày vào phạm vi đầu vào. Bằng cách này, chúng ta có thể chỉ cần trừ lower(range) từ upper(range) để biết số ngày.

Tôi giả định rằng "ngày hôm qua" là ngày mới nhất với dữ liệu đáng tin cậy. "Today" vẫn có thể thay đổi trong một ứng dụng đời thực. Do đó, tôi sử dụng "today" (now()::date ) làm đường viền trên dành riêng cho phạm vi mở.

Tôi cung cấp hai kết quả:

  • your_result đồng ý với kết quả hiển thị của bạn.
    Bạn chia cho số ngày trong phạm vi ngày của mình một cách vô điều kiện. Ví dụ:nếu một mặt hàng chỉ được liệt kê cho ngày cuối cùng, bạn sẽ nhận được "mức trung bình" rất thấp (gây hiểu lầm!).

  • my_result tính các số giống nhau hoặc cao hơn.
    Tôi chia cho thực tế số ngày một mặt hàng được liệt kê. Ví dụ:nếu một mặt hàng chỉ được liệt kê cho ngày cuối cùng, tôi trả về giá trị được liệt kê là giá trị trung bình.

Để hiểu rõ sự khác biệt, tôi đã thêm số ngày mặt hàng được liệt kê:days_in_range

SQL Fiddle .

Chỉ mục và hiệu suất

Đối với loại dữ liệu này, các hàng cũ thường không thay đổi. Điều này sẽ trở thành một trường hợp tuyệt vời cho chế độ xem cụ thể hóa :

CREATE MATERIALIZED VIEW mv_stock AS
SELECT store_id, product_id, value
     , daterange (day, lead(day, 1, now()::date) OVER (PARTITION BY store_id, product_id
                                                       ORDER BY day)) AS day_range
FROM   stock;

Sau đó, bạn có thể thêm chỉ mục GiST hỗ trợ toán tử liên quan && :

CREATE INDEX mv_stock_range_idx ON mv_stock USING gist (day_range);

Trường hợp thử nghiệm lớn

Tôi đã chạy thử nghiệm thực tế hơn với 200k hàng. Truy vấn sử dụng MV nhanh hơn khoảng 6 lần, nhanh gấp ~ 10 lần so với truy vấn của @ Joop. Hiệu suất phụ thuộc nhiều vào phân phối dữ liệu. Một MV hữu ích nhất với các bảng lớn và tần suất mục nhập cao. Ngoài ra, nếu bảng có các cột không liên quan đến truy vấn này, thì một MV có thể nhỏ hơn. Một câu hỏi về chi phí so với lợi nhuận.

Tôi đã đặt tất cả các giải pháp đã đăng cho đến nay (và đã điều chỉnh) trong một trò chơi lớn để chơi với:

SQL Fiddle với trường hợp thử nghiệm lớn.
SQL Fiddle chỉ với 40 nghìn hàng - để tránh thời gian chờ trên sqlfiddle.com



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. TẠO NGÔN NGỮ plpython3u - PostgreSQL 9.6

  2. rails động nơi truy vấn sql

  3. Sự khác biệt giữa LIKE và ~ trong Postgres

  4. Truy xuất giá trị đã biết cuối cùng cho mỗi cột của hàng

  5. Cách trả về chèn giá trị kết quả truy vấn bằng trình trợ giúp pg-promise