2

在数据库上产生负载后,我们的一个查询降级了。

我们的查询是 3 个表之间的连接:

  1. Base包含 10 M 行的表。
  2. EventPerson包含 5000 行的表。
  3. EventPerson788这是空的。

似乎优化器扫描索引EventPerson而不是搜索,这是用于复制问题的脚本:

--Create Tables 
CREATE TABLE [dbo].[BASE](
        [ID] [bigint] NOT NULL,
        [IsActive] BIT
       PRIMARY KEY CLUSTERED ([ID] ASC) 
)ON [PRIMARY]
GO

CREATE TABLE [dbo].[EventPerson](
    [DUID] [bigint] NOT NULL,
    [PersonInvolvedID] [bigint] NULL,

PRIMARY KEY CLUSTERED ([DUID] ASC)
) ON [PRIMARY]
GO

CREATE NONCLUSTERED INDEX [EventPerson_IDX] ON [dbo].[EventPerson]
(
    [PersonInvolvedID] ASC
)

CREATE TABLE [dbo].[EventPerson788](
    [EntryID] [bigint] NOT NULL,
    [LinkedSuspectID] [bigint] NULL,
    [sourceid] [bigint] NULL,

    PRIMARY KEY CLUSTERED ([EntryID] ASC)
) ON [PRIMARY]
GO

ALTER TABLE [dbo].[EventPerson788] WITH CHECK 
ADD CONSTRAINT [FK7A34153D3720F84A] 
FOREIGN KEY([sourceid]) REFERENCES [dbo].[EventPerson] ([DUID])
GO

ALTER TABLE [dbo].[EventPerson788] CHECK CONSTRAINT [FK7A34153D3720F84A]
GO

CREATE NONCLUSTERED INDEX [EventPerson788_IDX] 
ON [dbo].[EventPerson788] ([LinkedSuspectID] ASC)
GO

--POPOLATE BASE TABLE 
DECLARE @I BIGINT=1 
WHILE (@I<10000000) 
    BEGIN
    begin transaction
    INSERT INTO BASE(ID) VALUES(@I) 
    SET @I+=1
        if (@I%10000=0 )
        begin
    commit;
        end;

    END
go

--POPOLATE EventPerson TABLE 
DECLARE @I BIGINT=1 
WHILE (@I<5000) 
    BEGIN
    BEGIN TRANSACTION
    INSERT INTO EventPerson(DUID,PersonInvolvedID) VALUES(@I,(SELECT TOP 1 ID FROM BASE ORDER BY NEWID())) 
        SET @I+=1
        IF(@I%10000=0 )
            COMMIT TRANSACTION ;
    END

GO 

这是查询:

select 
    count(EventPerson.DUID) 
from 
    EventPerson  
inner loop join 
    Base on EventPerson.DUID = base.ID 
left outer join 
    EventPerson788 on EventPerson.DUID = EventPerson788.sourceid 
where 
    (EventPerson.PersonInvolvedID = 37909 or 
     EventPerson788.LinkedSuspectID = 37909) 
    AND BASE.IsActive = 1

你知道为什么优化器决定使用索引扫描而不是索引搜索吗?

已经完成的解决方法:

  • 分析表并建立统计数据。
  • 重建指数。
  • 尝试 FORCESEEK 提示

EventPerson以上都没有说服优化器在基表 上运行索引查找和查找。

谢谢你的帮助 。

4

2 回答 2

2

由于or条件和外部连接,扫描在那里EventPerson788

它会从EventPerson何时EventPerson.PersonInvolvedID = 37909 EventPerson788何时返回行where 中存在行EventPerson788.LinkedSuspectID = 37909。最后一部分意味着EventPerson必须根据连接检查每一行。

EventPerson788查询优化器不能使用空的事实,因为查询计划被保存以供以后在EventPerson788.

更新:

您可以使用 union all 重写您的查询,而不是 or 来获取 seek in EventPerson

select count(EventPerson.DUID) 
from 
  (
    select EventPerson.DUID
    from EventPerson
    where EventPerson.PersonInvolvedID = 1556 and
          not exists (select * 
                      from EventPerson788 
                      where EventPerson788.LinkedSuspectID = 1556) 
    union all
    select EventPerson788.sourceid
    from EventPerson788
    where EventPerson788.LinkedSuspectID = 1556
  ) as EventPerson  
  inner join BASE  
    on EventPerson.DUID=base.ID 
where
  BASE.IsActive=1

在此处输入图像描述

于 2013-08-11T13:47:58.643 回答
1

好吧,您是在要求 SQL Server计算表的行数-EventPerson那么为什么您希望在此处查找比扫描更好呢?

对于 a COUNT,SQL Server 优化器几乎总是使用扫描- 毕竟它需要对行进行计数 - 所有这些......如果没有其他非可空列被索引,它将执行聚集索引扫描。

如果您在一个小的、不可为空的列上(例如在 aID INT或类似的东西上)有一个索引,它可能会改为对该索引进行扫描(要读取的数据更少以计算所有行)。

但总的来说:seek非常适合选择一行或几行- 但如果您要处理所有行(比如计数),那就太糟糕了

如果您使用AdventureWorks示例数据库,您可以轻松观察到这种行为。

在具有超过 120000 行COUNT(*)的表上执行如下操作时:Sales.SalesOrderDetail

SELECT COUNT(*) FROM Sales.SalesOrderDetail

然后你会得到一个索引扫描IX_SalesOrderDetail_ProductID- 它只是没有回报对超过 120000 个条目进行搜索!

但是,如果您对较小的数据集执行相同的操作,如下所示:

SELECT COUNT(*) FROM Sales.SalesOrderDetail
WHERE ProductID = 897

然后你从所有这些中取回 2 行 - SQL Server 现在将在同一个索引上使用索引搜索

于 2013-08-11T13:13:07.133 回答