目前,我们有一个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
and XDATE
(因为我们有 XLOG= 并且我们按 XDATE 排序,所以它可能只获取 100 首/最后一行)
聚集索引查找操作的详细信息:
预期的执行计划是
我试图重写查询,但除了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”?是否需要另一个索引、提示或一些额外的统计信息来解决问题?我有义务重写请求 #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中也有同样的行为——所以它与数据库无关,更可能是错误的查询或错误的表设计。