Як порівняти дати в полях дат у Postgresql?


188

Я зіткнувся з дивним сценарієм при порівнянні між датами в postgresql (версія 9.2.4 у Windows). У таблиці в моєму стовпці сказано update_date з типом 'часова мітка без часового поясу'. Клієнт може шукати це поле лише з датою (наприклад, 2013-05-03) або з датою з часом (тобто: 2013-05-03 12:20:00). Цей стовпець має значення часової позначки для всіх поточних рядків і має однакову частину дати (2013-05-03), але різницю в частині часу.

Коли я порівнюю цю колонку, я отримую різні результати. Як і наступні:

select * from table where update_date >= '2013-05-03' AND update_date <= '2013-05-03' -> No results

select * from table where update_date >= '2013-05-03' AND update_date < '2013-05-03' -> No results

select * from table where update_date >= '2013-05-03' AND update_date <= '2013-05-04' -> results found

select * from table where update_date >= '2013-05-03' -> results found

Моє запитання - як я можу зробити перший запит можливим, щоб отримати результати, я маю на увазі, чому третій запит працює, але не перший?

Хтось може мені допомогти в цьому? Заздалегідь спасибі.

Відповіді:


279

@Nicolai вірно стосується кастингу і чому умова неправдива для будь-яких даних. Я думаю, ви віддаєте перевагу першій формі, тому що ви хочете уникнути маніпуляції з датою на вхідному рядку, правильно? вам не потрібно боятися:

SELECT *
FROM table
WHERE update_date >= '2013-05-03'::date
AND update_date < ('2013-05-03'::date + '1 day'::interval);

Чи специфічний цей синтаксис ( '2013-05-03'::dateі '1 day'::interval) PostgreSQL?
Заморожене полум'я

5
@FrozenFlame так. стандартний синтаксис був би CAST('2013-05-03' AS DATE) + CAST('1 day' AS INTERVAL)(IIRC). YMMV про існування та поведінку DATEта INTERVAL.
просто хтось

@FrozenFlame правильна, відповідь не працює без введення рядків до типів дати. Один акторський склад ще відсутній. там має бути ::DATEдодано до першої частини , де становище
StillLearningToCode

Не вдалося б WHERE update_date::date = '2013-05-03' працювати, і, можливо, трохи читабельніше?
MikeF

@MikeF OP сказав, що update_dateбуло timestamp without timezone. Я припустив, що індекс на цьому стовпці. ваш предикат не використовує цей індекс.
просто хтось

46

Якщо ви порівнюєте update_date >= '2013-05-03'postgres, вони відмічають значення одного типу для порівняння значень. Тож ваш "2013-05-03" передавався на "2013-05-03 00:00:00".

Тож для update_date = '2013-05-03 14:45:00' ваш вираз буде таким:

'2013-05-03 14:45:00' >= '2013-05-03 00:00:00' AND '2013-05-03 14:45:00' <= '2013-05-03 00:00:00'

Це завжди false

Щоб вирішити цю проблему, додайте update_date до date:

select * from table where update_date::date >= '2013-05-03' AND update_date::date <= '2013-05-03' -> Will return result

1
кастинг кожного update_dateв таблиці проти виведення єдиного значення параметра запиту надзвичайно неефективний, і гарантує, що сервер не зможе використовувати індекси на цьому стовпчику. Я спокусився на -1 це.
просто хтось

3
Так, я погоджуюся з тим, що кастинг кожного значення неефективний, і ви можете дати -1 для цього рішення. Але я описав причину проблеми і наводив приклад, який демонструє проблему. Тепер користувач2866264 знає, чому його запит не повертає очікуваних рядків, і вирішить, яке саме рішення краще для його унікального випадку.
Миколай

@Nicolai: Дякую за вашу відповідь. Це працює, слідуючи вашої відповіді. Також дякую за пояснення.
користувач2866264

1
@Nicolai - З огляду на те, що ви сказали про Postgres про розширення прямої дати на інсульт півночі, якщо метою є пошук записів, позначених однією датою (3 травня), чи буде цей код правильним та більш ефективним: SELECT * FROM my_table WHERE update_date >= '2013-05-03' AND update_date < '2013-05-04'; (Зверніть увагу на використання 4 травня замість 3-го і з меншим підписом, а не меншим рівним.)
Василь Бурк

9

Використовуйте rangeтип. Якщо користувач введе дату:

select *
from table
where
    update_date
    <@
    tsrange('2013-05-03', '2013-05-03'::date + 1, '[)');

Якщо користувач вводить часові позначки, ::date + 1частина вам не потрібна

http://www.postgresql.org/docs/9.2/static/rangetypes.html

http://www.postgresql.org/docs/9.2/static/functions-range.html


Це стисла і цікава відповідь!
yuriploc

2

Використовуйте конвертувати дату для порівняння з датою: Спробуйте це:

select * from table 
where TO_DATE(to_char(timespanColumn,'YYYY-MM-DD'),'YYYY-MM-DD') = to_timestamp('2018-03-26', 'YYYY-MM-DD')
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.