14

我有以下 sql 查询,只需 1 秒即可执行:

select a.date, b.rate, c.type, a.value from

a inner join b on a.id = b.aid
c inner join b.id = c.bid
where a.name = 'xxx'

但是我需要一个结果集来获得比率大于 0 的结果。因此,当我将查询更改为此时,需要 7 分钟才能执行:

select a.date, b.rate, c.type, a.value from

a inner join b on a.id = b.aid
c inner join b.id = c.bid
where a.name = 'xxx' and b.rate>0

为什么这会使查询时间从 1 秒增加到 7 分钟?由于 b 表很大,我什至尝试使用 CTE,但这也没有提高性能。我认为使用 CTE 会有更小的一组值可供过滤,因此它应该更快,但这并没有帮助:

;with x as
(select a.date, b.rate, c.type, a.value from

a inner join b on a.id = b.aid
c inner join b.id = c.bid
where a.name = 'xxx')
select * from x where rate>0

我不能包含执行计划,因为除了查询之外我没有对 db 的权限。

4

6 回答 6

16

我的猜测是,缓慢的执行计划正在rate>0以一种不幸的方式进行过滤,例如作为循环连接内部扫描的一部分或其他东西。

如果归根结底,一种解决方案是存储中间结果集并在单独的语句中对其进行过滤。

我建议这样做,因为您无法更改供应商的数据库并且您基本上被卡住了。这实质上是从优化器中剥夺了一些控制权——这是您通常不想做的事情——并且还通过创建临时表增加了相对少量的开销。但它应该缓解这种情况下的缓慢。如果可能,我将继续与您的供应商合作制定索引策略。

select a.date, b.rate, c.type, a.value 
into #t
from a inner join b on a.id = b.aid
c inner join b.id = c.bid
where a.name = 'xxx' 

select * from #t where rate>0
于 2012-07-12T17:32:50.860 回答
1

首先让我解释一下为什么你有一个糟糕的计划。一件事是我现在能想到的。当没有 rate.The table a 是第一个并且基于 sarg 和 index 说它估计有 10000 行。这可能使用了合并连接。现在与表 b 连接(截至目前,假设存在 1 到 n 映射,并且在 avg 上,每行表格 a 有 2 行)。然后连接后的估计行数为 20000,然后它与表 C 连接。现在基于表 C 的大小,它可以使用说合并连接。假设实际行数为 100

但是当您添加 SARG rate > 0。然后优化器在 A 和 B 连接后没有估计 20K 行而是估计 6667 行(当没有 auto_create_statistics 时,默认为 30% > 0)然后它可能选择了说与表 C 的嵌套循环连接而不是合并连接。但实际行可能更多,因此嵌套循环连接可能已用于 100 的数千个,因此最终的嵌套循环连接可能会花费这么多时间。

总之,我想说的是,由于额外的 sarg,优化器的估计不正确,因此是一个糟糕的计划。

您根本不在这里创建索引。索引不是每件事的解决方案,拥有它们的开销太多了,特别是在您的情况下,限制较少的查询运行得更好,所以问题与索引无关,而是它更多地与统计数据有关。检查以下

您的数据库的 auto_create_statistics 是打开还是关闭?或者表 b 具有列速率的统计信息?何时为这些表更新/创建统计信息?如果不是首先为该列创建统计信息,我相信您的计划将是正常的。如果您无法创建统计信息,那么请尝试强制执行与您获得的相同计划,但 rate > 0 CTE 不会提高代码性能,因此可能会出现异常。这些是为了使代码更具可读性。

于 2012-07-14T12:02:00.973 回答
0

ratename列上创建索引& 你会看到很大的不同。

编辑:

由于列属于两个不同的表,因此需要两个单独的索引

编辑:

索引指南

于 2012-07-12T17:03:08.343 回答
0

如果您试图告诉服务器如何使用 CTE 优化您的查询,我认为您是在自欺欺人。使用索引您将获得更好的结果。但无论如何,您似乎已经将 CTE 部分放在了头上。您想要求服务器首先执行 CTE,然后评估外部查询。或许像这样...

;with x as (Select a.id, a.date, a.value from a where a.name='xxx')
Select x.date, b.rate, c.type, x.value
From x
  Inner join b on x.id=b.aid
  Inner Join c on b.id=c.bid
where b.rate>0

或者

;with x as (
   Select b.id, b.rate, c.type 
   From b 
     Inner Join c on b.id=c.bid
   where b.rate>0)
Select a.date, x.rate, x.type, a.value
From a
  Inner join x on a.id=b.id
Where a.name='xxx'
于 2012-07-12T17:09:49.970 回答
0

您可以通过强制合并连接将优化器发送到正确的路径:

select ... from TableA left merge join TableB on ...

于 2019-09-04T18:56:47.793 回答
-2

该部分b>0彻底改变了它,因为服务器无法再使用聚集索引和其他方法优化查询。当您添加时b>0,将此查询转换为范围查询,这将需要更多时间,因为它确实必须检查更多值。我相信有一些优化已经到位,例如针对此类查询的 B-tree 组织,但它不会对其进行太大改进。

于 2012-07-12T16:54:43.123 回答