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

Tìm phạm vi ngày trùng lặp trong PostgreSQL

Câu trả lời hiện được chấp nhận không trả lời câu hỏi. Và nó là sai về nguyên tắc. a BETWEEN x AND y dịch sang:

a >= x AND a <= y

Bao gồm giới hạn trên, trong khi mọi người thường cần loại trừ nó:

a >= x AND a < y

Với ngày tháng bạn có thể dễ dàng điều chỉnh. Đối với năm 2009, hãy sử dụng '2009-12-31' làm giới hạn trên.
Nhưng nó không đơn giản với dấu thời gian cho phép các chữ số phân số. Các phiên bản Postgres hiện đại sử dụng một số nguyên 8 byte bên trong để lưu trữ tối đa 6 giây phân số (độ phân giải µs). Biết được điều này, chúng tôi có thể vẫn làm cho nó hoạt động, nhưng điều đó không trực quan và phụ thuộc vào chi tiết triển khai. Ý tưởng tồi.

Hơn nữa, a BETWEEN x AND y không tìm thấy các phạm vi chồng chéo. Chúng tôi cần:

b >= x AND a < y

Và những người chơi không bao giờ rời bỏ vẫn chưa được xem xét.

Câu trả lời thích hợp

Giả sử năm 2009 , Tôi sẽ diễn đạt lại câu hỏi mà không thay đổi ý nghĩa của nó:

"Tìm tất cả các cầu thủ của một đội nhất định đã tham gia trước năm 2010 và không rời khỏi trước năm 2009."

Truy vấn cơ bản:

SELECT p.*
FROM   team     t
JOIN   contract c USING (name_team) 
JOIN   player   p USING (name_player) 
WHERE  t.name_team = ? 
AND    c.date_join  <  date '2010-01-01'
AND    c.date_leave >= date '2009-01-01';

Nhưng còn nhiều hơn thế nữa:

Nếu tính toàn vẹn của tham chiếu được thực thi với các ràng buộc FK, bảng team bản thân nó chỉ là nhiễu trong truy vấn và có thể bị loại bỏ.

Mặc dù cùng một người chơi có thể rời đi và tham gia lại cùng một đội, chúng tôi cũng cần phải gấp các bản sao có thể có, ví dụ:với DISTINCT .

Và chúng tôi có thể cần cung cấp cho một trường hợp đặc biệt:người chơi không bao giờ rời đi. Giả sử những người chơi đó có NULL trong date_leave .

"Một cầu thủ không được biết là đã ra đi được cho là sẽ chơi cho đội cho đến ngày nay."

Truy vấn tinh chỉnh:

SELECT DISTINCT p.* 
FROM   contract c
JOIN   player   p USING (name_player) 
WHERE  c.name_team = ? 
AND    c.date_join  <  date '2010-01-01'
AND   (c.date_leave >= date '2009-01-01' OR c.date_leave IS NULL);

Quyền ưu tiên của toán tử chống lại chúng tôi, AND liên kết trước OR . Chúng ta cần dấu ngoặc đơn.

Câu trả lời liên quan với DISTINCT được tối ưu hóa (nếu trùng lặp là phổ biến):

  • Nhiều đến nhiều bàn - Hiệu suất kém

Thông thường, tên thể nhân không phải là duy nhất và khóa chính thay thế được sử dụng. Nhưng rõ ràng là name_player là khóa chính của player . Nếu tất cả những gì bạn cần là tên người chơi, chúng tôi không cần bảng player trong truy vấn:

SELECT DISTINCT name_player 
FROM   contract
WHERE  name_team = ? 
AND    date_join  <  date '2010-01-01'
AND   (date_leave >= date '2009-01-01' OR date_leave IS NULL);

SQL OVERLAPS toán tử

Hướng dẫn sử dụng:

OVERLAPS tự động lấy giá trị trước đó của cặp làm giá trị bắt đầu. Mỗi khoảng thời gian được coi là đại diện cho giai đoạn nửa mở cửa start <= time < end , trừ khi startend đều bằng nhau trong trường hợp nào thì nó đại diện cho thời điểm duy nhất đó.

Để chăm sóc NULL tiềm năng giá trị, COALESCE có vẻ dễ dàng nhất:

SELECT DISTINCT name_player 
FROM   contract
WHERE  name_team = ? 
AND    (date_join, COALESCE(date_leave, CURRENT_DATE)) OVERLAPS
       (date '2009-01-01', date '2010-01-01');  -- upper bound excluded

Loại phạm vi có hỗ trợ chỉ mục

Trong Postgres 9.2 trở lên bạn cũng có thể hoạt động với loại phạm vi thực tế :

SELECT DISTINCT name_player 
FROM   contract
WHERE  name_team = ? 
AND    daterange(date_join, date_leave) &&
       daterange '[2009-01-01,2010-01-01)';  -- upper bound excluded

Các loại phạm vi thêm một số chi phí và chiếm nhiều không gian hơn. 2 x date =8 byte; 1 x daterange =14 byte trên đĩa hoặc 17 byte trong RAM. Nhưng kết hợp với toán tử chồng chéo && truy vấn có thể được hỗ trợ bằng chỉ mục GiST.

Ngoài ra, không cần các giá trị NULL trong trường hợp đặc biệt. NULL có nghĩa là "phạm vi mở" trong một loại phạm vi - chính xác là những gì chúng ta cần. Định nghĩa bảng thậm chí không cần phải thay đổi:chúng tôi có thể tạo loại phạm vi ngay lập tức - và hỗ trợ truy vấn với chỉ mục biểu thức phù hợp:

CREATE INDEX mv_stock_dr_idx ON mv_stock USING gist (daterange(date_join, date_leave));

Có liên quan:

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


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Không thể kết nối với máy chủ postgres trong một docker từ một ứng dụng dày đặc

  2. Sử dụng CHÈN với Cơ sở dữ liệu PostgreSQL bằng Python

  3. Mục tiêu thời gian khôi phục Pgbackrest

  4. PostgreSQL tương đương cho TOP n CÓ TIES:GIỚI HẠN với quan hệ?

  5. Trình kích hoạt sự kiện ROLLBACK trong postgresql