我有两个表,两个表都包含数百万行数据。
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
列上也有一个非聚集索引。
我想运行一个查询,以查找purchasedtm
和eventdtm
在同一天的行。
我最初将查询写为:
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哪一列,我不应该哪一列?)注意:我没有创建或修改索引,添加列等的功能。
答案 0 :(得分:0)
可以在两个仅包含日期部分的表中创建一个持久化的计算列:
purchasedt AS CAST(purchasedtm AS DATE)
eventdt AS CAST(eventdtm AS DATE)
并在其上创建索引。
关于原始查询:SQL Server 可以对此进行翻译:
WHERE CAST(tbl_one.purchasedtm AS DATE) = CAST(tbl_two.eventdtm AS DATE)
类似于以下内容:
WHERE tbl_one.purchasedtm BETWEEN -- first ms of tbl_two.eventdtm
AND -- last ms of tbl_two.eventdtm
但是在您的情况下(i)必须在tbl_two内为数百万行计算该值(ii)在循环内必须执行范围扫描。 SQL Server可能不使用索引。
建立索引的日期列将导致相等比较且不进行转换。
答案 1 :(得分:0)
小。评论后很晚,但是...
如评论中所述,诸如CAST(DateTimeColumn AS date)
之类的代码实际上是可保存的。罗伯·法利(Rob Farley)发表了一些有关SARGable和Non-SARGable功能here的文章,但是,我还是要讲一些事情。
首先,将函数应用于列通常会使您的查询不可SARG,尤其是如果它更改了值的顺序或它们的顺序是没有意义的。采取类似的东西:
SELECT *
FROM TABLE
WHERE RIGHT(COLUMN,5) = 'value';
在此列中值的顺序完全无济于事,因为我们关注的是右侧字符。不幸的是,正如Rob还讨论的那样:
SELECT *
FROM TABLE
WHERE LEFT(COLUMN,5) = 'value';
这也是非SARG。但是,接下来呢?
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
)会使查询保持可保存状态。 CONVERT(date,varchardate,112)
将不会被保存,即使该列的顺序没有变化。但是,将decimal
转换为int
的结果与将datetime
转换为date
的结果相同,并且保持了可保存性:
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;
希望这能给您足够的帮助,但是请让我问您是否要我进一步添加任何内容。