Intereting Posts
JQuery / AJAX: загрузка внешних DIV с использованием динамического содержимого Должен ли я mysql_real_escape_string ввести пароль в регистрационную форму? Как я могу поместить свою форму в другую форму и использовать одну кнопку отправки? Как вставить сразу несколько строк в базу данных mysql с помощью подготовленных операторов? PHP: Загрузите входящую электронную почту из POP3 или IMAP, проанализируйте ее и пометьте как чтение / удаление на сервере Сортировка многомерного массива? Неработающий отчет обновления PDO не работает Проверка переключателей в php Paypal IPN, возвращающий HTTP / 1.1 200 OK PHP: эквивалент функции MySQL SUBSTRING_INDEX? Разрешение отклонено при открытии файла с помощью file_get_contents; webbrowser может открыть его без проблем Вызов функции-члена get () при ошибке null Невозможно разобрать CDATA с SimpleXML Постоянный локальный доменный сокет в php Используйте $ _POST для получения входных значений на одной странице

Найти перекрывающиеся диапазоны дат в PostgreSQL

Это верно?

SELECT * FROM contract JOIN team USING (name_team) JOIN player USING(name_player) WHERE name_team = ? AND DATE_PART('YEAR',date_join)>= ? AND DATE_PART('YEAR',date_leave)<= ? 

У моего настольного contract есть имя игрока, название команды и даты, когда он присоединился и покинул клуб.
Я хочу создать функцию, в которой перечислены все игроки, которые были в команде в определенные годы.
Вышеприведенный запрос, похоже, не работает …

Почему бы не использовать между элементами даты:

WHERE datefield BETWEEN '2009-10-10 00:00:00' AND '2009-10-11 00:00:00'

или что-то типа того?

Принятый ответ не годится

Ответ, предлагающий a BETWEEN x AND y получил много оборотов и был принят уже около 2 лет. Но это не только не отвечает на вопрос, но и в принципе неверно .

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

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

Если ссылочная целостность не нарушена, команда таблицы сама по себе является шумом в этом запросе и может быть удалена.

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

И нам может потребоваться предоставить специальный случай: игроки, которые никогда не уходили. Предположим, что у этих игроков есть date_leave IS NULL .

«Предполагается, что игрок, который, как известно, не ушел, будет играть за команду по сей день».

Приобретение этого оптимизированного запроса:

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

Связанный ответ с оптимизированным DISTINCT (если дубликаты являются общими):

  • Многие для многих – производительность плохая

Как правило, имена не уникальны, а первичный ключ суррогата используется для физических лиц. Но, очевидно, name_player является основным ключом player . Если вам нужны только имена игроков, нам не нужен player таблицы в запросе:

 SELECT DISTINCT c.name_player FROM contract c 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); 

Мы могли бы также использовать оператор SQL OVERLAPS :

OVERLAPS автоматически берет предыдущее значение пары в качестве начала. Каждый период времени считается периодом полуоткрытого интервала start <= time < end , если только start и end не равны, и в этом случае он представляет одноразовый момент.

Но нам нужно позаботиться о потенциальных значениях NULL . Самый простой с COALESCE :

 SELECT DISTINCT c.name_player FROM contract c WHERE t.name_team = ? AND (c.date_join, COALESCE(c.date_leave, current_date)) OVERLAPS ('2009-01-01'::date, '2009-12-31'::date) 

В Postgres 9.2 или новее вы также можете работать с реальными типами диапазонов в сочетании с оператором перекрытия && который может поддерживаться индексом GiST. Пример:

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