ไม่รวม SQL“ ระหว่าง”


143

ฉันมีคำถามดังนี้:

SELECT * FROM Cases WHERE created_at BETWEEN '2013-05-01' AND '2013-05-01'

แต่สิ่งนี้ไม่ให้ผลลัพธ์แม้ว่าจะมีข้อมูลในวันที่ 1 ก็ตาม

created_atดูเหมือนว่า2013-05-01 22:25:19ฉันสงสัยว่ามันจะเกี่ยวข้องกับเวลา? จะแก้ไขได้อย่างไร?

จะใช้ได้ดีถ้าฉันทำช่วงวันที่ที่ใหญ่ขึ้น แต่ควร (รวม) กับวันที่เดียวด้วย


25
เลขระหว่าง 1 ถึง 1 มีกี่ตัว? 1.5 ควรอยู่ระหว่าง 1 ถึง 1 หรือไม่? อย่าใช้ระหว่างช่วงวันที่ / เวลา เคย. และระมัดระวังในการประเมินว่า "ใช้ได้ผลดี" - คุณได้ตรวจสอบผลลัพธ์อย่างใกล้ชิดตั้งแต่วันสุดท้ายในช่วงดังกล่าวหรือไม่ คุณจะรวมแถวทั้งหมดก็ต่อเมื่อแถวนั้นไม่ได้เชื่อมโยงกับแถวนั้นเลย
Aaron Bertrand

1
อัปเดต URL สำหรับ Aaron: sqlblog.org/2011/10/19/…
JayRizzo

คำตอบ:


307

มันเป็นแบบครบวงจร คุณกำลังเปรียบเทียบวันที่กับวันที่ วันที่สองจะถูกตีความว่าเป็นเที่ยงคืนเมื่อวันที่เริ่มต้น

วิธีหนึ่งในการแก้ไขปัญหานี้คือ:

SELECT *
FROM Cases
WHERE cast(created_at as date) BETWEEN '2013-05-01' AND '2013-05-01'

อีกวิธีหนึ่งในการแก้ไขคือการเปรียบเทียบไบนารีอย่างชัดเจน

SELECT *
FROM Cases
WHERE created_at >= '2013-05-01' AND created_at < '2013-05-02'

Aaron Bertrand มีรายการบล็อกที่ยาวเกี่ยวกับวันที่ ( ที่นี่ ) ซึ่งเขาพูดถึงเรื่องนี้และปัญหาวันที่อื่น ๆ


16
เพื่อความสมบูรณ์ของคำตอบที่ดีนี้ฉันขอแนะนำให้ใช้dateaddเพื่อรับในวันถัดไป
Tim Lehner

7
@TimLehner เพื่อเป็นคำตอบที่ดีฉันจะใช้รูปแบบ ISO-8601 ของ '20130501' สำหรับผู้ที่ไม่ได้อยู่ในสหรัฐอเมริกาที่มี dmy รูปแบบวันที่คุณจะได้รับสิ่งนี้:set dateformat dmy;select month(cast('2013-05-01' as datetime)); =1
RichardTheKiwi

2
@RichardTheKiwi - ฉันเชื่อว่าบิตที่ชัดเจนใช้ช่วงเวลาปิดที่ปลายด้านหนึ่ง ( >=) และเปิดที่อีกด้านหนึ่ง ( <) รวมกับการตรวจสอบหนึ่งวันเกินวันที่สิ้นสุดที่ระบุ
HABO

3
@scottb โดยส่วนตัวแล้วฉันรู้สึกว่ามันน่ารำคาญที่ต้องแปลงเป็นวันที่ทุกครั้งที่ฉันต้องการแสดงส่งออกนำเข้าหรือเขียนคลาสพร้อมวันที่และเวลา ฉันคิดว่า SQL Server มีฟังก์ชันในตัวมากมายเพื่อจัดการและเปรียบเทียบวันที่ตามวัตถุประสงค์ส่วนใหญ่
Tim Lehner

11
คุณไม่ควรแคสต์คอลัมน์ในwhereประโยคเพราะจะสูญเสียการจัดทำดัชนีใด ๆ ที่มีอยู่ มันเป็นรูปแบบที่แย่จริงๆ
Buzinas

56

มันได้รับการสันนิษฐานว่าการอ้างอิงวันที่สองในBETWEENไวยากรณ์ถือว่าน่าอัศจรรย์เป็น "ตอนท้ายของวัน" แต่นี้ไม่เป็นความจริง

เช่นนี้คาดว่า:

เลือก * จากกรณี 
WHERE created_at ระหว่างจุดเริ่มต้นของ '2013-05-01' และตอนท้ายของ '2013-05-01'

แต่สิ่งที่เกิดขึ้นจริงคือ:

เลือก * จากกรณี 
WHERE created_at BETWEEN '2013-05-01 00: 00: 00 + 00000 ' AND '2013-05-01 00: 00: 00 + 00000 '

ซึ่งจะเทียบเท่ากับ:

เลือก * จากกรณีที่สร้างขึ้น _at = '2013-05-01 00: 00: 00 + 00000 '

ปัญหาที่เกิดขึ้นเป็นหนึ่งในการรับรู้ / ความคาดหวังเกี่ยวกับการBETWEENที่ไม่ได้รวมทั้งค่าที่ต่ำกว่าและค่าตอนบนในช่วง แต่ไม่ได้อย่างน่าอัศจรรย์ทำให้วันส่วน "จุดเริ่มต้นของ" หรือ "จุดสิ้นสุดของ"

BETWEEN ควรหลีกเลี่ยงเมื่อกรองตามช่วงวันที่

ใช้>= AND <แทนเสมอ

เลือก * จากกรณี 
WHERE (created_at > = '20130501' และ created_at < '20130502')

วงเล็บเป็นทางเลือกที่นี่ แต่อาจมีความสำคัญในการสืบค้นที่ซับซ้อนมากขึ้น


2
ไม่แน่ใจเกี่ยวกับภูมิปัญญาของการโพสต์สิ่งนี้หลังจากที่คำถามได้รับคำตอบแล้ว แต่ฉันต้องการเน้นประเด็นที่แตกต่างออกไปเล็กน้อย
Used_By_ แล้ว

5
ถึงบรรณาธิการ; โปรดอย่าพยายามทำให้ SQL หลอกเป็นบล็อกโค้ดมันไม่ได้ผลเนื่องจากความคิดเห็นขึ้นอยู่กับ BOLD
Used_By_ ตั้งแต่

2
ถึงบรรณาธิการ (อีกครั้ง) โปรดอย่าใส่เครื่องหมายคำพูดปลอมผ่านรหัสหลอก พวกเขาไม่ได้ช่วยในการทำความเข้าใจ
Used_By_Al แล้ว

18

คุณต้องเลือกทำอย่างใดอย่างหนึ่งจากสองตัวเลือกนี้:

  1. รวมองค์ประกอบเวลาไว้ในbetweenเงื่อนไขของคุณ: ... where created_at between '2013-05-01 00:00:00' and '2013-05-01 23:59:59'(ไม่แนะนำ ... ดูย่อหน้าสุดท้าย)
  2. ใช้อสมการแทนbetween. สังเกตว่าคุณจะต้องเพิ่มหนึ่งวันเป็นค่าที่สอง:... where (created_at >= '2013-05-01' and created_at < '2013-05-02')

ความชอบส่วนตัวของฉันเป็นตัวเลือกที่สอง นอกจากนี้Aaron Bertrandยังมีคำอธิบายที่ชัดเจนว่าทำไมจึงควรใช้


6
+1 สำหรับ 2 แต่ -1 สำหรับ 1 การแฮ็กในตอนท้ายของวันนี้ไม่น่าเชื่อถือและเป็นความคิดที่ไม่ดี
Aaron Bertrand

1
@AaronBertrand ฉันชอบตัวเลือกที่ 2 ด้วย (ฉันใช้บ่อย) แต่ทำไมคุณถึงบอกว่าตัวเลือกที่ 1 "ไม่น่าเชื่อถืออย่างสมบูรณ์"
Barranka

5
โปรดอ่านนี้เต็ม คุณไม่ควรใช้BETWEENสำหรับการค้นหาช่วงวันที่ที่มีเวลา มากเกินไปอาจผิดพลาดได้
Aaron Bertrand


7

ฉันพบว่าทางออกที่ดีที่สุดในการเปรียบเทียบฟิลด์วันที่และเวลากับฟิลด์วันที่มีดังต่อไปนี้:

DECLARE @StartDate DATE = '5/1/2013', 
        @EndDate   DATE = '5/1/2013' 

SELECT * 
FROM   cases 
WHERE  Datediff(day, created_at, @StartDate) <= 0 
       AND Datediff(day, created_at, @EndDate) >= 0 

สิ่งนี้เทียบเท่ากับการรวมระหว่างคำสั่งเนื่องจากมีทั้งวันที่เริ่มต้นและวันที่สิ้นสุดรวมทั้งวันที่อยู่ระหว่าง


3
cast(created_at as date)

ซึ่งจะใช้ได้เฉพาะในปี 2008 และ SQL Server เวอร์ชันที่ใหม่กว่า

หากคุณใช้เวอร์ชันเก่าให้ใช้ไฟล์

convert(varchar, created_at, 101)

2
convert(varchar, created_at, 101)ผลลัพธ์เป็นเหมือนdd/MM/yyyyและสตริงของ OP yyyy-MM-ddฉันคิดว่าคำตอบนี้จะใช้ไม่ได้;)
จันทร์ที่

2

คุณสามารถใช้date()ฟังก์ชันซึ่งจะดึงวันที่จากวันที่และเวลาและให้ผลลัพธ์เป็นวันที่รวม:

SELECT * FROM Cases WHERE date(created_at)='2013-05-01' AND '2013-05-01'

-1

วันที่ Dyamic ระหว่างแบบสอบถาม sql

var startDate = '2019-08-22';
var Enddate = '2019-10-22'
     let sql = "SELECT * FROM Cases WHERE created_at BETWEEN '?' AND '?'";
     const users = await mysql.query( sql, [startDate, Enddate]);
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.