PostgreSQL
 sql >> Teknologi Basis Data >  >> RDS >> PostgreSQL

Temukan rentang tanggal yang tumpang tindih di PostgreSQL

Jawaban yang diterima saat ini tidak menjawab pertanyaan. Dan itu salah pada prinsipnya. a BETWEEN x AND y diterjemahkan menjadi:

a >= x AND a <= y

Termasuk batas atas, sementara orang biasanya perlu mengecualikan itu:

a >= x AND a < y

Dengan tanggal Anda dapat dengan mudah menyesuaikan. Untuk tahun 2009 gunakan '2009-12-31' sebagai batas atas.
Tapi tidak sesederhana stempel waktu yang memungkinkan angka pecahan. Versi Postgres modern menggunakan integer 8-byte secara internal untuk menyimpan hingga 6 detik pecahan (resolusi s). Mengetahui hal ini kami bisa masih membuatnya berfungsi, tetapi itu tidak intuitif dan tergantung pada detail implementasi. Ide buruk.

Selain itu, a BETWEEN x AND y tidak menemukan rentang yang tumpang tindih. Kami membutuhkan:

b >= x AND a < y

Dan pemain yang tidak pernah pergi belum dipertimbangkan.

Jawaban yang tepat

Dengan asumsi tahun 2009 , saya akan mengulangi pertanyaannya tanpa mengubah artinya:

"Temukan semua pemain dari tim tertentu yang bergabung sebelum 2010 dan tidak pergi sebelum 2009."

Kueri dasar:

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';

Tapi ada lagi:

Jika integritas referensial diterapkan dengan batasan FK, tabel team itu sendiri hanyalah noise dalam kueri dan dapat dihapus.

Meskipun pemain yang sama dapat keluar dan bergabung kembali dengan tim yang sama, kita juga perlu melipat kemungkinan duplikat, misalnya dengan DISTINCT .

Dan kami mungkin perlu menyediakan kasus khusus:pemain yang tidak pernah pergi. Dengan asumsi para pemain tersebut memiliki NULL di date_leave .

"Seorang pemain yang tidak diketahui telah pergi dianggap bermain untuk tim hingga hari ini."

Kueri yang disempurnakan:

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);

Prioritas operator merugikan kami, AND mengikat sebelum OR . Kami membutuhkan tanda kurung.

Jawaban terkait dengan DISTINCT yang dioptimalkan (jika duplikat sering terjadi):

  • Meja Banyak ke Banyak - Performa buruk

Biasanya, nama orang alami tidak unik dan kunci primer pengganti digunakan. Tapi, jelas, name_player adalah kunci utama player . Jika yang Anda butuhkan hanyalah nama pemain, kami tidak memerlukan tabel player dalam kueri, baik:

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 operator

Panduan:

OVERLAPS secara otomatis mengambil nilai pasangan sebelumnya sebagai permulaan. Setiap periode waktu dianggap mewakili interval setengah terbuka start <= time < end , kecuali start dan end sama dalam hal ini mewakili satu waktu instan.

Untuk menjaga potensi NULL nilai, COALESCE tampaknya paling mudah:

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

Jenis rentang dengan dukungan indeks

Di Postgres 9.2 atau lebih baru Anda juga dapat beroperasi dengan jenis rentang yang sebenarnya :

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

Jenis rentang menambahkan beberapa overhead dan menempati lebih banyak ruang. 2 x date =8 byte; 1 x daterange =14 byte di disk atau 17 byte di RAM. Namun dalam kombinasi dengan operator tumpang tindih && kueri dapat didukung dengan indeks GiST.

Juga, tidak perlu nilai NULL kasus khusus. NULL berarti "rentang terbuka" dalam tipe rentang - persis seperti yang kita butuhkan. Definisi tabel bahkan tidak perlu diubah:kita dapat membuat jenis rentang dengan cepat - dan mendukung kueri dengan indeks ekspresi yang cocok:

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

Terkait:

  • Tabel riwayat stok rata-rata


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Bagaimana cara menggunakan jpa data pegas untuk menanyakan kolom jsonb?

  2. Variasi kinerja kueri SEPERTI PostgreSQL

  3. Bagaimana Acos() Bekerja di PostgreSQL

  4. Postgres:periksa apakah bidang array berisi nilai?

  5. Mengacu pada alias kolom agregat pilih dalam klausa memiliki di Postgres