select *
from A
where posted_date >= '2015-07-27 00:00:00.000'
and posted_date <= '2015-07-27 23:59:59.999'
แต่ผลลัพธ์มีบันทึกที่โพสต์วันนี้: 2015-07-28 เซิร์ฟเวอร์ฐานข้อมูลของฉันไม่ได้อยู่ในประเทศของฉัน อะไรคือปัญหา ?
select *
from A
where posted_date >= '2015-07-27 00:00:00.000'
and posted_date <= '2015-07-27 23:59:59.999'
แต่ผลลัพธ์มีบันทึกที่โพสต์วันนี้: 2015-07-28 เซิร์ฟเวอร์ฐานข้อมูลของฉันไม่ได้อยู่ในประเทศของฉัน อะไรคือปัญหา ?
คำตอบ:
เนื่องจากคุณใช้datetimeประเภทข้อมูลคุณต้องเข้าใจว่าเซิร์ฟเวอร์ sql ปัดเศษข้อมูลวันที่และเวลาอย่างไร
╔═══════════╦═════╦═════════════════════════════╦═════════════════════════════╦══════════╦═══════════╗
║ Name ║ sn ║ Minimum value ║ Maximum value ║ Accuracy ║ Storage ║
╠═══════════╬═════╬═════════════════════════════╬═════════════════════════════╬══════════╬═══════════╣
║ datetime ║ dt ║ 1753-01-01 00:00:00.000 ║ 9999-12-31 23:59:59.997 ║ 3.33 ms ║ 8 bytes ║
║ datetime2 ║ dt2 ║ 0001-01-01 00:00:00.0000000 ║ 9999-12-31 23:59:59.9999999 ║ 100ns ║ 6-8 bytes ║
╚═══════════╩═════╩═════════════════════════════╩═════════════════════════════╩══════════╩═══════════╝
การใช้แบบสอบถามด้านล่างคุณสามารถเห็นปัญหาการปัดเศษที่เซิร์ฟเวอร์ sql ทำเมื่อคุณใช้DATETIME
ชนิดข้อมูล
select '2015-07-27 00:00:00.000' as Original_startDateTime,
convert(datetime ,'2015-07-27 00:00:00.000') as startDateTime,
'2015-07-27 23:59:59.999' as Original_endDateTime,
convert(datetime ,'2015-07-27 23:59:59.999') as endDateTime,
'2015-07-27 00:00:00.000' as Original_startDateTime2,
convert(datetime2 ,'2015-07-27 00:00:00.000') as startDateTime2, -- default precision is 7
'2015-07-27 23:59:59.999' as Original_endDateTime2,
convert(datetime2 ,'2015-07-27 23:59:59.999') as endDateTime2 -- default precision is 7
DATETIME2
ได้รับรอบนับตั้งแต่ SQL Server 2008 DATETIME
เพื่อเริ่มต้นใช้มันแทน สำหรับสถานการณ์ของคุณคุณสามารถใช้datetime2
กับความแม่นยำของทศนิยม 3 ตำแหน่งdatetime2(3)
เช่น
ประโยชน์ของการใช้datetime2
:
datetime
สนับสนุนเพียง 3 ตำแหน่งทศนิยม .. และด้วยเหตุนี้คุณเห็นปัญหาการปัดเศษตั้งแต่เริ่มต้นโดยdatetime
รอบที่ใกล้ที่สุด.003 seconds
กับการเพิ่มขึ้นของ.000
, .003
หรือ.007
วินาทีdatetime2
เป็นมากขึ้นได้อย่างแม่นยำกว่าdatetime
และdatetime2
ช่วยให้คุณควบคุมDATE
และเมื่อเทียบกับTIME
datetime
เอกสารอ้างอิง:
DateTime2
กับDateTime
: สำหรับ - ส่วนใหญ่ - ของ- โลกแห่งความจริง - การใช้งาน - คดี, ประโยชน์ของDateTime2
ต้นทุน <มาก ดู: stackoverflow.com/questions/1334143/… b นั่นไม่ใช่ปัญหารูทที่นี่ ดูความคิดเห็นต่อไป
datetime3
เพิ่มความแม่นยำ 70 หลัก (เทียบกับ 7)) แนวปฏิบัติที่เหมาะสมที่สุดคือการใช้ค่าที่ความแม่นยำไม่สำคัญนั่นคือ <การเริ่มต้นของวินาทีถัดไปนาทีชั่วโมงหรือวันเทียบกับ <= จุดสิ้นสุดของวินาทีวินาทีนาทีชั่วโมงหรือวันก่อนหน้า
ตามที่คนอื่นหลายคนได้กล่าวถึงในความคิดเห็นและคำตอบอื่น ๆ สำหรับคำถามของคุณปัญหาหลัก2015-07-27 23:59:59.999
คือการถูกปัดเศษ2015-07-28 00:00:00.000
โดย SQL Server ตามเอกสารสำหรับDATETIME:
ช่วงเวลา - 00:00:00 ถึง 23: 59: 59.997
โปรดทราบว่าช่วงเวลาไม่สามารถเป็น.999
ได้ นอกจากนี้ในเอกสารประกอบจะระบุกฎการปัดเศษที่ SQL Server ใช้สำหรับเลขนัยสำคัญน้อยที่สุด
โปรดสังเกตว่าตัวเลขที่มีนัยสำคัญน้อยที่สุดสามารถมีค่าที่เป็นไปได้หนึ่งในสามค่า: "0", "3" หรือ "7"
มีวิธีแก้ไขปัญหา / วิธีแก้ไขปัญหาหลายประการสำหรับสิ่งนี้ที่คุณสามารถใช้ได้
-- Option 1
SELECT
*
FROM A
WHERE posted_date >= '2015-07-27 00:00:00.000'
AND posted_date < '2015-07-28 00:00:00.000' --Round up and remove equality
-- Option 2
SELECT
*
FROM A
WHERE posted_date >= '2015-07-27 00:00:00.000'
AND posted_date <= '2015-07-27 23:59:59.997' --Round down and keep equality
-- Option 3
SELECT
*
FROM A
WHERE CAST(posted_date AS DATE) = '2015-07-27' -- Use different data type
-- Option 4
SELECT
*
FROM A
WHERE CONVERT(CHAR(8), DateColumn, 112) = '20150727' -- Cast to string stripping off time
-- Option 5
SELECT
*
FROM A
WHERE posted_date BETWEEN '2015-07-27 00:00:00.000'
AND '2015-07-27 23:59:59.997' --Use between
จากห้าตัวเลือกที่ฉันนำเสนอข้างต้นฉันจะพิจารณาตัวเลือกที่ 1 และ 3 ตัวเลือกที่ทำงานได้เท่านั้น สิ่งเหล่านี้สื่อความตั้งใจของคุณอย่างชัดเจนและจะไม่แตกถ้าคุณอัปเดตประเภทข้อมูล หากคุณใช้ SQL Server 2008 หรือใหม่กว่าฉันคิดว่าตัวเลือก 3 ควรเป็นแนวทางที่คุณต้องการ โดยเฉพาะอย่างยิ่งถ้าคุณสามารถเปลี่ยนจากการใช้DATETIMEประเภทข้อมูลเป็นDATEชนิดข้อมูลสำหรับposted_date
คอลัมน์ของคุณ
เกี่ยวกับตัวเลือกที่ 3 คำอธิบายที่ดีมากเกี่ยวกับบางประเด็นสามารถพบได้ที่นี่: การเผยแพร่ในปัจจุบันเป็นเรื่องที่น่าเบื่อ แต่เป็นความคิดที่ดีหรือไม่?
ฉันไม่ชอบตัวเลือกที่ 2 และ 5 เพราะ.997
เศษเสี้ยววินาทีจะเป็นเพียงจำนวนเวทมนตร์ที่ผู้คนอยากจะ "แก้ไข" ด้วยเหตุผลเพิ่มเติมบางประการที่BETWEEN
ไม่ได้รับการยอมรับอย่างกว้างขวางคุณอาจต้องการเช็คเอาต์โพสต์นี้
ฉันไม่ชอบตัวเลือก 4 เนื่องจากการแปลงชนิดข้อมูลเป็นสตริงเพื่อการเปรียบเทียบรู้สึกสกปรกสำหรับฉัน เหตุผลเชิงคุณภาพเพิ่มเติมเพื่อหลีกเลี่ยงใน SQL Server คือมันส่งผลกระทบต่อsargability aka คุณไม่สามารถทำการค้นหาดัชนีและที่มักจะส่งผลให้ประสิทธิภาพการทำงานต่ำ
สำหรับข้อมูลเพิ่มเติมเกี่ยวกับวิธีการที่เหมาะสมและวิธีที่ผิดในคำสั่งช่วงวันที่จับเช็คเอาต์โพสต์นี้โดยแอรอนเบอร์ทรานด์
ในการแยกทางที่คุณจะสามารถที่จะเก็บแบบสอบถามเดิมของคุณและมันจะทำงานตามที่ต้องการถ้าคุณเปลี่ยนของคุณposted_date
คอลัมน์จากไปDATETIME DATETIME2(3)
ซึ่งจะช่วยประหยัดพื้นที่เก็บข้อมูลบนเซิร์ฟเวอร์ให้ความแม่นยำที่มากขึ้นด้วยความแม่นยำเดียวกันเป็นมาตรฐานที่มากขึ้น / พกพาได้และช่วยให้คุณปรับความแม่นยำ / ความแม่นยำได้อย่างง่ายดายหากความต้องการของคุณเปลี่ยนแปลงในอนาคต อย่างไรก็ตามนี่เป็นเพียงตัวเลือกหากคุณใช้ SQL Server 2008 หรือใหม่กว่า
เล็กน้อย1/300
ของความแม่นยำที่สองด้วยDATETIMEดูเหมือนว่าจะถูกระงับจาก UNIX ต่อคำตอบ StackOverflowนี้ Sybase ซึ่งมีการแบ่งมรดกมีความคล้ายคลึงกัน1/300
ของความถูกต้องที่สองในประเภทDATETIME
และTIME
ข้อมูล แต่ตัวเลขที่สำคัญน้อยที่สุดของพวกเขาคือการสัมผัสที่แตกต่างกันที่ "0", "3" และ "6" ในความเห็นของฉันความ1/300
ถูกต้องของวินาทีและ / หรือ 3.33ms เป็นการตัดสินใจทางสถาปัตยกรรมที่โชคร้ายเนื่องจากบล็อก 4 ไบต์สำหรับเวลาในDATETIMEชนิดข้อมูลของ SQL Server อาจสนับสนุนความแม่นยำ 1ms ได้อย่างง่ายดาย
datetime3
เพิ่มความแม่นยำ 70 หลัก (กับ 7) แนวปฏิบัติที่เหมาะสมที่สุดคือการใช้ค่าที่ความแม่นยำไม่สำคัญนั่นคือ <การเริ่มต้นของวินาทีถัดไปนาทีชั่วโมงหรือวันเทียบกับ <= จุดสิ้นสุดของวินาทีวินาทีนาทีชั่วโมงหรือวันก่อนหน้า
การแปลงโดยนัย
ฉันว่าประเภทข้อมูลที่โพสต์แล้วคือ Datetime อย่างไรก็ตามมันไม่สำคัญว่าประเภทในด้านอื่น ๆ คือ Datetime, Datetime2 หรือ Just Time เพราะสตริง (Varchar) จะถูกแปลงเป็น Datetime โดยปริยาย
ด้วย posts_date ที่ประกาศเป็น Datetime2 (หรือเวลา) ส่วนposted_date <= '2015-07-27 23:59:59.99999'
คำสั่งล้มเหลวเนื่องจาก altough 23:59:59.99999
เป็นค่า Datetime2 ที่ถูกต้องนี่ไม่ใช่ค่า Datetime ที่ถูกต้อง:
Conversion failed when converting date and/or time from character string.
ช่วงเวลาสำหรับวันที่และเวลา
ช่วงเวลาของวันที่และเวลาคือ 00:00:00 ถึง 23: 59: 59.997 ดังนั้น 23: 59: 59.999 จึงอยู่นอกระยะและต้องปัดขึ้นหรือลงเพื่อให้ได้ค่าที่ใกล้เคียงที่สุด
ความถูกต้อง
นอกเหนือจากค่าของวันที่และเวลาจะถูกปัดเศษด้วยการเพิ่มขึ้นของ. 000, .003 หรือ .007 วินาที (เช่น. 000, 003, 007, 010, 013, 017, 020, ... , 997)
นี้ไม่ได้เป็นกรณีที่มีค่า2015-07-27 23:59:59.999
ที่อยู่ในช่วงนี้และ2015-07-27 23:59:59.997
2015-07-28 0:00:00.000
ช่วงนี้สอดคล้องกับตัวเลือกก่อนหน้าและตัวเลือกที่ใกล้เคียงที่สุดซึ่งทั้งคู่ลงท้ายด้วย .000, .003 หรือ. 007
ปัดเศษขึ้นหรือลง ?
เพราะมันอยู่ใกล้กับ2015-07-28 0:00:00.000
(1 เมื่อเทียบกับ -2) มากกว่าสตริงจะถูกปัดเศษขึ้นและกลายเป็นนี้ค่าวันที่และเวลา:2015-07-27 23:59:59.997
2015-07-28 0:00:00.000
ด้วยขีด จำกัด บนเช่น2015-07-27 23:59:59.998
(หรือ .995, .996, .997, .998) มันจะถูกปัดเศษลง2015-07-27 23:59:59.997
และแบบสอบถามของคุณจะทำงานตามที่คาดไว้ อย่างไรก็ตามมันจะไม่ได้เป็นทางออก แต่เป็นเพียงโชคดี
Datetime2 หรือประเภทเวลา
ช่วงเวลา Datetime2 และเวลา00:00:00.0000000
ผ่านไป23:59:59.9999999
ด้วยความแม่นยำ 100ns (ตัวเลขสุดท้ายเมื่อใช้กับความแม่นยำ 7 หลัก)
อย่างไรก็ตามช่วง Datetime (3) ไม่เหมือนกับช่วง Datetime:
0:0:00.000
ถึง23:59:59.997
0:0:00.000000000
ถึง23:59:59.999
วิธีการแก้
ในท้ายที่สุดการค้นหาวันที่ด้านล่างในวันถัดไปนั้นจะปลอดภัยกว่าหรือเท่ากับสิ่งที่คุณคิดว่าเป็นช่วงเวลาสุดท้ายของวัน นี่เป็นส่วนใหญ่เพราะคุณรู้ว่าในวันถัดไปจะเริ่มต้นที่ 0: 00: 00.000 แต่ประเภทข้อมูลที่แตกต่างกันอาจไม่มีเวลาเดียวกันในตอนท้ายของวัน:
Datetime `0:0:00.000` to `23:59:59.997`
Datetime2 `0:0:00.000000000` to `23:59:59.999-999-900`
Time2 `0:0:00.000000000` to `23:59:59.999-999-900`
< 2015-07-28 0:00:00.000
จะให้ผลลัพธ์ที่ถูกต้องแก่คุณและเป็นตัวเลือกที่ดีที่สุด<= 2015-07-27 23:59:59.xxx
อาจส่งคืนค่าที่ไม่คาดคิดเมื่อมันไม่ถูกปัดเศษขึ้นกับสิ่งที่คุณคิดว่าควรจะเป็นเราคิดว่าการเปลี่ยน [posted_date] เป็น Datetime2 และความแม่นยำที่สูงขึ้นของมันสามารถแก้ไขปัญหานี้ได้ แต่จะไม่ช่วยได้เพราะสตริงนั้นยังคงถูกแปลงเป็น Datetime แต่ถ้าหล่อจะมีการเพิ่มcast(2015-07-27 23:59:59.999' as datetime2)
นี้ทำงานได้ดี
หล่อและแปลง
Cast สามารถแปลงค่าที่มีสูงสุด 3 หลักเป็น Datetime หรือสูงสุด 9 หลักเป็น Datetime2 หรือ Time และปัดเศษเป็นความแม่นยำที่ถูกต้อง
เป็นที่น่าสังเกตว่า Cast of Datetime2 และ Time2 อาจให้ผลลัพธ์ที่แตกต่าง:
select cast('20150101 23:59:59.999999999' as datetime2(7))
เปิดตัว 2015-05-03 00: 00: 00.0000000 (สำหรับมูลค่าที่มากกว่า 999999949)select cast('23:59:59.999999999' as time(7))
=> 23: 59: 59.9999999การเรียงลำดับของการแก้ไขปัญหา datetime จะมีการเพิ่มขึ้น 0, 3 และ 7 แม้ว่ามันจะยังดีกว่าเสมอที่จะมองหาวันที่ก่อนที่ 1 นาโนวินาทีที่สองของวันถัดไป (เสมอ 0: 00: 00.000)
Source MSDN: datetime (Transact-SQL)
มันกำลังปัดเศษ
select cast('2015-07-27 23:59:59.999' as datetime)
returns 2015-07-28 00:00:00.000
.998, .997, .996, .995 ทุกตัวละคร / รอบเป็น. 997
ควรใช้
select *
from A
where posted_date >= '2015-07-27 00:00:00.000'
and posted_date < '2015-07-28 00:00:00.000'
หรือ
where cast(posted_date as date) = '2015-07-27'
ดูความแม่นยำในลิงค์นี้
รายงานเป็น. 000, .003, .007 เสมอ
select * from A where date(posted_date) = '2015-07-27'
'DATE' is not a recognized built-in function name.
gives you control of DATE and TIME as opposed to datetime.
นั่นหมายความว่าอย่างไร?