SQL Server 2008 R2中的奇怪CAST()行为

时间:2014-11-17 10:55:52

标签: sql sql-server sql-server-2008-r2

我在DataBase中有一个表。表由两列组成。 DeliveryDate的第一列有一种DATETIME类型 DeliveryTime的第二列,其类型为VARCHAR

以下是表格数据的一个小例子

          DeliveryDate       -  DeliveryTime  
    2014-11-06 00:00:00.000  -     15:00
    2014-11-06 00:00:00.000  -     15:00
    2014-11-12 00:00:00.000  -     09:00
    2014-11-12 00:00:00.000  -     09:00

表中有92行。

目标是创建一个查询,将DeliveryDate和DeliveryTime连接到单个DATETIME列

我提出这样的要求

SELECT CAST((O.DeliveryDate + RIGHT(ISNULL(O.DeliveryTime,'00:00'),5)) AS DATETIME)
FROM MySuperTable

查询失败,并显示varchar无效转换错误

但如果我用TOP进行查询,例如TOP 92.

SELECT TOP 92 CAST((O.DeliveryDate + RIGHT(ISNULL(O.DeliveryTime,'00:00'),5)) AS DATETIME)
FROM MySuperTable

查询完成且没有错误。 此外TOP到620是好的坚果TOP 621再次失败。 请帮助解决顶级逻辑如何工作以及我的代码中的问题

5 个答案:

答案 0 :(得分:1)

这是由于查询优化器以及它如何优化查询。它很可能假设它可以重新安排一两步以使事情更快,而不是意识到存在数据问题。这就是为什么

  • 在只有92行时执行TOP 620因为它可能影响优化器构建如何获取数据的路径的方式
  • 将计算分离到UDF,因为它阻止优化器采取导致问题的任何快捷方式。

但UDF非常慢。相反,请尝试在查询中更明确,不要将其留给SQL Server,以隐式地将字符串(RIGHT函数中出来的时间部分)转换为DATETIME

SELECT O.DeliveryDate + CONVERT(DATETIME, RIGHT(ISNULL(O.DeliveryTime, '00:00'), 5))
FROM MySuperTable;

答案 1 :(得分:0)

您可以简化查询,请尝试以下查询:

SELECT deliverydate
       + Cast(COALESCE(DeliveryTime, '00:00') AS TIME) AS DeliverDateTime
FROM   MySuperTable 

但是,我们应该为列使用适当的数据类型。我们有TIME数据类型来存储时间。

答案 2 :(得分:0)

你有一个空行,它可能是表格中的最后一行。 查找填充NULL的行。

编辑:在“时间”栏中也要仔细查看,可能是某些不正确的时间或不同的格式。

答案 3 :(得分:0)

您的时间价值不合适。以下内容可帮助您找到值:

select t.*
from MySuperTable t
where DeliveryTime not like '[0-5][0-9]:[0-5][0-9]';

您可以将方法与case

一起使用
select (case when DeliveryTime like '[0-5][0-9]:[0-5][0-9]'
             then CAST((O.DeliveryDate + RIGHT(ISNULL(O.DeliveryTime,'00:00'),5)) AS DATETIME)
        end)

如果您使用的是SQL Server 2012+,也可以使用try_convert()

编辑:

另一种可能性是DeliveryDate有一个时间组件。这又回来了什么?

select DeliveryDate
from MySuperTable
where DeliveryDate <> cast(DeliveryDate as date);

答案 4 :(得分:0)

很奇怪,但是当我在标量函数中将CAST分开时,一切都开始正常。但很慢:(

CREATE FUNCTION [dbo].[ConcatDateAndTime]
(
    @DatePart DATETIME,
    @TimePart VARCHAR(50)
)
RETURNS DATETIME
AS
BEGIN
    RETURN @DatePart + RIGHT(COALESCE(@TimePart,'00:00'),5)
END