【问题标题】:Is there a more efficient way to do this join?有没有更有效的方法来做这个加入?
【发布时间】:2013-06-04 06:19:50
【问题描述】:

我需要加入两个表,第一个表包含 CustomerNumber 和 IdentificationNumber,以及 IdentificationType。第二个表包含 IdentificationType、EffectiveDate 和 EndDate。

我的查询基本上是这样的:

Select CustomerNumber, IdentificationNumber
From Identification i
Inner Join IdentificationType it On it.IdentificationType = i.IdentificationType
And it.EffectiveDate < @TodaysDate
And (it.EndDate IS NULL Or it.EndDate > @TodaysDate)

我的执行计划显示了对标识类型表的聚集索引扫描,我假设这是因为连接子句中的 OR。

有没有更有效的加入方式,知道 EndDate 字段必须允许 Null 或真实的日期时间值?

【问题讨论】:

  • 什么版本的 SQL Server?
  • 能否请您出示执行计划?
  • 对不起,SQL Server 2008R2

标签: sql sql-server


【解决方案1】:

我知道您说过EndDate 列必须允许NULL,因此仅作记录:最有效的方法是停止使用NULLs 代替IdentificationType 表中的“无结束日期” , 而是使用9999-12-31。然后您的查询可以跳过整个OR 子句。 (我知道这可能需要对应用程序进行一些更改,但出于这个确切原因,我认为这是值得的——而且我已经看到这种“NULL = 开放式”模式使查询变得困难或表现不佳在我自己的工作和在线 SQL 问题中一遍又一遍。)

另外,您可以考虑交换两个OR 条件的顺序——这听起来像是巫术,但我相信我听说有一些特殊情况可以更好地优化在这种特定情况下,变量是第一位的(尽管我可能是错的)。

与此同时,您会尝试这个并分享与您的解决方案和其他解决方案相比它的性能如何?

SELECT
   CustomerNumber, IdentificationNumber
FROM
   dbo.Identification i
   INNER JOIN dbo.IdentificationType it
      ON it.IdentificationType = i.IdentificationType
WHERE
   it.EffectiveDate < @TodaysDate
   AND it.EndDate IS NULL
UNION ALL
SELECT
   CustomerNumber, IdentificationNumber
FROM
   dbo.Identification i
   INNER JOIN dbo.IdentificationType it
      ON it.IdentificationType = i.IdentificationType
WHERE
   it.EffectiveDate < @TodaysDate
   AND it.EndDate > @TodaysDate
;

通过使用这种精确的策略,我已经从 OR 子句的糟糕表现中恢复过来。查询大小/复杂性爆炸是很痛苦的,但与您现在处理的扫描相比,仅仅获得几次搜索的可能性是完全值得的。

你的不等式比较有些可疑:第一个比较应该有一个等号&lt;=。您没有告诉我们日期列和@TodaysDate 的数据类型,但最佳做法是设计一个系统,使其不会因任何输入而失败。因此,即使变量是 datetime 并且 EffectiveDate 没有时间部分,在该比较中它仍然应该是 &lt;=,因此恰好在午夜的查询不会包含当天的数据。

附:很抱歉没有保留您的格式——我只是在以我喜欢的样式格式化时更好地理解查询。此外,我将日期条件移至WHERE 子句,因为在我看来它们不属于JOIN

【讨论】:

  • 我可以考虑尝试实现这种方法,但必须在我们的代码库上进行大量搜索以搜索所有“EndDate”列,并实现一个默认值(DateTime.MaxValue)如果什么都没有通过。我会开始搜索,看看会有多难。
  • 再次感谢埃里克。仅供参考 - 我们所有的日期列都是 DateTime 格式,以及 @TodaysDate 变量。我正在努力调整这个存储过程,并考虑在整个系统范围内实现这个。
  • 我强烈建议您对可用的所有不同方法(我的答案和其他方法)的性能进行一些测试。这应该会让你现在不清楚的某些方面暴露出来。
【解决方案2】:

尝试使用 isnull 代替 OR 语句。我也认为您应该使用 Datediff 而不是比较运算符。

select CustomerNumber, IdentificationNumber
From Identification i
Inner Join IdentificationType it On it.IdentificationType = i.IdentificationType
And it.EffectiveDate < @TodaysDate
And (isnull(it.EndDate,@TodaysDate) >= @TodaysDate)

【讨论】:

  • 我很好奇为什么人们不赞成这个答案。使用 ISNULL 函数查询是否执行得不是很好?这听起来对我来说是一个很好的答案,没有任何测试......
  • 一方面,请注意 &gt;&gt;= 的更改,这将 EndDate 更改为 inclusive 而不是 exclusive:这与问题中给出的逻辑条件不同。如果您进行性能测试,也许您可​​以与我们分享结果。
猜你喜欢
  • 2014-04-13
  • 1970-01-01
  • 2015-02-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-01-11
  • 1970-01-01
相关资源
最近更新 更多