Réponses:
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
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
:
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 :
DateTime2
vs DateTime
: a. Pour - la - vaste - majorité - des - cas d'utilisation du monde réel , les avantages de DateTime2
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:
Plage de temps - 00:00:00 à 23: 59: 59.997
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.
-- 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
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 votreposted_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 qui BETWEEN
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 une 1/300
précision similaire d'une seconde dans leurs types de données DATETIME
etTIME
, mais leurs chiffres les moins significatifs sont légèrement différents à "0", "3" et "6". À mon avis, la 1/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.
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.
Conversion implicite
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 altough 23:59:59.99999
est une valeur Datetime2 valide, ce n'est pas une valeur Datetime valide:
Conversion failed when converting date and/or time from character string.
Plage de temps pour Datetime
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.
Précision
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
et 2015-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.
Arrondir vers le haut ou vers le bas ?
Parce qu'il est plus proche de 2015-07-28 0:00:00.000
(+1 par rapport à -2) que 2015-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.
Datetime2 ou types d'heure
Plages de temps datetime2 et le temps sont 00:00:00.0000000
grâce 23: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
Solution
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:
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
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 bien
Cast et conversion
Cast 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)
Il arrondit
select cast('2015-07-27 23:59:59.999' as datetime)
returns 2015-07-28 00:00:00.000
.998, .997, .996, .995 tous moulés / ronds à .997
Devrait utiliser
select *
from A
where posted_date >= '2015-07-27 00:00:00.000'
and posted_date < '2015-07-28 00:00:00.000'
ou
where cast(posted_date as date) = '2015-07-27'
Voir la précision dans ce lien
Toujours signalé comme .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.
Qu'est-ce que ça veut dire?