我试图在下面的场景中找出性能最佳的查询。
下表非常大,超过500万行。每天都会发生一些负载,尽管确切的变化次数。每个负载由load_id(聚集索引的一部分)标识,load_datetimestamp是适用于该负载的时间戳。每个载荷插入约30,000行。
CREATE TABLE [VeryLargeTable](
[load_id] [int] NOT NULL,
[acct_cd] [varchar](20) NOT NULL,
[acct_num] [varchar](255) NULL,
[prod_id] [varchar](50) NOT NULL,
[domestic_foreign_cd] [varchar](3) NOT NULL,
[vendor_prod_id] [varchar](15) NULL,
[prod_name] [varchar](100) NULL,
[currency_cd] [varchar](3) NULL,
[total_Qty] [int] NULL,
[mkt_price] [float] NULL,
[load_datetimestamp] [datetime] NULL,
CONSTRAINT [pk_VeryLargeTable] PRIMARY KEY CLUSTERED (
[load_id] ASC,
[acct_cd] ASC,
[prod_id] ASC,
[domestic_foreign_cd] ASC )
)
在任何一个晚上,我想从今天的第一次加载获得所有行。在上面的DDL中,查询必须尽可能高效。显然,你不想开始使用" WHERE datediff(day,load_datetimestamp,getDate())= 0"在整个桌子上。
我写了这两个查询。有人比另一个好吗?我知道两者都会返回相同的结果。你能否建议一个比这两个中的任何一个更好的?
查询1
With
T1 as (select
load_id,
load_datetimestamp
from dbo.VeryLargeTable
group by
load_id,load_datetimestamp),
T2 as (select
load_id,
load_datetimestamp
from T1
where
datediff(day,load_datetimestamp,getDate())=0),
T3 as (select min(load_id) as loadID from T2)
select * from dbo.VeryLargeTable
where load_id = (select loadID from T3)
查询2
declare @found tinyint;
declare @loadID int;
declare @dateTimeStamp datetime;
-- Get max value of load id
select @loadID = max(load_id) from [dbo].[VeryLargeTable];
-- Keep looping until previous day is found or minimum load_id is reached
set @found = 0;
WHILE (@found=0)
BEGIN
select @dateTimeStamp = load_datetimestamp from [dbo].[VeryLargeTable] where load_id=@loadID;
if (@loadID=0) SET @found=1
else
BEGIN
if (DATEPART(day, @dateTimeStamp) = DATEPART(day, GetDate())) SET @loadID = @loadID - 1;
else SET @found=1;
END
END
SELECT * from [dbo].[VeryLargeTable] where load_id=(@loadID + 1);
答案 0 :(得分:3)
1)正如评论中提到的那样,不要使用像datediff(day,load_datetimestamp,getDate())=0)
这样的表达式。此表达式不能使用列load_datetimestamp
上的索引。
使用load_datetimestamp >= CAST(CAST(GETDATE() AS date) AS datetime)
。
2)在列load_datetimestamp
上创建索引。
3)找到今天加载的第一行并从中获取load_id
:
SELECT TOP(1) load_id
FROM dbo.VeryLargeTable
WHERE load_datetimestamp >= CAST(CAST(GETDATE() AS date) AS datetime)
ORDER BY load_datetimestamp
此查询应该是load_datetimestamp
上的索引的即时搜索。检查执行计划。
4)在load_id
列上创建索引。
5)使用找到的load_id
返回该加载的所有行:
WITH
CTE_FirstLoad
AS
(
SELECT TOP(1) load_id
FROM dbo.VeryLargeTable
WHERE load_datetimestamp >= CAST(CAST(GETDATE() AS date) AS datetime)
ORDER BY load_datetimestamp
)
SELECT *
FROM dbo.VeryLargeTable
WHERE load_id IN (SELECT load_id FROM CTE_FirstLoad)
;
这应该使用load_id
上的索引。
我已经编辑了答案,并将IN
代替=
放在最后WHERE
中。使用IN
即使今天没有任何负载,查询也会起作用。 =
最有可能出现错误。
现在我们知道您无法在此表上创建新索引。我们知道每天的负载数量很少(大约5)。
有了这个限制,很可能你的第二个带有显式循环的查询将是最快的。无论如何,你必须尝试真实系统上的所有建议并测量它们的性能。
我们可以假设每个负载在同一天开始和结束吗?换句话说,对于每个load_id
,load_datetimestamp
的日期是相同的?
然后,您可以尝试对您的循环进行一些调整。它们可能不会改变或根本不改变性能,但你必须衡量。
而不是:
select @loadID = max(load_id) from [dbo].[VeryLargeTable];
尝试:
SET @loadID =
(select TOP(1) load_id from [dbo].[VeryLargeTable] ORDER BY load_id DESC);
在循环中以相同的方式(我怀疑当前行实际上是有效的,因为你有相同的load_id
的许多行)而不是:
select @dateTimeStamp = load_datetimestamp from [dbo].[VeryLargeTable] where load_id=@loadID;
尝试:
SET @dateTimeStamp =
(select TOP(1) load_datetimestamp from [dbo].[VeryLargeTable] where load_id=@loadID);
我假设负载ID会随着时间的推移而增加,即ID=N
的负载比load_datetimestamp
的负载少ID=N+1
。
使用Loads
上的主要唯一聚集索引列load_id
创建一个单独的表load_id
。
此表格包含每天最后load_id
行的一行。它会比主表小得多。
此表的目的是快速找到“今天的第一个load_id”而不是扫描大表。
每天晚上运行报告时,这个小Loads
表格会包含前几天的加载行,但不会包含今天的行。
您能否说,当您在晚上运行报告时,当天所有装货都已完成?如果是的话:
从小表中获取前几天的最后一次加载:
SET @PrevLoadID =
(SELECT TOP(1) load_id FROM Loads ORDER BY load_id DESC)
从大表中获取今天的第一个负载:
SET @TodayFirstLoadID =
(SELECT TOP(1) load_id
FROM VeryLargeTable
WHERE VeryLargeTable.load_id > @PrevLoadID
ORDER BY load_id ASC)
从大表中获取今天的最后一次加载:
SET @TodayLastLoadID =
(SELECT TOP(1) load_id
FROM VeryLargeTable
ORDER BY load_id DESC)
INSERT
将@TodayLastLoadID
加入Loads
。
使用@TodayFirstLoadID
运行主报告:
SELECT *
FROM VeryLargeTable
WHERE load_id = @TodayFirstLoadID
答案 1 :(得分:1)
这里你不需要3个CTE。您可以通过简单地选择满足过滤条件的Load ID和Load DateTimeStamp的所有不同组合来替换前2个CTE。然后,您可以通过将该检查直接移动到最终选择中的子查询来摆脱第三个CTE。生成的查询如下所示:
;With
T2 as
(select distinct
load_id,
load_datetimestamp
from dbo.VeryLargeTable
where datediff(day,load_datetimestamp,getDate())=0)
select * from dbo.VeryLargeTable
where load_id = (select min(load_ID) from T2)
答案 2 :(得分:0)
您需要在执行计划中检查它,但如果我是您,我会使用临时表而不是使用3个表表达式并将其与原始表连接。
同样如前所述,最好不要在WHERE
子句中使用函数。相反,您可以使用date BETWEEN start AND end