【问题标题】:SQL request very slow althought index is created尽管创建了索引,但 SQL 请求非常慢
【发布时间】:2016-03-02 10:48:27
【问题描述】:

我的 SQL - Server 上的一些内部连接有一个奇怪的问题。 尽管在内连接和 where 子句中使用的所有列都有索引,但它非常慢。

这是我的 SQL 请求:

SELECT p.VORNA AS firstName,
       p.NACHN AS lastName,
       p.USRID AS [user],
       o.ORGEH AS OUID,
       o.STEXT AS OU,
       a.HolidayDate AS absentFrom,
       a.HolidayDate AS absentUntil,
       k.MessageDate AS actionDate,
       'holiday' AS reason
FROM Kondor_User_Activities AS k
INNER JOIN dbo.SAP_Personaldaten AS p ON p.USRID = k.Code
INNER JOIN Kondor_Users u ON u.Users_Id = k.Users_Id
INNER JOIN SAP_OE AS o ON p.ORGEH = o.ORGEH
INNER JOIN Kondor_UsersGrp AS g ON g.UsersGrp_Id = u.UsersGrp_Id
INNER JOIN Kondor_Cities AS c ON c.Cities_Id = u.Cities_Id OR (u.Cities_Id IS NULL AND c.Cities_Id = g.Cities_Id)
INNER JOIN Kondor_FixedHolidays AS a ON k.MessageDate >= a.HolidayDate
    AND k.MessageDate < a.HolidayDateEnd
    AND a.Cities_Id = c.Cities_Id
--WHERE g.UsersGrp_ShortName NOT LIKE 'UA_%'
WHERE (g.UsersGrp_ShortName < 'UA_' OR g.UsersGrp_ShortName >= 'UA`')

这是我的执行计划:

  |--Compute Scalar(DEFINE:([Expr1018]='holiday'))
   |--Nested Loops(Inner Join, WHERE:([RevisionReport].[dbo].[Kondor_FixedHolidays].[Cities_Id] as [a].[Cities_Id]=[RevisionReport].[dbo].[Kondor_Cities].[Cities_Id] as [c].[Cities_Id] AND [RevisionReport].[dbo].[Kondor_User_Activities].[MessageDate] as [k].[MessageDate]>=[RevisionReport].[dbo].[Kondor_FixedHolidays].[HolidayDate] as [a].[HolidayDate] AND [RevisionReport].[dbo].[Kondor_User_Activities].[MessageDate] as [k].[MessageDate]<[RevisionReport].[dbo].[Kondor_FixedHolidays].[HolidayDateEnd] as [a].[HolidayDateEnd]))
        |--Parallelism(Gather Streams)
        |    |--Hash Match(Inner Join, HASH:([o].[ORGEH])=([p].[ORGEH]), RESIDUAL:([RevisionReport].[dbo].[SAP_Personaldaten].[ORGEH] as [p].[ORGEH]=[RevisionReport].[dbo].[SAP_OE].[ORGEH] as [o].[ORGEH]))
        |         |--Bitmap(HASH:([o].[ORGEH]), DEFINE:([Bitmap1025]))
        |         |    |--Parallelism(Repartition Streams, Hash Partitioning, PARTITION COLUMNS:([o].[ORGEH]))
        |         |         |--Table Scan(OBJECT:([RevisionReport].[dbo].[SAP_OE] AS [o]))
        |         |--Parallelism(Repartition Streams, Hash Partitioning, PARTITION COLUMNS:([p].[ORGEH]), WHERE:(PROBE([Bitmap1025])=TRUE))
        |              |--Hash Match(Inner Join, HASH:([Expr1019])=([p].[USRID]), RESIDUAL:([RevisionReport].[dbo].[SAP_Personaldaten].[USRID] as [p].[USRID]=[Expr1019]))
        |                   |--Parallelism(Repartition Streams, Hash Partitioning, PARTITION COLUMNS:([Expr1019]))
        |                   |    |--Compute Scalar(DEFINE:([Expr1019]=CONVERT_IMPLICIT(nvarchar(25),[RevisionReport].[dbo].[Kondor_User_Activities].[Code] as [k].[Code],0)))
        |                   |         |--Nested Loops(Inner Join, OUTER REFERENCES:([Bmk1000], [Expr1024]) WITH UNORDERED PREFETCH)
        |                   |              |--Nested Loops(Inner Join, OUTER REFERENCES:([u].[Users_Id]) OPTIMIZED)
        |                   |              |    |--Nested Loops(Inner Join, WHERE:([RevisionReport].[dbo].[Kondor_Cities].[Cities_Id] as [c].[Cities_Id]=[RevisionReport].[dbo].[Kondor_Users].[Cities_Id] as [u].[Cities_Id] OR [RevisionReport].[dbo].[Kondor_Users].[Cities_Id] as [u].[Cities_Id] IS NULL AND [RevisionReport].[dbo].[Kondor_Cities].[Cities_Id] as [c].[Cities_Id]=[RevisionReport].[dbo].[Kondor_UsersGrp].[Cities_Id] as [g].[Cities_Id]))
        |                   |              |    |    |--Nested Loops(Inner Join, OUTER REFERENCES:([u].[UsersGrp_Id]))
        |                   |              |    |    |    |--Clustered Index Scan(OBJECT:([RevisionReport].[dbo].[Kondor_Users].[PK_Kondor_Users] AS [u]), ORDERED FORWARD)
        |                   |              |    |    |    |--Clustered Index Seek(OBJECT:([RevisionReport].[dbo].[Kondor_UsersGrp].[PK_Kondor_UsersGrp] AS [g]), SEEK:([g].[UsersGrp_Id]=[RevisionReport].[dbo].[Kondor_Users].[UsersGrp_Id] as [u].[UsersGrp_Id]),  WHERE:([RevisionReport].[dbo].[Kondor_UsersGrp].[UsersGrp_ShortName] as [g].[UsersGrp_ShortName]<'UA_' OR [RevisionReport].[dbo].[Kondor_UsersGrp].[UsersGrp_ShortName] as [g].[UsersGrp_ShortName]>='UA`') ORDERED FORWARD)
        |                   |              |    |    |--Table Spool
        |                   |              |    |         |--Index Scan(OBJECT:([RevisionReport].[dbo].[Kondor_Cities].[IX_Kondor_Cities_Countries_Id] AS [c]))
        |                   |              |    |--Index Seek(OBJECT:([RevisionReport].[dbo].[Kondor_User_Activities].[IX_Kondor_User_Activities_Users_Id] AS [k]), SEEK:([k].[Users_Id]=[RevisionReport].[dbo].[Kondor_Users].[Users_Id] as [u].[Users_Id]) ORDERED FORWARD)
        |                   |              |--RID Lookup(OBJECT:([RevisionReport].[dbo].[Kondor_User_Activities] AS [k]), SEEK:([Bmk1000]=[Bmk1000]) LOOKUP ORDERED FORWARD)
        |                   |--Parallelism(Repartition Streams, Hash Partitioning, PARTITION COLUMNS:([p].[USRID]))
        |                        |--Table Scan(OBJECT:([RevisionReport].[dbo].[SAP_Personaldaten] AS [p]))
        |--Table Scan(OBJECT:([RevisionReport].[dbo].[Kondor_FixedHolidays] AS [a]))

使用索引变得更好,但在获取所有数据时仍然非常非常慢。 也许,有人对我有一些提示,如何在合理的时间内获取所有行。

非常感谢!

桌子大小:

Kondor_FixedHolidays:         14,416 rows
SAP_Personaldaten:            13,001 rows
Kondor_User_Activities:    7,247,086 rows

这也是我的执行计划,我认为错误出在 Kondor_Users_Activities 表中,尽管我在那里有两个索引。也许,聚集索引会比非聚集索引更好?

Query Plan

RID Lookup Properties

【问题讨论】:

  • 是使用的索引,能否粘贴执行计划
  • 您能添加视觉计划吗?更容易阅读。
  • 您对 kondor_fixedholidays messagedate 有索引吗?
  • 是的。说真的,给我们一个 .sqlplan 文件的链接。假设和实际。它不仅更容易,而且比您发布的这篇小文章还包含更多信息。
  • 在任何人考虑尝试提供帮助之前。此人在对我的回答的评论中表示,即使他的查询时间超过 1 小时,他也会在 10 分钟后中止任何答案。

标签: sql sql-server performance


【解决方案1】:

加入不喜欢 OR
你可以用 isnull 去掉一个 OR

SELECT p.VORNA AS firstName, 
       p.NACHN AS lastName, 
       p.USRID AS [USER], 
       o.ORGEH AS OUID, 
       o.STEXT AS OU, 
       a.HolidayDate AS absentFrom, 
       a.HolidayDate AS absentUntil, 
       k.MessageDate AS actionDate,
       'holiday' as reason
  FROM Kondor_User_Activities AS k       
  JOIN Kondor_Users AS u 
        ON u.Users_Id = k.Users_Id 
  JOIN Kondor_UsersGrp AS g 
        ON g.UsersGrp_Id = u.UsersGrp_Id 
       and (   g.UsersGrp_ShortName <  'UA_' 
            OR g.UsersGrp_ShortName >= 'UA`')
  JOIN Kondor_Cities AS c 
        ON c.Cities_Id = isnull(u.Cities_Id, g.Cities_Id)
  JOIN Kondor_FixedHolidays AS a 
        ON a.Cities_Id = c.Cities_Id
       AND k.MessageDate >= a.HolidayDate 
       and k.MessageDate <  a.HolidayDateEnd 

  JOIN dbo.SAP_Personaldaten AS p 
        ON p.USRID = k.Code 
  JOIN SAP_OE AS o 
        ON p.ORGEH = o.ORGEH 

【讨论】:

  • 感谢您的解决方案,但它似乎比以前慢得多。与此同时,我发现 Kondor_Users_Activities 表存在问题,尽管我在那里拥有所有必要的索引,但成本为 91%。执行计划在网站下方。
  • 似乎?你让它跑了吗?您说这需要一个多小时,并在发布此答案后 20 分钟发布了评论。
  • 我在一段时间后停止了它,这是真的 - 因为我们没有数百万行,所以不会超过 1 到 2 分钟。
  • 所以您的查询需要一个多小时。您在 10 分钟内中止了此操作,并得出结论认为它似乎比以前慢了很多。
  • 所以总而言之,您在没有实际测试它是否更快的情况下忽略了它。不是解决性能问题的好方法。更不用说粗鲁了。
【解决方案2】:

我怀疑大部分减速来自此加入INNER JOIN Kondor_Cities AS c ON c.Cities_Id = u.Cities_Id OR (u.Cities_Id IS NULL AND c.Cities_Id = g.Cities_Id)。 SqlServer 不擅长优化OR 谓词。

试试这个:

SELECT  p.VORNA AS firstName, 
        p.NACHN AS lastName, 
        p.USRID AS [USER], 
        o.ORGEH AS OUID, 
        o.STEXT AS OU, 
        x.HolidayDate AS absentFrom, 
        x.HolidayDate AS absentUntil, 
        k.MessageDate AS actionDate,
        'holiday' as reason
FROM        Kondor_User_Activities AS k 
      INNER JOIN dbo.SAP_Personaldaten AS p ON p.USRID = k.Code
        INNER JOIN Kondor_Users u ON u.Users_Id = k.Users_Id 
          INNER JOIN SAP_OE AS o ON p.ORGEH = o.ORGEH 
            INNER JOIN Kondor_UsersGrp AS g ON g.UsersGrp_Id = u.UsersGrp_Id
              INNER JOIN (
                    SELECT DISTINCT a.HolidayDate, a.HolidayDateEnd, a.Cities_Id
                    FROM Kondor_FixedHolidays AS a
              ) x ON k.MessageDate >= x.HolidayDate and k.MessageDate < x.HolidayDateEnd AND x.Cities_Id IN (u.Cities_Id,g.Cities_Id)
WHERE (g.UsersGrp_ShortName < 'UA_' OR g.UsersGrp_ShortName >= 'UA`')

【讨论】:

  • 嗨,由于某些别名不起作用,我无法执行您的 sql:Msg 4104,Level 16,State 1,Line 1 无法绑定多部分标识符“x.Cities_Id”。消息 4104,级别 16,状态 1,行 1 无法绑定多部分标识符“u.Cities_Id”。消息 4104,级别 16,状态 1,行 1 无法绑定多部分标识符“a.HolidayDate”。 Msg 4104, Level 16, State 1, Line 1 无法绑定多部分标识符“a.HolidayDate”。
  • 现在只有 2 个错误。消息 4104,级别 16,状态 1,行 1 无法绑定多部分标识符“g.Cities_Id”。 Msg 4104, Level 16, State 1, Line 1 无法绑定多部分标识符“u.Cities_Id”。
  • 非常感谢,我让它运行,因为我在sql查询“嵌套”时不太好,所以我总是要问这是怎么回事。我只在大学里学会了如何在没有如此嵌套的内部连接的情况下完成它)。我会让你知道它是否比以前更快。当我创建聚集索引而不是非聚集索引时,你认为我会摆脱 91% 的成本吗?
  • 这取决于您计划如何使用表中的数据。例如,如果频繁更新数据,聚集索引会使您的表变慢。有关更多信息,请参阅本文info
  • 这就是你的问题...您的Kondor_User_Activities 表有超过700 万条记录...。尝试添加一个where 原因以从Kondor_User_Activities 表中过滤掉尽可能多的记录。在没有某种过滤器的情况下内部连接 ​​700 万条记录是一个坏主意...
【解决方案3】:

作为一个盲目的远景 - 没有表、关系、索引或任何有用的东西,试试这个:

    SELECT
        UActivities.firstName, 
        UActivities.lastName, 
        UActivities.[USER], 
        UActivities.OUID, 
        UActivities.OU, 
        UserHoliday.HolidayDate AS absentFrom, 
        UserHoliday.HolidayDate AS absentUntil, 
        UActivities.actionDate,
        'holiday' as reason
    FROM (
            SELECT 
                k.MessageDate AS actionDate,
                k.Users_Id,
                k.Code,
                p.VORNA AS firstName, 
                p.NACHN AS lastName, 
                p.USRID AS [USER], 
                o.ORGEH AS OUID, 
                o.STEXT AS OU
            FROM Kondor_User_Activities AS k 
                INNER JOIN dbo.SAP_Personaldaten AS p ON k.Code = p.USRID
                    INNER JOIN SAP_OE AS o ON p.ORGEH = o.ORGEH 
            ) AS UActivities
        INNER JOIN (
            SELECT 
                UserCities.Users_Id,
                a.HolidayDate
            FROM (
                    SELECT 
                        u.Users_Id,
                        ISNULL(u.Cities_Id, g.Cities_Id) AS CityId
                    FROM 
                        Kondor_Users u 
                        INNER JOIN Kondor_UsersGrp AS g ON g.UsersGrp_Id = u.UsersGrp_Id AND (g.UsersGrp_ShortName NOT LIKE 'UA_%')
                    ) AS UserCities
                INNER JOIN Kondor_FixedHolidays AS a ON a.Cities_Id = UserCities.CityId
            ) AS UserHoliday ON UActivities.Users_Id = UserHoliday.Users_Id AND UActivities.actionDate >= UserHoliday.HolidayDate and UActivities.actionDate < UserHoliday.HolidayDateEnd

不知道我是否正确地点击了所有列名,但我认为这里的问题是优化器发疯了,并且由于您的条件复杂,他说:“我要去一行对每个用户活动进行时间测试,看看它是否符合所有其他条件”。

我的工作是按以下顺序分离查询的逻辑部分:
1. 创建用户可以成为的所有城市 - 直接指定或从组继承 (ISNULL(u.Cities_Id, g.Cities_Id))
2.根据从#1获得的城市创建所有假期
3. 将需要的用户数据从其余表中加入。

看看这个结果——也许最好不要创建UActivities 内部查询,而是将这些表从它连接到UserCities 的结果。要确定这一点,我确实需要访问权限 - 但你可以做到。

在运行前检查计划。在阅读方面应该有很大不同并且更加平衡。

【讨论】:

  • 非常感谢您的努力! - 我也会试试的。我也考虑过重新排序我的查询,或者更好地说,我已经这样做了(但不是你提供给我的那种方式)但总是说重新排序可以帮助但不需要帮助,因为编译器(?)正在做所有事情它自己的逻辑 - 我收到了这些错误消息,但我会尝试解决它们:Msg 4104, Level 16, State 1, Line 1 多部分标识符“k.MessageDate”、“a.HolidayDate”、“k.MessageDate” ,a.HolidayDateEnd" 无法绑定。
  • 最后一行有错误 - 我已经更正了。立即尝试。
【解决方案4】:

试试这个 -

CREATE NONCLUSTERED INDEX ix1
ON dbo.Kondor_User_Activities (Code, Users_Id, MessageDate)
GO

SELECT  p.VORNA AS firstName, 
        p.NACHN AS lastName, 
        p.USRID AS [USER], 
        o.ORGEH AS OUID, 
        o.STEXT AS OU, 
        a.HolidayDate AS absentFrom, 
        a.HolidayDateEnd AS absentUntil, 
        k.MessageDate AS actionDate,
        'holiday' as reason
FROM dbo.Kondor_User_Activities k 
JOIN dbo.SAP_Personaldaten p ON p.USRID = k.Code
JOIN dbo.SAP_OE o ON p.ORGEH = o.ORGEH 
JOIN dbo.Kondor_Users u ON u.Users_Id = k.Users_Id 
JOIN dbo.Kondor_UsersGrp g ON g.UsersGrp_Id = u.UsersGrp_Id
JOIN dbo.Kondor_FixedHolidays a ON k.MessageDate >= a.HolidayDate AND k.MessageDate < a.HolidayDateEnd AND a.Cities_Id = g.Cities_Id
WHERE (g.UsersGrp_ShortName < 'UA_' OR g.UsersGrp_ShortName >= 'UA`')
    AND EXISTS(
        SELECT 1
        FROM Kondor_Cities c
        WHERE c.Cities_Id IN (u.Cities_Id, g.Cities_Id)
        --WHERE c.Cities_Id = ISNULL(u.Cities_Id, g.Cities_Id)
    )

【讨论】:

  • 感谢您的所有提示和选择,但它仍在获取行。一旦我有一个执行计划,我会发布它。我的 SELECT 仍在运行,所以它可能需要更长的时间。
  • @user3003944 只需添加ix1 即可避免RID Lookup
猜你喜欢
  • 2012-04-01
  • 2013-02-21
  • 1970-01-01
  • 2018-07-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-10
  • 2020-01-17
相关资源
最近更新 更多