SQL 服务器查询过滤器的顺序很慢

SQL Server Query filter with order is slow

我为此苦恼了一段时间。

我有一个包含三个表(每个表都有数百万条记录)的数据库,如下(为简单起见删除了一些列):

1.[Entity]
    [Id]                UNIQUEIDENTIFIER PK,
    [EntityLevel_Id]    UNIQUEIDENTIFIER NOT NULL FK [EntityLevel] ([Id])
2.[EntityData]
    [Id]                UNIQUEIDENTIFIER PK,
    [Entity_Id]         UNIQUEIDENTIFIER NOT NULL FK [Entity] ([Id]),
    [DataLanguage_Id]   UNIQUEIDENTIFIER NOT NULL FK [Language] ([Id]),
    [Code]              NVARCHAR (250) NOT NULL
3.[EntityLevel]
    [Id]                UNIQUEIDENTIFIER PK,
    [Sort]              INT NOT NULL

存在索引如下

[IX_Entity_EntityLevelId] ON [Entity] ([EntityLevel_Id])
[IX_EntityData_EntityId] ON [EntityData] ([Entity_Id])
[IX_EntityData_DataLanguageId_Code] ON [EntityData] ([DataLanguage_Id], [Code])
[IX_EntityLevel_Sort] ON [EntityLevel] ([Sort])

为了消除缓慢是因为 selected 列的可能性,我只 select 一个固定值

以下查询运行速度非常快(不到 1 秒):

SELECT TOP 20
    1
FROM
    [Entity]
    INNER JOIN [EntityData] ON [Entity].[Id] = [EntityData].[Entity_Id]
    INNER JOIN [EntityLevel] ON [Entity].[EntityLevel_Id] = [EntityLevel].[Id]
WHERE
    [EntityData].[DataLanguage_Id] = 'B6930015-F177-4ED3-97B0-AAEF401F9265'

以下查询也运行得非常快:

SELECT TOP 20
    1
FROM
    [Entity]
    INNER JOIN [EntityData] ON [Entity].[Id] = [EntityData].[Entity_Id]
    INNER JOIN [EntityLevel] ON [Entity].[EntityLevel_Id] = [EntityLevel].[Id]
WHERE
    [EntityData].[DataLanguage_Id] = 'B6930015-F177-4ED3-97B0-AAEF401F9265'
ORDER BY
    [EntityData].[Code] ASC

下面的也运行得很快:

SELECT TOP 20
    1
FROM
    [Entity]
    INNER JOIN [EntityData] ON [Entity].[Id] = [EntityData].[Entity_Id]
    INNER JOIN [EntityLevel] ON [Entity].[EntityLevel_Id] = [EntityLevel].[Id]
WHERE
    [EntityLevel].[Sort] = 1

但是,以下查询运行非常慢(大约 10 秒):

SELECT TOP 20
    1
FROM
    [Entity]
    INNER JOIN [EntityData] ON [Entity].[Id] = [EntityData].[Entity_Id]
    INNER JOIN [EntityLevel] ON [Entity].[EntityLevel_Id] = [EntityLevel].[Id]
WHERE
    [EntityData].[DataLanguage_Id] = 'B6930015-F177-4ED3-97B0-AAEF401F9265'
    AND
    [EntityLevel].[Sort] = 1
ORDER BY
    [EntityData].[Code]

我不知道原因,也找不到任何方法来应用更多索引以使查询运行得更快

感谢任何帮助!

编辑: 以下查询也运行得很快:

SELECT TOP 20
    1
FROM
    [Entity]
    INNER JOIN [EntityData] ON [Entity].[Id] = [EntityData].[Entity_Id]
    INNER JOIN [EntityLevel] ON [Entity].[EntityLevel_Id] = [EntityLevel].[Id]
WHERE
    [EntityData].[DataLanguage_Id] = 'B6930015-F177-4ED3-97B0-AAEF401F9265'
    AND
    [EntityLevel].[Sort] = 1

SELECT TOP 20
    1
FROM
    [Entity]
    INNER JOIN [EntityData] ON [Entity].[Id] = [EntityData].[Entity_Id]
    INNER JOIN [EntityLevel] ON [Entity].[EntityLevel_Id] = [EntityLevel].[Id]
WHERE
    [EntityLevel].[Sort] = 1
ORDER BY
    [EntityData].[Code]

问题只出现在 order by 和两个过滤器上

上尝试索引
  1. [Entity] ([Id], [EntityLevel_Id]),
  2. [EntityData] ([DataLanguage_Id], [Entity_id], [Code])
  3. [EntityLevel] ([Sort], [Id]).

试验列的顺序。对于 2. 和 3. 我假设 WHERE 子句中的 [EntityData].[DataLanguage_Id] = 'B6930015-F177-4ED3-97B0-AAEF401F9265'[EntityLevel].[Sort] = 1 比连接过滤得更多。但我不知道数据,这个假设可能是错误的。

你的问题是关于这个查询的:

SELECT TOP 20 1
FROM [Entity] JOIN
     [EntityData]
     ON [Entity].[Id] = [EntityData].[Entity_Id] JOIN
     [EntityLevel]
     ON [Entity].[EntityLevel_Id] = [EntityLevel].[Id]
WHERE [EntityData].[DataLanguage_Id] = 'B6930015-F177-4ED3-97B0-AAEF401F9265' AND
      [EntityLevel].[Sort] = 1
ORDER BY [EntityData].[Code];

我认为问题是 SQL 服务器无法使用索引进行排序。您可以使用 EXISTS:

来解决这个问题
SELECT TOP 20 1
FROM Entity e JOIN
     EntityData ed
     ON e.Id = ed.Entity_Id 
WHERE ed.DataLanguage_Id = 'B6930015-F177-4ED3-97B0-AAEF401F9265' AND          
      EXISTS (SELECT 1
              FROM EntityLevel el
              WHERE e.EntityLevel_Id = el.Id AND
                    el.Sort = 1
             )
ORDER BY ed.Code;

对于此版本,您需要 EntityLevel(ID, Sort) 上的索引。

可能 允许 SQL 引擎使用索引进行排序(就像在其他情况下一样)。当然,您不能 select 来自 EntityLevel 的任何列——但是您的示例查询无论如何都不会这样做。

对于任何关心的人,这是问题的解决方案:

事实证明,有那么多行,由于索引不同,需要时间进行键查找和哈希匹配,因此解决方案是添加以下索引以允许在 table 对于所有过滤器(和排序依据)值:

CREATE NONCLUSTERED INDEX [IX_EntityData_EntityId_DataLanguageId_IncCode] ON [EntityData] ([Entity_Id], [DataLanguage_Id]) INCLUDE ([Code])

但是由于我在某些情况下也可能会按代码进行过滤,而不仅仅是使用order by,所以我将索引修改为这样:

CREATE NONCLUSTERED INDEX [IX_EntityData_EntityId_DataLanguageId_Code] ON [EntityData] ([Entity_Id], [DataLanguage_Id], [Code])