从字符串转换日期和/或时间失败

时间:2018-10-14 10:47:42

标签: sql sql-server tsql

我有此查询,我尝试将其转换为每种格式,我指的是日期时间等,但它不起作用并引发错误:

从字符串转换日期和/或时间时转换失败。

SELECT  W.Organization_ID, 
        W.NIT_No, 
        W.SchemeID,
        OpeningDate,
        OpeningTime,
        GETDATE(), 
        WorkNo, 
        CONVERT(decimal(10, 2), W.Cost) AS Cost, 
        WorkName, 
        W.ExpiryDate as ExpiryDate,
        CONVERT(VARCHAR,OpeningDate,106),
        CASE WHEN 
               CONVERT(DATETIME, CONVERT(VARCHAR(20),OpeningDate,106) + ' ' 
               + CONVERT(VARCHAR(20),OpeningTime,108))< GETDATE() 
             THEN 1 
             ELSE 0 END AS OpeningVaild
FROM Works W

CASE部分抛出错误。

  

OpeningDate的类型为Varchar,而OpeningTime的类型为Time。

为什么?

3 个答案:

答案 0 :(得分:0)

您仅转换TIME数据类型,而不转换DATETIME,因此无需指定样式:

DECLARE @T TIME = '08:05:06';

SELECT CONVERT(VARCHAR(8), @T) AS [Time];
SELECT CAST(@T AS VARCHAR(8)) AS [Time];

或者因为您正在使用CONVERT()TIME108的{​​{1}}数据类型而不是114选择正确的style,< / p>

106

更新:

根据错误消息,您的问题出在SELECT CONVERT(VARCHAR(8), @T, 108) AS [Time]; 部分。

因为您要尝试将CASE数据类型与DATETIME连接起来,所以请在此处查看要转换的内容:

VARCHAR

根据错误Msg -,列CASE WHEN CONVERT(DATETIME, CONVERT(VARCHAR(20),OpeningDate,106) + ' ' + CONVERT(VARCHAR(20),OpeningTime,108))< GETDATE() THEN 1 ELSE 0 END AS OpeningVaild -也是OpeningDate,因此您将VARCHAR转换为VARCHAR,然后将其转换再次复制到VARCHAR,然后尝试用将DATETIME列从DATETIME转换为VARCHAR返回的OpeningTime来概括TIME,然后尝试将它们与VARCHAR数据类型GETDATE()进行比较。

所以您DATETIME应该看起来像:

CASE

注释旁边的一行,在此行

CASE WHEN 
       (
         CAST(OpeningDate AS DATETIME) + -- VARCHAR to DATETIME  
         CAST(OpeningTime AS DATETIME)   -- TIME to DATETIME
       ) < GETDATE() 
     THEN 1 
     ELSE 0 END AS OpeningVaild

您正在尝试将CONVERT(VARCHAR,OpeningDate,106), 转换为VARCHAR,但也未指定长度,因此此行应为:

VARCHAR

最后,永远不要将CONVERT(VARCHAR(10),CAST(OpeningDate AS DATE),106), 储存为DATEVARCHAR的存在是有原因的,所以请明智地使用它们和所有其他数据类型。

以下是 demo 代表您的问题以及解决方法。

答案 1 :(得分:0)

所以我知道问题出在这部分:

  

CASE WHEN CONVERT(DATETIME, CONVERT(VARCHAR(20),OpeningDate,106) + ' ' + CONVERT(VARCHAR(20),OpeningTime,108))< GETDATE() THEN 1 ELSE 0 END AS OpeningVaild

更新

自从我第一次发布答案以来,事实证明您将开始日期存储为varchar而不是date
首先,您应该停止这样做。切勿将日期存储在Date列以外的任何内容中(除非您同样需要时间,然后再使用DateTime2)。 有关更多信息,请阅读亚伦·贝特朗(Aaron Bertrand)的Bad habits to kick : choosing the wrong data type.

假设该列的数据类型不能更改,则您在问题的注释中写道:

  

@ZoharPeled:这是开幕日期2017-04-10的格式

说明由将日期存储为字符串引起的问题之一-我或其他任何人如何知道这是4月10日还是10月4日?答案是我们做不到。

因此,假设它是4月10日,则可以使用带有126作为样式参数的convert将其转换为DateTime

CASE 
    WHEN CONVERT(DateTime, OpeningDate, 126) + CAST(OpeningTime As DateTime) < GETDATE() THEN 
        1 
    ELSE 
        0 
END As OpeningVaild

第一版:

假设OpeningDate的数据类型为Date,而OpeningTime的数据类型为Time,似乎您正在尝试确定这些列是否组合成DateTime在当前DateTime之前。

您可以将它们都转换为DateTime并简单地将它们添加在一起,而不是将它们转换为字符串并返回到DateTime

CASE 
    WHEN CAST(OpeningDate As DateTime) + CAST(OpeningTime As DateTime) < GETDATE() THEN 
        1 
    ELSE 
        0 
END As OpeningVaild

另一个选择是两次使用GETDATE()。我认为在select子句中应该没关系,但是在where子句中,使用此选项很重要,因为第一个将使这些列不可分割,这意味着数据库引擎不会能够使用任何可能有助于语句执行计划的索引:

CASE 
WHEN OpeningDate < CAST(GETDATE() AS DATE) 
     OR 
     (
         OpeningDate = CAST(GETDATE() AS DATE) 
         AND OpeningTime <= CAST(GETDATE() AS TIME)
     ) THEN 
    1 
ELSE 
    0 
END AS OpeningVaild

话虽这么说,您的查询也有CONVERT(VARCHAR,OpeningDate,106)-106样式返回日期的字符串表示形式为dd mon yyyy-表示11个字符-因此将其更改为CONVERT(CHAR(11),OpeningDate,106) varchar(未指定长度)默认为30,在这种情况下这不是问题,因为它超过了您需要的11个字符,但it's a bad habit to not specify length and you should kick it.

答案 2 :(得分:0)

您可以通过稍微改变表达方式来简化这一过程。

通过这种方式,您不必转换和连接。

SELECT
  CASE WHEN OpeningDate < GETDATE() - OpeningTime
       THEN 1 
       ELSE 0 END AS OpeningVaild

请注意,我假设Openingdate的格式为dd-mon-yyyy。否则,您仍然需要对其进行转换,但仍要短一些:

SELECT
  CASE WHEN Convert(date, OpeningDate, 106) < GETDATE() - OpeningTime
       THEN 1 
       ELSE 0 END AS OpeningVaild