将 varchar 数据类型转换为 datetime 数据类型导致值超出范围的问题

Posted

技术标签:

【中文标题】将 varchar 数据类型转换为 datetime 数据类型导致值超出范围的问题【英文标题】:Problem with The conversion of a varchar data type to a datetime data type resulted in an out-of-range value 【发布时间】:2020-10-29 11:33:05 【问题描述】:

我在 SQL 服务器中执行此 SQL 请求时收到此消息,

将 varchar 数据类型转换为 datetime 数据类型导致值超出范围。

这是我的 SQL 请求:

set language english

SELECT DISTINCT TOP (10000)
       ROW_NUMBER() OVER (PARTITION BY TPP.NoPersonne,
                                       TROC.reftiers,
                                       CONVERT(datetime, TSIT.DateAffiliation, 120),
                                       CONVERT(datetime, COALESCE(TSIT.DateSortie, '31/12/9999'), 120)
                          ORDER BY CONVERT(datetime, COALESCE(TCT.DateFinValidite, '31/12/9999'), 120) DESC) AS RowCount1,
       ROW_NUMBER() OVER (PARTITION BY TPP.NoPersonne,
                                       TROC.reftiers,
                                       CONVERT(datetime, TSIT.DateAffiliation, 120),
                                       CONVERT(datetime, COALESCE(TSIT.DateSortie, '31/12/9999'), 120)
                          ORDER BY CONVERT(datetime, COALESCE(TP.DateFin, '31/12/9999'), 120) DESC) AS RowCount2
FROM stock.TPersonnePhysique TPP
     INNER JOIN [dbo].[ZED_PP_ACTIVE] PPA ON TPP.OID = PPA.OID_PP
     INNER JOIN stock.TRoleAssure TRO ON TPP.oid = TRO.reftiers
                                     AND TPP.flag_delete = 0
                                     AND TRO.flag_delete = 0
     INNER JOIN stock.TSitContractAff TSIT ON TRO.oid = TSIT.refroletiers
                                          AND TSIT.flag_delete = 0
                                          AND CONVERT(datetime, ISNULL(TSIT.DateSortie, '01/01/9999'), 120) > DATEADD(YEAR, -3, GETDATE())
     INNER JOIN stock.TLienContratAdherentCol TLI ON TCON.RefLienContratAdherentColl = TLI.oid
                                                 AND TLI.flag_delete = 0
     LEFT JOIN(stock.TRattachementPopulation TRP
               INNER JOIN stock.JDonneesComplementaires_RattPop JDC_RP ON JDC_RP.RefRattachementPopulation = TRP.OID
                                                                      AND JDC_RP.Flag_Delete = 0
                                                                      AND TRP.Flag_Delete = 0
               INNER JOIN stock.TContratTravail TCT ON TCT.OID = JDC_RP.ListDonneesComplementaires
                                                   AND TCT.Flag_Delete = 0
                                                   AND TCT.CategorieProfessionnelle <> ''
                                                   AND TCT.CategorieProfessionnelle IS NOT NULL)ON TRP.RefMembre = TRO.OID
                                                                                               AND ((CONVERT(datetime, TCT.DateDebutValidite, 120) >= CONVERT(datetime, TSIT.DateAffiliation, 120)
                                                                                                 AND CONVERT(datetime, TCT.DateDebutValidite, 120) <= CONVERT(datetime, COALESCE(TSIT.DateSortie, '2999-12-31'), 120))
                                                                                                 OR (CONVERT(datetime, TSIT.DateAffiliation, 120) >= CONVERT(datetime, TCT.DateDebutValidite, 120)
                                                                                                 AND CONVERT(datetime, TSIT.DateAffiliation, 120) < CONVERT(datetime, COALESCE(TCT.DateFinValidite, '2999-12-31'), 120)));

我想修复这条消息:(

【问题讨论】:

SELECT DISTINCT TOP (1) 没有ORDER BYTOP 1 何时会产生超过 1 个不同的行(提示:它不会)并且您真的对您的解决方案返回任意行感到满意吗? 显示示例输入数据和所需的输出,并解释为什么会这样映射。 至于问题,像AND ((CONVERT(datetime, TCT.DateDebutValidite, 120) &gt;= CONVERT(datetime, TSIT.DateAffiliation, 120)这样的子句strongly意味着TCT.DateDebutValiditeTSIT.DateAffiliation没有存储为datetime;那是你的真正的问题。修复设计; varchar 不是一种适合所有数据类型的尺寸。 这只是一个例子,查询返回超过 2300 万行。 "这只是示例" 那么为什么要在查询中没有 DISTINCT TOP(1) 时添加它?这只会使您的问题脱轨,而这两者的结合,加上缺少ORDER BY 很有可能使您的问题脱轨。 【参考方案1】:

不要将日期存储为字符串!它只会导致诸如此类的问题。

解决错误的一种方法是使用try_convert()

try_convert(datetime, TSIT.DateAffiliation, 120)

这不是失败,而是返回NULL,因此结果将被过滤掉。

您可以使用以下方法找到违规值:

select TSIT.DateAffiliation
from stock.TSitContractAff TSIT
where try_convert(datetime, TSIT.DateAffiliation, 120) is null and
      TSIT.DateAffiliation is not null;

注意:您需要对查询中的所有日期执行此操作。你真的应该把这些精力放在修复数据模型上,而不是修复查询上。也就是说,使用正确的日期/时间数据类型,而不是字符串。

【讨论】:

以上是关于将 varchar 数据类型转换为 datetime 数据类型导致值超出范围的问题的主要内容,如果未能解决你的问题,请参考以下文章

即使没有 datetime 列,也获取“将 varchar 数据类型转换为 datetime 数据类型导致超出范围的值”

将 varchar 2007-12-19-11.57.1​​7.366731 转换为 datetime 数据类型

Polybase - 将数据类型 VARCHAR 转换为 DATETIME 时出错

如何解决“将 varchar 数据类型转换为 datetime 数据类型导致值超出范围。”错误

消息 242:将 varchar 数据类型转换为 datetime 数据类型导致值超出范围

System.Data.SqlClient.SQLException:将 varchar 数据类型转换为 datetime