วิธีเปรียบเทียบวันที่ในฟิลด์วันที่และเวลาใน 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

คำถามของฉันคือฉันจะทำให้การสืบค้นแรกเป็นไปได้เพื่อให้ได้ผลลัพธ์ฉันหมายถึงสาเหตุที่การสืบค้นที่ 3 ทำงาน แต่ไม่ใช่คำถามแรก

ใครช่วยฉันได้ไหม ขอบคุณล่วงหน้า.

คำตอบ:


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 หรือไม่
Frozen Flame

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 casts ค่ากับชนิดเดียวกันเพื่อเปรียบเทียบค่า ดังนั้น '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

เพื่อแก้ปัญหานี้นำเสนอการอัปเดตไปที่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

@Nicolai: ขอบคุณมากสำหรับคำตอบของคุณ มันทำงานโดยทำตามคำตอบของคุณ ขอบคุณสำหรับคำอธิบาย
user2866264

1
@Nicolai - จากสิ่งที่คุณพูดเกี่ยวกับ Postgres การขยายวันที่ตามตัวอักษรถึงจังหวะเที่ยงคืนหากเป้าหมายคือการค้นหาบันทึกที่ทำเครื่องหมายในวันเดียว (3 พฤษภาคม) รหัสนี้จะถูกต้องและมีประสิทธิภาพมากขึ้น: SELECT * FROM my_table WHERE update_date >= '2013-05-03' AND update_date < '2013-05-04'; (หมายเหตุการใช้ 4 พฤษภาคม มากกว่า 3 และด้วยการเข้าสู่ระบบน้อยกว่าน้อยกว่าหรือเท่ากับ)
Basil Bourque

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')
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.