Bagaimana cara membandingkan tanggal di bidang datetime di Postgresql?

188

Saya telah menghadapi skenario aneh ketika membandingkan antara tanggal di postgresql (versi 9.2.4 di windows). Saya memiliki kolom di tabel saya mengatakan update_date dengan ketik 'timestamp tanpa zona waktu'. Klien dapat mencari bidang ini dengan hanya tanggal (yaitu: 2013-05-03) atau tanggal dengan waktu (yaitu: 2013-05-03 12:20:00). Kolom ini memiliki nilai sebagai cap waktu untuk semua baris saat ini dan memiliki bagian tanggal yang sama (2013-05-03) tetapi perbedaan dalam bagian waktu.

Ketika saya membandingkan kolom ini, saya mendapatkan hasil yang berbeda. Seperti berikut ini:

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

Pertanyaan saya adalah bagaimana saya bisa membuat kueri pertama mungkin untuk mendapatkan hasil, maksud saya mengapa kueri ke-3 berfungsi tetapi bukan yang pertama?

Adakah yang bisa membantu saya dengan ini? Terima kasih sebelumnya.

pengguna2866264
sumber

Jawaban:

278

@Nicolai benar tentang casting dan mengapa kondisinya salah untuk data apa pun. Saya kira Anda lebih suka bentuk pertama karena Anda ingin menghindari manipulasi tanggal pada string input, benar? Anda tidak perlu takut:

SELECT *
FROM table
WHERE update_date >= '2013-05-03'::date
AND update_date < ('2013-05-03'::date + '1 day'::interval);
hanya seseorang
sumber
Apakah sintaks ini ( '2013-05-03'::datedan '1 day'::interval) PostgreSQL spesifik?
Api Beku
5
@ FrostFlame ya itu. sintaks standarnya adalah CAST('2013-05-03' AS DATE) + CAST('1 day' AS INTERVAL)(IIRC). YMMV tentang keberadaan dan perilaku DATEdan INTERVAL.
hanya seseorang
@FrozenFlame benar, jawabannya tidak bekerja tanpa melemparkan string ke tipe tanggal. Satu pemain masih hilang. perlu ::DATEditambahkan ke bagian pertama klausa where
StillLearningToCode
Tidak akan WHERE update_date::date = '2013-05-03' bekerja dengan baik dan mungkin sedikit lebih mudah dibaca?
MikeF
@MikeF OP katakan update_dateadalah timestamp without timezone. saya mengasumsikan indeks pada kolom itu. predikat Anda tidak akan menggunakan indeks itu.
hanya seseorang
46

Ketika Anda membandingkan update_date >= '2013-05-03'postgres, melemparkan nilai ke tipe yang sama untuk membandingkan nilai. Jadi '2013-05-03' Anda dimasukkan ke '2013-05-03 00:00:00'.

Jadi untuk update_date = '2013-05-03 14:45:00' ekspresi Anda adalah:

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

Ini selalu false

Untuk mengatasi masalah ini, berikan pembaruan_date ke date:

select * from table where update_date::date >= '2013-05-03' AND update_date::date <= '2013-05-03' -> Will return result
Nicolai
sumber
1
casting setiap update_datedalam tabel vs casting nilai tunggal dari parameter kueri sangat tidak efisien, dan memastikan server tidak akan dapat meningkatkan indeks pada kolom itu. Saya tergoda untuk -1 ini.
hanya seseorang
3
Ya, saya setuju bahwa casting setiap nilai tidak efisien dan Anda dapat memberikan -1 untuk solusi ini. Tetapi saya menggambarkan alasan masalahnya dan telah memberikan contoh yang menunjukkan masalah tersebut. Sekarang user2866264 tahu mengapa permintaannya tidak mengembalikan baris yang diharapkan dan akan memutuskan solusi apa yang lebih baik untuk kasus uniknya.
Nicolai
@Nicolai: Terima kasih banyak atas jawaban Anda. Ini bekerja dengan mengikuti jawaban Anda. Juga terima kasih atas penjelasannya.
user2866264
1
@Nicolai - Mengingat apa yang Anda katakan tentang Postgres memperluas literal tanggal hingga tengah malam, jika tujuannya adalah menemukan catatan yang ditandai pada satu tanggal (3 Mei), apakah kode ini benar dan lebih efisien: SELECT * FROM my_table WHERE update_date >= '2013-05-03' AND update_date < '2013-05-04'; (Perhatikan penggunaan 4 Mei daripada ke-3 dan dengan KURANG-KETIGA DARI TANDA daripada kurang-atau-sama.)
Basil Bourque
2

Gunakan Konversi tanggal untuk membandingkan dengan tanggal: Coba Ini:

select * from table 
where TO_DATE(to_char(timespanColumn,'YYYY-MM-DD'),'YYYY-MM-DD') = to_timestamp('2018-03-26', 'YYYY-MM-DD')
Yenky Bustamante
sumber