我有此查询,我尝试将其转换为每种格式,我指的是日期时间等,但它不起作用并引发错误:
从字符串转换日期和/或时间时转换失败。
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。
为什么?
答案 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()
为TIME
或108
的{{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),
储存为DATE
,VARCHAR
的存在是有原因的,所以请明智地使用它们和所有其他数据类型。
以下是 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