我有一个返回数千条记录的查询,它们连接在几个表中。在WHERE子句中,过去检查日期不超过两个月。首先,查询将此日期边界设置为变量,并使用WHERE。
DECLARE @startdate as DATE = DATEADD(MONTH, -2, SYSDATETIME())
select [....]
where dateinquestion >= @startdate
运行正常(返回4秒内预期的结果),但我想摆脱变量声明并将赋值移到WHERE子句本身:
select [....]
where dateinquestion >= DATEADD(MONTH, -2, SYSDATETIME())
运行超过一分钟并吃掉所有CPU。我终止了查询以停止推送服务器,没有给出结果。更改为GETDATE()(我不需要SYSDATETIME()的精度)加快速度:
select [....]
where dateinquestion >= DATEADD(MONTH, -2, GETDATE())
结果与方案1类似。
我认为这是因为SYSDATETIME是基于每行进行评估的,因为处理行需要几纳秒,这对SYSDATETIME来说很重要。但是,GETDATE具有更高的更改阈值,不会受到影响(或影响较小),并且不会在每行的基础上发生变化或需要重新评估。
你能证实吗?我对这种行为的假设是否正确?
我已经搜索了这个,但是找不到除此之外的任何内容,这只涉及将SYSDATETIME()分配给变量,而不是在WHERE中使用它: Does SYSDATETIME() cost more than GETDATE()?
此外,但在示例中仅使用GETDATE: TSQL datetimes functions in where clause
答案 0 :(得分:8)
GETDATE
和SYSDATETIME
之间最重要的区别是返回值的类型。不评估每一行SYSDATETIME
,不评估每行的GETDATE
。它们是Runtime Constant Functions另见https://dba.stackexchange.com/questions/18459/does-sql-server-evaluate-functions-once-for-every-row
dateinquestion
列的类型是什么?
使用@startdate
变量后,您将SYSDATETIME
的结果转换为date
。如果您不使用变量,DATEADD
的结果在您的示例中有不同的类型。
要同时使用GETDATE
和SYSDATETIME
两个查询,您可以明确地转换为date
:
比较
select [....]
where dateinquestion >= CAST(DATEADD(MONTH, -2, SYSDATETIME()) AS date)
VS
select [....]
where dateinquestion >= CAST(DATEADD(MONTH, -2, GETDATE()) as date)
如果这两个查询的运行方式不同,我会感到惊讶。
理想情况下,您应该将它们转换为dateinquestion
所具有的相同类型。
你说使用SYSDATETIME
的变体使用了大量的CPU。如果dateinquestion
的类型为datetime
,那么可能的解释就是这样。似乎在此变体中,来自dateinquestion
列的值在比较之前被隐式转换为datetime2(7)
类型。对于每一行。首先,它使用CPU。其次,它可能会阻止优化器在此列上使用索引(如果有索引)。
要了解真实情况,而不是猜测,请比较两种变体的实际执行计划。
顺便说一下,带变量的变量不等同于优化器的内联变体。优化器不知道变量的值,但它知道GETDATE()
和SYSDATETIME()
的值,因此基数估计不同,这可能导致不同的计划和不同的性能。
答案 1 :(得分:3)
没有人提到类型优先权。但这实际上是你问题的答案。事实是SYSDATETIME
返回类型datetime2(7)
而GETDATE
返回类型datetime
。如果您查看类型优先级表(https://msdn.microsoft.com/en-us/library/ms190309.aspx),您会看到datetime2(7)
位于datetime
之前。这意味着:
当一个运算符组合了两个不同数据类型的表达式时, 数据类型优先级的规则指定具有的数据类型 较低的优先级转换为具有较高的数据类型 优先级。
所以实际上表中的每一行都转换为datetime2(7)
。这就是为什么它很慢。如果优先级是相反的,则转换变量而不是表中的每一行。
答案 2 :(得分:2)
Microsoft的定义:
datetime2(7)
会返回 GETDATE
值datetime
会返回 dateinquestion
值如果您的字段datetime
的类型为datetime
,则每行中datetime2
和module TicTacToe
open FsUnit
open NUnit.Framework
[<Test>]
let ``player has connected row`` () =
let grid = Map.empty
.Add(0, true).Add(1, true).Add(2, true)
.Add(3, true).Add(4, false).Add(5, true)
.Add(6, true).Add(7, true).Add(8, true)
let firstRowIsStreak = grid
|> Seq.take 3
|> Seq.forall (fun x -> x.Value = true)
let secondRowIsStreak = grid
|> Seq.skip 3
|> Seq.take 3
|> Seq.forall (fun x -> x.Value = true)
let thirdRowIsStreak = grid
|> Seq.skip 6
|> Seq.take 3
|> Seq.forall (fun x -> x.Value = true)
firstRowIsStreak |> should equal true
secondRowIsStreak |> should equal false
thirdRowIsStreak |> should equal true
之间会有一个广告(因为每行都需要进行比较)因此,执行速度较慢。另外,正如在其他答案中已经说明的那样,由于演员表,可能无法使用索引。