【问题标题】:SQL Server Query filter with order is slowSQL Server 查询过滤器的顺序很慢
【发布时间】:2020-03-29 09:01:14
【问题描述】:

我已经为此苦苦挣扎了一段时间。

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

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])

为了排除速度慢是因为选择的列的可能性,我只选择了一个固定值

以下查询运行速度非常快(不到 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 和两个过滤器才会出现问题

【问题讨论】:

  • sort=1 是普通过滤器吗?过滤索引将对此有所帮助。请注意,您在这里使用的 EAV 数据库以性能和可伸缩性问题而闻名
  • sort=1 不是常用过滤器,按排序过滤很常见,但值不常见,我不明白的是它只是普通连接,查询运行速度非常快,有几个条件但使用 order by 和两个过滤器会减慢速度
  • 它使用了不同的查询计划。当您观察它们并尝试找出它为什么这样做时,您通常可以修复它。通常只是更新统计数据就可以解决问题。有时问题是参数嗅探
  • 好的,只是重读,它已缩小到order by code?代码不是任何索引中的前导列,所以我猜顺序(总是一个昂贵的操作)是问题所在。聚集索引通常会按性能问题修复顺序,但您只会得到一个。
  • 我必须再次评论 EAV 设计 guid pk。对于开发人员来说,这似乎是一个绝妙的想法,但对于 DBA 来说却不是。

标签: sql sql-server indexing query-performance


【解决方案1】:

尝试索引

  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 比连接过滤更多。但我不知道数据,这个假设可能是错误的。

【讨论】:

  • 我可能弄错了,但你能解释一下为什么我需要索引 #1,因为它应该已经存在,因为 [Id] 是 PK 和聚集索引?,也是索引 #3,为什么会我需要在索引中包含 PK 列吗?它不是已经包含在所有索引中了吗?假设,查询运行速度很快,没有 order by
  • @AhmedMSedeek:我无法(并且仍然无法)在您的帖子中找到该信息。但无论如何,我不知道你是否需要,你必须自己测试它。我不能这样做,因为我没有数据和一切。它认为它可能比聚集索引更好地帮助连接,因为它也按[EntityLevel_Id] 排序。然后,正如我所提到的,在更改订单时,([EntityLevel_Id], [Id]) 可能是更好的选择。聚集索引根本不涵盖这一点。
【解决方案2】:

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

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 Server 无法使用索引进行排序。您或许可以使用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 引擎使用索引进行排序(在其他情况下似乎是这样)。当然,您不能从 EntityLevel 中选择任何列——但您的示例查询无论如何都不会这样做。

【讨论】:

    【解决方案3】:

    对于任何担心的人来说,这是解决问题的方法:

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

    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])
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-02-09
      • 1970-01-01
      • 1970-01-01
      • 2012-11-23
      相关资源
      最近更新 更多