我们目前有一个LOGS
TABLE,分为多个日志编号(列XLOG
),并在有限的时间范围内访问。
使用群集“自然”主键声明表,其中XLOG
是日志标识符,XDATE
是时间戳,XHW and XCELL
是确保日志事件唯一性的硬件标识符:
CREATE TABLE [dbo].[LOGS](
[XDATE] [datetime] NOT NULL,
[XHW] [nvarchar](3) NOT NULL,
[XCELL] [nvarchar](3) NOT NULL,
[XALIAS] [nvarchar](255) NULL,
[XMESSAGE] [nvarchar](255) NULL,
[XLOG] [int] NOT NULL,
CONSTRAINT [PK_LOG] PRIMARY KEY CLUSTERED ([XLOG] ASC,[XDATE] ASC,[XHW] ASC,[XCELL] ASC)
问题是在使用相同查询访问多个日志时出现了一个糟糕的执行计划(例如下面的示例查询中的XLOG = 1 OR XLOG = 1002
),请求#1:
SELECT TOP 100 XDATE, XHW, XCELL, XMESSAGE, XLOG FROM LOGS
WHERE XDATE > '2012-06-12T00:00:00' AND XDATE < '2012-07-13T08:29:03.250'
AND (XLOG = 1 OR XLOG = 1002)
ORDER BY XDATE DESC, XLOG DESC
编辑:所需的100行不仅来自日志#1,还来自日志,混合,订购日期。这就是两个查询返回的内容。
在测试之前更新了统计数据。
实际执行计划基本上使用聚集索引寻求使用XLOG
和XDATE
上的谓词来获取数百万行数据,(因为我们有XLOG =它可能只获取100个第一行/最后一行)我们按XDATE({0}}
聚集索引查找操作的详细信息:
预期的执行计划为
我尝试重写查询,但除了UNION ALL
之外找不到其他方法。生成的查询返回相同的结果(使用正确的计划!)但感觉过于复杂(并且无法通过XLOG上的JOIN进行调整,但这不是问题)请求#2:
WITH A AS (SELECT TOP 100 XDATE, XHW, XCELL, XMESSAGE, XLOG FROM LOGS
WHERE XDATE > '2012-06-12T00:00:00' AND XDATE < '2012-07-13T08:29:03.250'
AND XLOG = 1
ORDER BY XDATE DESC),
B AS (SELECT TOP 100 XDATE, XHW, XCELL, XMESSAGE, XLOG FROM LOGS
WHERE XDATE > '2012-06-12T00:00:00' AND XDATE < '2012-07-13T08:29:03.250'
AND XLOG = 1002
ORDER BY XDATE DESC)
SELECT TOP 100 * FROM (
SELECT * FROM A
UNION ALL
SELECT * FROM B
) A
ORDER BY XDATE DESC, XLOG DESC
问题:请求#1有什么问题?如何重写/修改,在尝试排序数百万行之前考虑“TOP”?是解决问题所需的另一个索引,HINT或一些额外的统计数据?我有义务像请求#2一样重写查询吗?
编辑:数量上这个表包含十几本日志,有些只有每月一个事件,而其他人每月有数百万个事件。
这种查询最常用于此表(还有其他变体带有额外的过滤器,但它们与此问题无关 - 除了使用请求#2时的复杂性。)
编辑#2:我尝试了将聚集索引更改为(XDATE,XLOG,...)而不是(XLOG,XDATE,...)的解决方案 - nb:此复合主键是这样设计的,因为XLOG列的选择性很低。
我在生产数据库的副本上测试了这个查询,对着只有几千行的日志:查询计划生成了很多I / O(它只过滤掉XLOG=12
中的几行广泛的XDATE
s。所以这个特殊的解决方案还不行。
SELECT TOP 100 XDATE, XHW, XCELL, XMESSAGE, XLOG FROM LOGS
WHERE XDATE > '2012-06-12T00:00:00' AND XDATE < '2012-07-13T08:29:03.250'
AND (XLOG = 12 AND XALIAS LIKE 'KEYWORD%' )
ORDER BY XDATE DESC, XLOG DESC, XHW DESC, XCELL DESC
PS:顺便说一句,我们对 PostgreSQL 9.1 有相同的行为 - 因此它与数据库无关,更可能是错误的查询或错误的表设计。
答案 0 :(得分:1)
问题是数据库不知道你想要的前100行都是XLOG = 1所以必须得到所有可能的XLOGS,然后排序找到前100行。
在第二种情况下,您提供了更多信息或减少了所选行,以便优化器可以使用索引进行排序。
另一种方法是在XDATE DESC,XLOG DESC上创建聚簇索引,然后优化器将知道它不必排序并使主键成为哈希或其他索引。如果这个查询是最常用的查询,那么这个有意义。
答案 1 :(得分:0)
XDate的订单导致问题。数据必须按xdate排序才能获得前100名,这就是为什么你有这种情况。最好的方法是在xdate,xlog.But上有一个索引,这会增加开销。当其他东西不起作用时应该是选项。尝试以下方法。
SELECT TOP 100 XDATE, XHW, XCELL, XMESSAGE, XLOG
into #mytop100
FROM LOGS
WHERE XDATE > '2012-06-12T00:00:00' AND XDATE < '2012-07-13T08:29:03.250'
AND (XLOG = 1)
ORDER BY XDATE DESC
union all
SELECT TOP 100 XDATE, XHW, XCELL, XMESSAGE, XLOG FROM LOGS
WHERE XDATE > '2012-06-12T00:00:00' AND XDATE < '2012-07-13T08:29:03.250'
AND (XLOG = 1002)
ORDER BY XDATE DESC
select TOP 100 XDATE, XHW, XCELL, XMESSAGE, XLOG from #mytop 100 ORDER BY XDATE DESC, XLOG DESC
还可以尝试将主要的SQL与#mytop100一起使用,看看它是否能找到一个好的计划。我打赌它会,但仍然检查。