Puisque vous utilisez le datetimetype de données, vous devez comprendre comment le serveur SQL arrondit les données datetime.
╔═══════════╦═════╦═════════════════════════════╦═════════════════════════════╦══════════╦═══════════╗
║ 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 ║
╚═══════════╩═════╩═════════════════════════════╩═════════════════════════════╩══════════╩═══════════╝
En utilisant la requête ci-dessous, vous pouvez facilement voir le problème d'arrondi que fait le serveur SQL lorsque vous utilisez DATETIME
le type de données.
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
Cliquez pour agrandir
DATETIME2
existe depuis SQL Server 2008, alors commencez à l'utiliser au lieu de DATETIME
. Pour votre situation, vous pouvez utiliser datetime2
avec une précision de 3 décimales par exemple datetime2(3)
.
Avantages de l'utilisation datetime2
:
- Prend en charge jusqu'à 7 décimales pour le composant temps par rapport à la prise en
datetime
charge de seulement 3 décimales .. et donc vous voyez le problème d'arrondi car, par défaut, datetime
arrondit le plus proche .003 seconds
avec des incréments de .000
, .003
ou .007
secondes.
datetime2
est beaucoup plus précis que datetime
et datetime2
vous donne le contrôle de DATE
et TIME
par opposition à datetime
.
Référence :
gives you control of DATE and TIME as opposed to datetime.
Qu'est-ce que ça veut dire?DateTime2
vsDateTime
: a. Pour - la - vaste - majorité - des - cas d'utilisation du monde réel , les avantages deDateTime2
Much <cost. Voir: stackoverflow.com/questions/1334143/… b. Ce n'est pas le problème racine ici. Voir le commentaire suivant.datetime3
avec 70 (vs 7) chiffres de précision ajoutés?). La meilleure pratique consiste à utiliser une valeur où la précision n'a pas d'importance, c'est-à-dire <le début de la seconde, minute, heure ou jour suivante vs <= la fin de la seconde, minute, heure ou jour précédente.Comme plusieurs autres l'ont mentionné dans les commentaires et autres réponses à votre question, le problème principal
2015-07-27 23:59:59.999
est arrondi à2015-07-28 00:00:00.000
SQL Server. Selon la documentation de DATETIME:Notez que la plage de temps ne peut jamais l' être
.999
. Plus bas dans la documentation, il spécifie les règles d'arrondi que SQL Server utilise pour le chiffre le moins significatif.Notez que le chiffre le moins significatif ne peut avoir qu'une des trois valeurs potentielles: "0", "3" ou "7".
Il existe plusieurs solutions / solutions de contournement pour cela que vous pouvez utiliser.
Sur les cinq options que j'ai présentées ci-dessus, je considérerais les options 1 et 3 comme les seules options viables. Ils expriment clairement votre intention et ne briseront pas si vous mettez à jour les types de données. Si vous utilisez SQL Server 2008 ou une version plus récente, je pense que l'option 3 devrait être votre approche préférée. Cela est particulièrement vrai si vous pouvez remplacer l'utilisation du DATETIMEtype de données par un DATEtype de données pour votre
posted_date
colonne.En ce qui concerne l'option 3, une très bonne explication sur certains problèmes peut être trouvée ici: Cast to date is sargable but is this good idea?
Je n'aime pas les options 2 et 5, car les
.997
secondes fractionnaires ne seront qu'un autre nombre magique que les gens voudront "corriger". Pour quelques raisons supplémentaires quiBETWEEN
ne sont pas largement acceptées, vous voudrez peut-être consulter ce post .Je n'aime pas l'option 4 car la conversion des types de données en chaîne à des fins de comparaison me semble sale. Une raison plus qualitative de l'éviter dans SQL Server est qu'elle affecte la sargabilité vous ne pouvez pas effectuer une recherche d'index et que cela entraînera souvent de moins bonnes performances.
Pour plus d'informations sur la bonne et la mauvaise façon de gérer les requêtes de plage de dates, consultez cet article d' Aaron Bertrand .
En vous séparant, vous seriez en mesure de conserver votre requête d'origine et elle se comporterait comme vous le souhaitez si vous changez votre
posted_date
colonne de a DATETIMEen aDATETIME2(3)
. Cela économiserait de l'espace de stockage sur le serveur, vous donnerait une plus grande précision à la même précision, serait plus conforme aux normes / portable et vous permettrait d'ajuster facilement la précision / précision si vos besoins changent à l'avenir. Cependant, ce n'est qu'une option si vous utilisez SQL Server 2008 ou une version plus récente.Comme un petit anecdote, la
1/300
précision d'une seconde avec DATETIMEsemble être un maintien d'UNIX par cette réponse StackOverflow . Sybase qui a un héritage partagé a une1/300
précision similaire d'une seconde dans leurs types de donnéesDATETIME
etTIME
, mais leurs chiffres les moins significatifs sont légèrement différents à "0", "3" et "6". À mon avis, la1/300
précision d'une seconde et / ou 3,33 ms est une décision architecturale malheureuse car le bloc de 4 octets pour le moment dans le DATETIMEtype de données SQL Server aurait pu facilement prendre en charge une précision de 1 ms.la source
datetime3
avec 70 (contre 7) chiffres de précision ajoutés? La meilleure pratique consiste à utiliser une valeur où la précision n'a pas d'importance, c'est-à-dire <le début de la seconde, minute, heure ou jour suivante vs <= la fin de la seconde, minute, heure ou jour précédente.Je supposais que le type de données posted_date est Datetime. Cependant, peu importe que le type de l'autre côté soit Datetime, Datetime2 ou simplement Time car la chaîne (Varchar) sera implicitement convertie en Datetime.
Avec Posté_date déclaré en tant que Datetime2 (ou Time), la
posted_date <= '2015-07-27 23:59:59.99999'
clause where échoue car altough23:59:59.99999
est une valeur Datetime2 valide, ce n'est pas une valeur Datetime valide:La plage horaire de Datetime est de 00:00:00 à 23: 59: 59.997. Par conséquent, 23: 59: 59.999 est hors de portée et doit être arrondi à la valeur la plus proche.
En outre, les valeurs Datetime sont arrondies par incréments de .000, .003 ou .007 secondes. (c'est-à-dire 000, 003, 007, 010, 013, 017, 020, ..., 997)
Ce n'est pas le cas avec la valeur
2015-07-27 23:59:59.999
qui se situe dans cette plage:2015-07-27 23:59:59.997
et2015-07-28 0:00:00.000
.Cette plage correspond aux options précédentes et suivantes les plus proches, toutes deux se terminant par .000, .003 ou .007.
Parce qu'il est plus proche de
2015-07-28 0:00:00.000
(+1 par rapport à -2) que2015-07-27 23:59:59.997
, la chaîne est arrondie et devient cette valeur Datetime:2015-07-28 0:00:00.000
.Avec une limite supérieure comme
2015-07-27 23:59:59.998
(ou .995, .996, .997, .998), elle aurait été arrondie à2015-07-27 23:59:59.997
et votre requête aurait fonctionné comme prévu. Cependant, cela n'aurait pas été une solution mais juste une valeur chanceuse.Plages de temps datetime2 et le temps sont
00:00:00.0000000
grâce23:59:59.9999999
avec une précision de 100 ns (le dernier chiffre , lorsqu'il est utilisé avec une précision à 7 chiffres).Cependant, une plage Datetime (3) n'est pas similaire à la plage Datetime:
0:0:00.000
à23:59:59.997
0:0:00.000000000
à23:59:59.999
En fin de compte, il est plus sûr de rechercher des dates inférieures au lendemain que des dates inférieures ou égales à ce que vous pensez que c'est le dernier fragment de l'heure de la journée. C'est principalement parce que vous savez que le lendemain commence toujours à 0: 00: 00.000 mais que différents types de données peuvent ne pas avoir la même heure à la fin de la journée:
< 2015-07-28 0:00:00.000
vous donnera des résultats précis et est la meilleure option<= 2015-07-27 23:59:59.xxx
peut renvoyer des valeurs inattendues lorsqu'elle n'est pas arrondie à ce que vous pensez qu'elle devrait être.Nous pourrions penser que le changement de [posted_date] en Datetime2 et sa précision plus élevée pourraient résoudre ce problème, mais cela n'aidera pas car la chaîne est toujours convertie en Datetime. Cependant, si un casting est ajouté
cast(2015-07-27 23:59:59.999' as datetime2)
, cela fonctionne bienCast peut convertir une valeur avec jusqu'à 3 chiffres en Datetime ou avec jusqu'à 9 chiffres en Datetime2 ou Time et l'arrondir à la précision correcte.
Il est à noter que Cast of Datetime2 et Time2 peuvent donner des résultats différents:
select cast('20150101 23:59:59.999999999' as datetime2(7))
est arrondi 2015-05-03 00: 00: 00.0000000 (pour une valeur supérieure à 999999949)select cast('23:59:59.999999999' as time(7))
=> 23: 59: 59.9999999Il résout en quelque sorte le problème de datetime avec les incréments de 0, 3 et 7, bien qu'il soit toujours préférable de rechercher des dates avant la 1re nano seconde du jour suivant (toujours 0: 00: 00.000).
MSDN source: datetime (Transact-SQL)
la source
Il arrondit
.998, .997, .996, .995 tous moulés / ronds à .997
Devrait utiliser
ou
Voir la précision dans ce lien
Toujours signalé comme .000, .003, .007
la source
la source
'DATE' is not a recognized built-in function name.