PostgreSQL
 sql >> база данни >  >> RDS >> PostgreSQL

Намерете припокриващи се периоди от време в PostgreSQL

Приетият в момента отговор не отговаря на въпроса. И е грешно по принцип. a BETWEEN x AND y превежда се на:

a >= x AND a <= y

Включително горната граница, докато хората обикновено трябва да изключват то:

a >= x AND a < y

С дати можете лесно да регулирате. За 2009 г. използвайте „2009-12-31“ като горна граница.
Но не е толкова просто с клеймите за време които позволяват дробни цифри. Съвременните версии на Postgres използват вътрешно 8-байтово цяло число, за да съхраняват до 6 части секунди (резолюция µs). Знаейки това, можем все още го накара да работи, но това не е интуитивно и зависи от детайлите на изпълнението. Лоша идея.

Освен това, a BETWEEN x AND y не намира припокриващи се диапазони. Нуждаем се от:

b >= x AND a < y

И играчи, които никога не са напускали все още не се разглеждат.

Правилен отговор

Ако приемем годината 2009 , ще префразирам въпроса, без да променям значението му:

„Намерете всички играчи от даден отбор, които са се присъединили преди 2010 г. и не са напуснали преди 2009 г.“

Основна заявка:

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

Но има още:

Ако референтната цялост е наложена с FK ограничения, таблицата team сама по себе си е просто шум в заявката и може да бъде премахната.

Въпреки че един и същ играч може да напусне и да се присъедини отново към същия отбор, ние също трябва да хвърлим възможни дубликати, например с DISTINCT .

И ние можем трябва да се предвиди специален случай:играчи, които никога не са напускали. Ако приемем, че тези играчи имат NULL в date_leave .

„Предполага се, че играч, за който не е известно, че е напуснал, играе за отбора и до днес.“

Прецизирана заявка:

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

Приоритетът на оператора работи срещу нас, AND се свързва преди OR . Нуждаем се от скоби.

Свързан отговор с оптимизиран DISTINCT (ако дубликатите са често срещани):

  • Много към много таблица – производителността е лоша

Обикновено имена на физически лица не са уникални и се използва сурогатен първичен ключ. Но, очевидно, name_player е първичният ключ на player . Ако всичко, от което се нуждаете, са имена на играчи, нямаме нужда от таблицата player в заявката или:

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 оператор

Ръководството:

OVERLAPS автоматично приема по-ранната стойност на двойката като начало. Всеки период от време се счита за представляващ полуотворен интервал start <= time < end , освен ако start и end са равни, в който случай представлява този единичен момент от време.

За да се погрижим за потенциалния NULL стойности, COALESCE изглежда най-лесно:

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

Тип диапазон с поддръжка на индекс

В Postgres 9.2 или по-нова версия можете също да работите с действителни типове обхват :

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

Типовете диапазони добавят малко режийни разходи и заемат повече място. 2 x date =8 байта; 1 x daterange =14 байта на диск или 17 байта в RAM. Но в комбинация с оператора за припокриване && заявката може да се поддържа с GiST индекс.

Освен това няма нужда от специални NULL стойности. NULL означава "отворен диапазон" в тип диапазон - точно това, от което се нуждаем. Дефиницията на таблицата дори не трябва да се променя:можем да създадем типа на диапазона в движение - и да поддържаме заявката със съвпадащ индекс на израз:

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

Свързано:

  • Средна таблица на фондовата история


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Използване на курсори за страници в PostgreSQL

  2. Как мога да съпоставя последните две думи в изречение в PostgreSQL?

  3. Postgresql генерира_серия от месеци

  4. Дизайн на база данни за създаване на таблици в движение

  5. Споделяне на вашите данни с PostgreSQL 11