使用“WHERE A = 1 OR A = 2子句”SELECT查询不良执行计划

时间:2012-07-17 09:35:21

标签: sql-server sql-server-2008 sql-server-2005 postgresql sql-execution-plan

我们目前有一个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,还来自日志,混合,订购日期。这就是两个查询返回的内容。

在测试之前更新了统计数据。

实际执行计划基本上使用聚集索引寻求使用XLOGXDATE上的谓词来获取数百万行数据,(因为我们有XLOG =它可能只获取100个第一行/最后一行)我们按XDATE({0}}

订购

聚集索引查找操作的详细信息:actual plan

预期的执行计划为tooltip

我尝试重写查询,但除了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

expected plan

PS:顺便说一句,我们对 PostgreSQL 9.1 有相同的行为 - 因此它与数据库无关,更可能是错误的查询或错误的表设计。

2 个答案:

答案 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一起使用,看看它是否能找到一个好的计划。我打赌它会,但仍然检查。

相关问题