说,我有一个仅附加审计表,其中包含以下架构:
ColumnX, AuditRowId, AuditDTim
CLUSTERED INDEX ON AuditRowId
如何编写“高性能”SQL Server查询以查找特定COUNT(*)
值之间的AuditDTim
行?
这些是限制:
AuditDTim
。假设legacyDb并且没有DDL访问权。AuditDTim
添加新索引。答案 0 :(得分:2)
您应该能够在AuditRowId上使用两个二进制搜索。
一个用于查找与开始时间关联的ID,另一个用于查找与结束时间关联的ID,然后在这两个Id之间执行范围搜索。
基本思路的一个例子如下:SQL Fiddle(虽然我不保证它没有错误)
DECLARE @AuditDTimStart DATETIME = '2000-01-15',
@AuditDTimEnd DATETIME = '2000-01-20'
IF @AuditDTimEnd < @AuditDTimStart
RAISERROR('Start date after end date',16,1)
DECLARE @AuditRowIdStart INT,
@AuditRowIdEnd INT,
@AuditRowIdMin1 INT,
@AuditRowIdMax1 INT,
@AuditRowIdMin2 INT,
@AuditRowIdMax2 INT
SELECT TOP 1 @AuditRowIdMin1 = AuditRowId,
@AuditRowIdMin2 = AuditRowId,
@AuditRowIdStart = -1 + CASE
WHEN @AuditDTimStart < AuditDTim
THEN AuditRowId
END
FROM YourTable
ORDER BY AuditRowId
SELECT TOP 1 @AuditRowIdMax1 = AuditRowId,
@AuditRowIdMax2 = AuditRowId,
@AuditRowIdEnd = 1 + CASE
WHEN @AuditDTimEnd > AuditDTim
THEN AuditRowId
END
FROM YourTable
ORDER BY AuditRowId DESC
WHILE @AuditRowIdStart IS NULL
BEGIN
-- Binary search to find latest row where AuditDTim < @AuditDTimStart
SELECT TOP 1 @AuditRowIdMax1 = CASE
WHEN AuditDTim >= @AuditDTimStart
THEN AuditRowId
ELSE @AuditRowIdMax1
END,
@AuditRowIdMin1 = CASE
WHEN AuditDTim < @AuditDTimStart
THEN AuditRowId
ELSE @AuditRowIdMin1
END
FROM YourTable
WHERE AuditRowId <= @AuditRowIdMin1 + ( ( @AuditRowIdMax1 - @AuditRowIdMin1 ) / 2 )
ORDER BY AuditRowId DESC
IF @AuditRowIdMax1 - @AuditRowIdMin1 <= 1
SET @AuditRowIdStart = @AuditRowIdMin1;
END
WHILE @AuditRowIdEnd IS NULL
BEGIN
-- Binary search to find earliest row where AuditDTim > @AuditRowIdEnd
SELECT TOP 1 @AuditRowIdMax2 = CASE
WHEN AuditDTim > @AuditDTimEnd
THEN AuditRowId
ELSE @AuditRowIdMax2
END,
@AuditRowIdMin2 = CASE
WHEN AuditDTim <= @AuditDTimEnd
THEN AuditRowId
ELSE @AuditRowIdMin2
END
FROM YourTable
WHERE AuditRowId >= @AuditRowIdMin2 + ( ( @AuditRowIdMax2 - @AuditRowIdMin2 ) / 2 )
ORDER BY AuditRowId ASC
IF @AuditRowIdMax2 - @AuditRowIdMin2 <= 1
SET @AuditRowIdEnd = @AuditRowIdMax2;
END
SELECT *
FROM YourTable
WHERE AuditRowId > @AuditRowIdStart
AND AuditRowId < @AuditRowIdEnd
ORDER BY AuditRowId
答案 1 :(得分:1)
据推测,您可以使用如下查询:
select count(*)
from audi
where auditdtim >= @auditstart and auditdtim <= @auditend;
索引的存在主要影响查询的性能,而不是它的编写方式。
答案 2 :(得分:1)
跟进马丁的回答。尝试使用cte表达式。张贴@ http://sqlfiddle.com/#!6/0270c/9/0。
代码:
--declare @SearchTerm datetime = '2000-01-11 00:00:00';
; with cte as
(
select TOP 1
Iter=convert(int,0),
CurDTim=convert(datetime, null),
CurId=convert(int, -1),
CurMinId=convert(int,-1),
CurMaxId=convert(int,-1),
NextMinId=MIN([AuditRowId]),
NextMaxId=MAX([AuditRowId])
from [YourTable] (nolock) --assume clustered index only on AuditRowId
UNION ALL
select
Iter=Iter+1,
CurDTim=NextDTim,
CurId=(((NextMaxId+NextMinId)/2)+1),
CurMinId=NextMinId,
CurMaxId=NextMaxId,
NextMinId=case when NextDTim<(/*@SearchTerm*/ '2000-01-11 00:00:00' /*@SearchTerm*/) then (((NextMaxId+NextMinId)/2)+1) else NextMinId end,
NextMaxId=case when NextDTim>(/*@SearchTerm*/ '2000-01-11 00:00:00' /*@SearchTerm*/) then (((NextMaxId+NextMinId)/2)+1) else NextMaxId end
from cte t1
inner join (select AuditRowId, NextDTim=AuditDTim from [YourTable] (nolock)) as t2
on [AuditRowId] = (((NextMaxId+NextMinId)/2)+1)
where
NextMinId < NextMaxId
and (((NextMaxId+NextMinId)/2)+1) > NextMinId
and (((NextMaxId+NextMinId)/2)+1) < NextMaxId
and (CurDTim <> (/*@SearchTerm*/ '2000-01-11 00:00:00' /*@SearchTerm*/) or CurDTim is null)
)
select TOP 100 t1.*
from cte t1
order by t1.Iter
;