4

以下两个查询返回不同的结果。我知道差异与处理日期的时间部分的方式有关,但为什么它会以这种方式工作?

// QUERY ONE
select top 3 OrderDate
from Orders
where OrderDate >= '2013-11-01 04:00'
and OrderDate <= '2013-11-30 05:00'
order by OrderDate

// RESULTS
// 2013-11-01
// 2013-11-01
// 2013-11-01

// QUERY TWO
exec sp_executesql
    N'select top 3 OrderDate
      from Orders
      where OrderDate >= @p__linq__0
      and OrderDate <= @p__linq__1
      order by OrderDate',
    N'@p__linq__0 datetime2(7),@p__linq__1 datetime2(7)',
    @p__linq__0='2013-11-01T04:00:00',
    @p__linq__1='2013-11-30T05:00:00'

// RESULTS
// 2013-11-02
// 2013-11-02
// 2013-11-02

更新

如果我将传递给 sp_executesql 的参数类型更改为“日期”而不是“日期时间”,结果是相同的。

// QUERY THREE
exec sp_executesql
N'select top 3 OrderDate
  from Orders
  where OrderDate >= @p__linq__0
  and OrderDate <= @p__linq__1
  order by OrderDate',
N'@p__linq__0 date,@p__linq__1 date',
@p__linq__0='2013-11-01T04:00:00',
@p__linq__1='2013-11-30T05:00:00'

// RESULTS
// 2013-11-01
// 2013-11-01
// 2013-11-01
4

3 回答 3

6

数据类型优先级是获取表中的数据,该数据以日期开头,并将其作为 datetime2(7) 进行比较。因此,您的动态 SQL 版本实际上正在运行:

WHERE column_as_datetime2 >= @parameter_as_datetime2

因此,由于2013-11-01 00:00:00.0000000不大于或等于,因此2013-11-01 04:00:00.0000000从 11 月 1 日开始的行将被忽略。

最实用的解决方案是使用DATE参数(首选,因为参数应该匹配底层数据类型,毕竟),和/或停止与它们一起传递时间值。试试这些:

USE tempdb;
GO

CREATE TABLE dbo.Orders(OrderDate DATE);

INSERT dbo.Orders VALUES('2013-11-01'),('2013-11-01'),('2013-11-01'),
  ('2013-11-02'),('2013-11-02'),('2013-11-02');

exec sp_executesql N'select top 3 OrderDate
      from Orders
      where OrderDate >= @p__linq__0
      and OrderDate <= @p__linq__1
      order by OrderDate;
select top 3 OrderDate
      from Orders
      where OrderDate >= @p2
      and OrderDate <= @p3
      order by OrderDate;
select top 3 OrderDate
      from Orders
      where OrderDate >= @p4
      and OrderDate <= @p5
      order by OrderDate;',
    N'@p__linq__0 datetime2(7),@p__linq__1 datetime2(7),
      @p2 datetime2(7),@p3 datetime2(7),@p4 date,@p5 date',
    @p__linq__0='2013-11-01T04:00:00',
    @p__linq__1='2013-11-30T05:00:00',
    @p2='2013-11-01T00:00:00', -- note no time
    @p3='2013-11-30T00:00:00', -- note no time
    @p4='2013-11-01',
    @p5='2013-11-30';

结果:

OrderDate
----------
2013-11-02
2013-11-02
2013-11-02

OrderDate
----------
2013-11-01
2013-11-01
2013-11-01

OrderDate
----------
2013-11-01
2013-11-01
2013-11-01
于 2013-11-05T16:48:25.230 回答
4

我敢打赌专栏OrderDate是类型date,不是datetime。所以当你这样做时

where OrderDate >= '2013-11-01 04:00'

它转换'2013-11-01 04:00'date, not datetime,因此它丢失了时间信息。因此,第一个查询中的条件被解释为'2013-11-01 00:00:00' >= '2013-11-01 00:00:00'。这是真的。

在第二个查询中,SP 接收到一个类型为 的参数datetime,其中包含时间信息。那里的条件被解释为'2013-11-01 00:00:00' >= '2013-11-01 04:00:00'哪个是假的。

如果您希望在第一个查询中具有相同的行为,请使用datetime变量而不是字符串。

declare @d1 datetime
declare @d2 datetime
set @d1 = '2013-11-01 04:00'
set @d2 = '2013-11-30 05:00'

select top 3 OrderDate
from Orders
where OrderDate >= @d1
and OrderDate <= @d2
order by OrderDate
于 2013-11-05T16:43:42.067 回答
0

尝试这个 ...

DECLARE @p__linq__0_R datetime2(7) = '2013-11-01T04:00:00'
DECLARE @p__linq__1_R datetime2(7) = '2013-11-30T05:00:00'

DECLARE @sql NVARCHAR(MAX);

SET @sql =  N'select top 3 OrderDate
      from Orders
      where OrderDate >= @p__linq__0_s
      and OrderDate <= @p__linq__1_s
      order by OrderDate'

exec sp_executesql @sql,
    N'@p__linq__0_s datetime2(7),@p__linq__1_s datetime2(7)',
    @p__linq__0_s =@p__linq__0_R,
    @p__linq__1_s=@p__linq__1_R

编辑
如果您的列只是 Datetime 而不是 Datetime2,那么 sql server 正在使用 Datetime 数据类型对您的列进行隐式转换,因为 Datetime2 是比 datetime 更高优先级的数据类型。比较两个值 Sql server 期望值是相同的数据类型。也许这就是导致问题的原因。一种解决方法是您可以在比较值时将 datetime 列转换为 datetime2。

于 2013-11-05T16:32:41.430 回答