【问题标题】:Using indexes when comparing datetimes比较日期时间时使用索引
【发布时间】:2019-08-06 21:54:21
【问题描述】:

我有两个表,都包含数百万行数据。

tbl_one:
purchasedtm DATETIME,
userid      INT,
totalcost   INT

tbl_two:
id          BIGINT,
eventdtm    DATETIME,
anothercol  INT

第一个表的前两列有一个聚集索引:CLUSTERED INDEX tbl_one_idx ON(purchasedtm, userid)

第二个在其 ID 列上有一个主键,在 eventdtm 列上也有一个非聚集索引。

我想运行一个查询来查找purchasedtmeventdtm 在同一天的行。

最初,我将查询写为:

WHERE CAST(tbl_one.purchasedtm AS DATE) = CAST(tbl_two.eventdtm AS DATE)

但这不会使用两个索引中的任何一个。

后来,我将查询更改为:

WHERE tbl_one.purchasedtm >= CAST(tbl_two.eventdtm AS DATE)
AND tbl_one.purchasedtm < DATEADD(DAY, 1, CAST(tbl_two.eventdtm AS DATE))

这样,因为只有比较的一侧被包裹在一个函数中,另一侧仍然可以使用它的索引。对吗?

我还有一些其他问题:

  • 我也可以用另一种方式编写查询,即保持tbl_two.eventdtm 不变并将tbl_one.purchasedtm 包装在CAST() 中。这会对性能产生影响吗?
  • 如果上一个问题的答案是肯定的,是不是因为eventdtm 有自己的专用索引,而查找purcahsedtm 只会是部分索引匹配?
  • 在决定这两种选择中哪一种更好时,我还可以考虑其他因素吗? (例如,如果 tbl_one 有数百万行,而 tbl_two 有数十亿行,这会影响我应该 CAST 哪一列,不应该 CAST 哪一列?)
  • 一般来说,如果您比较两个都被索引的列,与只有其中一个被索引的类似情况相比,我们是否会获得任何性能?
  • 最后,我可以在不使用 CAST 的情况下执行我的原始任务吗?

注意:我没有创建或修改索引、添加列等的能力。

【问题讨论】:

  • WHERE CAST(tbl_one.purchasedtm AS DATE) = CAST(tbl_two.eventdtm AS DATE) “但这不会使用两个索引中的任何一个。” 错误。 CAST([column] AS date) SARGable/ SARGable functions in SQL Server
  • 理想情况下,用户 ID 将是表 1 的 PK,并且您将拥有另一个包含所有购买的表。你在 IT 领域加入这些吗?如果是这样,交换该集群键的顺序会有所帮助。
  • 您修改后的查询是否使用了索引?
  • @Larnu 我不知道!很有意思。我会阅读更多内容并更新我的问题。但是你能在非 SARGable 的上下文中回应它吗? (例如,如果我使用的 CAST 不是 SRGable,或者不是 CAST 的函数)
  • 大部分时间any 函数应用于WHERE 中的列将使其不可搜索。我能想到的唯一一个实际上是 SARGable 是CAST({column},AS date)。我不记得我的头顶,但我认为CONVERT(int,DecimalColumn) 是SARGable。你(我)看到的最常见的东西是 WHERE ISNULL(MyColumn,0) = ISNULL(@MyVariable,0) 之类的东西,它不是 SARGable。使用布尔逻辑WHERE (MyColumn = @MyVariable OR (MyColumn IS NULL AND @MyVariable IS NULL)) 编写类似的东西会更好。

标签: sql-server datetime indexing casting sql-server-2016


【解决方案1】:

很少。评论后迟到了,但是...

正如 cmets 中所讨论的,CAST(DateTimeColumn AS date) 之类的代码实际上是 SARGable。 Rob Farley 发表了一篇关于 SARGable 和非 SARGable 功能的文章 here,不过,我还是会介绍一些内容。

首先,将函数应用于列通常会使您的查询不可搜索,特别是如果它改变了值的顺序或它们的顺序毫无意义。采取类似的方式:

SELECT *
FROM TABLE
WHERE RIGHT(COLUMN,5) = 'value';

列中值的顺序在这里完全没有帮助,因为我们关注的是右手字符。不幸的是,正如 Rob 所讨论的那样:

SELECT *
FROM TABLE
WHERE LEFT(COLUMN,5) = 'value';

这也是非 SARGable。但是下面的呢?

SELECT *
FROM TABLE
WHERE Column LIKE 'value%';

这是因为逻辑未应用于列并且顺序不会改变。如果值是 '%value%',那么它也是非 SARGable。

在应用添加(或减去)您想要查找的内容的逻辑时,您总是希望将其应用于文字值(或函数,如 GETDATE()`)。例如,这些表达式之一是 SARGable,另一个不是:

Column + 1  = @Variable --non-SARGable
Column = @Variable - 1 --SARGable

这同样适用于DATEADD之类的东西

@DateVariable BETWEEN DateColumn AND DATEADD(DAY, 30,DateColumn) --non-SARGable
DateColumn BETWEEN DATEADD(DAY, -30, @DateVariable) AND @DateVariable --SARGable

很少更改数据类型(date 除外)会使查询保持 SARGable。 CONVERT(date,varchardate,112) 不会是 SARGable,即使列的顺序没有改变。然而,将decimal 转换为int 与将datetime 转换为date 具有相同的结果,并且保持了SARGability:

CREATE TABLE testtab (n decimal(2,1) PRIMARY KEY CLUSTERED);
INSERT INTO testtab
VALUES(0.1),
      (0.3),
      (1.1),
      (1.7),
      (2.4);
GO

SELECT n
FROM testtab
WHERE CONVERT(int,n) = 2;
GO    

DROP TABLE testtab;

希望这足以让您继续下去,但请询问您是否希望我进一步添加任何内容。

【讨论】:

    猜你喜欢
    • 2021-01-19
    • 2014-05-02
    • 2023-03-08
    • 1970-01-01
    • 1970-01-01
    • 2021-11-05
    • 2023-04-06
    • 2015-07-20
    相关资源
    最近更新 更多