【问题标题】:Slow SQL SELECT for specific key values特定键值的慢速 SQL SELECT
【发布时间】:2015-02-02 21:13:51
【问题描述】:

为什么这两个查询在时间上有这么大的差异?

此查询大约需要 51 秒才能完成:

SELECT TOP 1000 * 
FROM [PHONER].[dbo].[V_PhonerSubjects]
WHERE ProjectID = 137;

但是,此查询大约需要 1 秒才能完成:

SELECT TOP 1000 * 
FROM [PHONER].[dbo].[V_PhonerSubjects]
WHERE ProjectID = 107;

注意:唯一的区别是键的值。

两者的记录总数均超过 1000 条。项目 107 有 26000 条记录,项目 137 有 4500 条记录。

ProjectID 是索引主键。

我注意到第二个查询在 1 秒内完成并将所有行发送到结果窗口。第一个 Query 大约 3 秒后开始发送行,大约 51 秒后完成。

这是视图V_PhonerSubjects的SQL:

SELECT        
   dbo.PhonerEmner.PhonerEmID AS SubjectID, dbo.PhonerEmner.FK_ProID AS ProjectID, 
   dbo.PhonerProjekt.PhonerTitel AS ProjectName, dbo.Medlemsdata.Vennenr AS FriendNo, 
   dbo.Medlemsdata.OpretteDato AS CreatedDate, dbo.Medlemsdata.OpretteID AS CreatedID, 
   dbo.Bruger.Intialer AS CreatedBy, dbo.Medlemsdata.ÆndretDato AS ChangedDate, 
   dbo.Medlemsdata.ÆndretID AS ChangedID,  
   Bruger_3.Intialer AS ChangedBy, 
   ISNULL(dbo.Medlemsdata.Organisation, N'') + N' ' + ISNULL(dbo.Medlemsdata.Fornavn, N'') + N' ' + ISNULL(dbo.Medlemsdata.Efternavn, N'') AS Name, 
   dbo.MedlemsAdresse.AdrID AS AddressID, dbo.MedlemsAdresse.Adresse AS Address1, 
   dbo.MedlemsAdresse.Adresse2 AS Address2, dbo.MedlemsAdresse.Postnr AS ZIP, 
   dbo.Postnumre.[By] AS City, dbo.Medlemsdata.CPRnr AS CPRno, 
   dbo.Medlemsdata.Køn AS Gender, dbo.Medlemsdata.Telefon AS Phone01, 
   dbo.Medlemsdata.TlfNote1 AS Phone02Type, dbo.Medlemsdata.Tlf1 AS Phone02, 
   dbo.Medlemsdata.TlfNote2 AS Phone03Type, dbo.Medlemsdata.Tlf2 AS Phone03, 
   dbo.Medlemsdata.TlfNote3 AS Phone04Type, 
   dbo.Medlemsdata.Tlf3 AS Phone04, dbo.Medlemsdata.TlfSMS AS PhoneMobile, 
   dbo.Medlemsdata.[E-mail] AS Email, dbo.Medlemsdata.SPFelt1 AS SPField01,
   dbo.Medlemsdata.SPFelt2 AS SPField02, dbo.Medlemsdata.SPFelt3 AS SPField03, 
   dbo.Medlemsdata.SPFelt4 AS SPField04, dbo.Medlemsdata.SPFelt5 AS SPField05, 
   dbo.Medlemsdata.SPFelt6 AS SPField06, dbo.Medlemsdata.SPFelt7 AS SPField07, 
   dbo.Medlemsdata.SPFelt8 AS SPField08, dbo.Medlemsdata.SPFelt9 AS SPField09, 
   dbo.Medlemsdata.SPFelt10 AS SPField10, dbo.Medlemsdata.SPFelt11 AS SPField11, 
   dbo.Medlemsdata.SPFelt12 AS SPField12, dbo.Medlemsdata.SPFelt13 AS SPField13, 
   dbo.Medlemsdata.SPFelt14 AS SPField14, dbo.PhonerEmner.SidsteKontakt AS LastContact, 
   dbo.PhonerEmner.AntalKontakt AS ContactTimes, dbo.PhonerEmner.KontaktDage AS ContactDays, 
   dbo.PhonerEmner.KontaktEfter AS ContactAfter, 
   dbo.PhonerEmner.PhonerIgen AS ContactAfterPhonerID, 
   Bruger_1.Navn AS ContactAfterPhonerName, dbo.PhonerEmner.PhonerNote, 
   dbo.PhonerEmner.Stemning AS Mood, dbo.PhonerEmner.Status, 
   dbo.PhonerEmner.PhonerAft AS LastPhonerID, Bruger_2.Navn AS LastPhonerName, 
   dbo.PhonerEmner.SlutNote AS EndNote, dbo.PhonerEmner.SlutDato AS EndDate, 
   dbo.PhonerImport.PhonerImportID AS ImportID, dbo.PhonerImportData.Status AS ImportStatus, 
   dbo.PhonerImport.ImportFileName, dbo.PhonerImport.ImportTime, 
   dbo.PhonerProjekt.SvarerIkkeTid
FROM            
   dbo.Bruger AS Bruger_1 
RIGHT OUTER JOIN
   dbo.PhonerProjekt 
RIGHT OUTER JOIN
   dbo.PhonerEmner ON dbo.PhonerProjekt.PhonerProID = dbo.PhonerEmner.FK_ProID 
LEFT OUTER JOIN
   dbo.PhonerImportData ON dbo.PhonerEmner.PhonerEmID = dbo.PhonerImportData.FK_PhonerEmID 
LEFT OUTER JOIN
   dbo.Bruger AS Bruger_2 ON dbo.PhonerEmner.PhonerAft = Bruger_2.BrugerID ON Bruger_1.BrugerID = dbo.PhonerEmner.PhonerIgen 
LEFT OUTER JOIN
   dbo.Bruger 
RIGHT OUTER JOIN
   dbo.Bruger AS Bruger_3 
RIGHT OUTER JOIN
   dbo.Medlemsdata ON Bruger_3.BrugerID = dbo.Medlemsdata.ÆndretID ON dbo.Bruger.BrugerID = dbo.Medlemsdata.OpretteID ON dbo.PhonerEmner.FK_Vennenr = dbo.Medlemsdata.Vennenr 
LEFT OUTER JOIN
   dbo.Postnumre 
RIGHT OUTER JOIN
   dbo.MedlemsAdresse ON dbo.Postnumre.Postnummer = dbo.MedlemsAdresse.Postnr ON dbo.Medlemsdata.FK_AdrID = dbo.MedlemsAdresse.AdrID 
LEFT OUTER JOIN
   dbo.PhonerImport ON dbo.PhonerImportData.FK_PhonerImportID = dbo.PhonerImport.PhonerImportID

项目 107 的客户统计数据:

Client Execution Time   14:04:24        
Query Profile Statistics            
  Number of INSERT, DELETE and UPDATE statements    0       0.0000
  Rows affected by INSERT, DELETE, or UPDATE statements 0       0.0000
  Number of SELECT statements   2       2.0000
  Rows returned by SELECT statements    1001        1001.0000
  Number of transactions    0       0.0000
Network Statistics          
  Number of server roundtrips   3       3.0000
  TDS packets sent from client  3       3.0000
  TDS packets received from server  241     241.0000
  Bytes sent from client    340     340.0000
  Bytes received from server    976874      976874.0000
Time Statistics         
  Client processing time    95      95.0000
  Total execution time  391     391.0000
  Wait time on server replies   296     296.0000

项目 137 的客户统计数据:

Client Execution Time   13:58:28        
Query Profile Statistics            
  Number of INSERT, DELETE and UPDATE statements    0       0.0000
  Rows affected by INSERT, DELETE, or UPDATE statements 0       0.0000
  Number of SELECT statements   2       2.0000
  Rows returned by SELECT statements    1001        1001.0000
  Number of transactions    0       0.0000
Network Statistics          
  Number of server roundtrips   3       3.0000
  TDS packets sent from client  3       3.0000
  TDS packets received from server  217     217.0000
  Bytes sent from client    340     340.0000
  Bytes received from server    877700      877700.0000
Time Statistics         
  Client processing time    129596      129596.0000
  Total execution time  130297      130297.0000
  Wait time on server replies   701     701.0000

【问题讨论】:

  • 如果您分别为两个语句提供查询执行计划,这可能会有所帮助。在 SQL Management Studio 中,选择“包括实际执行计划”的图标(或使用 Ctrl + M 切换),并在查询完成后获取执行计划,该执行计划将在查询结果窗格。对于每个查询,这些执行计划是什么样的?
  • 统计数据是否过时?在执行计划中检查估计的行与返回的实际行
  • 请发布2个查询计划。
  • 您有以下评论:“ProjectID 是索引主键。”但是,在视图V_PhonerSubjects 中返回的ProjectID 值是dbo.PhonerEmner.FK_ProID AS ProjectID,我假设它不是主键,而是返回dbo.PhonerProjekt 表的外键。如果在视图中将dbo.PhonerEmner.FK_ProID AS ProjectID 更改为dbo.PhonerProjekt.PhonerProID AS ProjectID,有帮助吗?
  • 你说得对,“fk_”表示外键。但是,它仍然是主键的一部分。它是由 Vennenr 和 FK_ProID 组成的共享密钥的一部分。因此,一位朋友可以参与多个项目。但每个项目只有一次。不过我确实尝试了你的建议,但没有效果。

标签: sql sql-server performance select sql-server-2012


【解决方案1】:

我发现了问题。

我更改了加入。对我来说,不管我是用一种方式还是另一种方式,但显然它确实做到了。

而不是将 PhonerEmner 加入 PhonerEmID = FK_PhonerEmID 上的 PhonerImportData。我在 Vennenr = FK_Vennenr 上将 MedlemsData 加入到 PhonerImportData。它一定与 PhonerEmID 不是主键而只是索引有关。

【讨论】:

    【解决方案2】:

    您复杂的左右连接混乱,以及查询的非常不相关的可读性很可能是问题的一部分。我可能是完全错误的并接受这一点。但是,我根据从每个相应表到下一个表的所有分层连接条件重组了您的查询......每个表“JOIN”ed 和“ON”条件直接作为关联与混合。在这里,您可以按层次结构查看表与获取必要详细信息的关系。我还使用 ALIASES 来提高原始帖子的可读性。

    因此,您似乎有一些电话项目已加入到 PhonerEmner 表中。基于这些元素,您正试图提取尽可能多的查找辅助数据。如果它存在,很好,得到它,但如果在子级相关表中没有找到这样的记录,请不要停止。这样,它们都是 LEFT JOIN。如果您希望在任何给定表中都需要记录,只需将 LEFT JOIN 更改为 INNER JOIN,和/或添加 WHERE 子句以确保找到给定的 alias.ID 列。

    说了这么多,我会确保表有索引来正确优化连接...

    table            index
    PhonerEmner      (FK_ProID, PhonerIgen, PhonerEmID, PhonerAft, FK_Vennenr
    PhonerProjekt    (PhonerProID)
    Bruger           (BrugerID
    PhonerImportData (FK_PhonerEmID, FK_PhonerImportID
    PhonerImport     (PhonerImportID
    Medlemsdata      (Vennenr, OpretteID, ÆndretID, FK_AdrID )
    MedlemsAdresse   (AdrID, Postnr)
    Postnumre        (Postnummer)
    

    并提出了这个修改后的查询,您可以将其创建为新的视图以进行测试。

    SELECT
          PE.PhonerEmID AS SubjectID, 
          PE.FK_ProID AS ProjectID, 
          PP.PhonerTitel AS ProjectName, 
          MED.Vennenr AS FriendNo, 
          MED.OpretteDato AS CreatedDate, 
          MED.OpretteID AS CreatedID, 
          B.Intialer AS CreatedBy, 
          MED.ÆndretDato AS ChangedDate, 
          MED.ÆndretID AS ChangedID,  
          B3.Intialer AS ChangedBy, 
          ISNULL(MED.Organisation, N'') 
             + N' ' + ISNULL(MED.Fornavn, N'') 
             + N' ' + ISNULL(MED.Efternavn, N'') AS Name, 
          ADR.AdrID AS AddressID, 
          ADR.Adresse AS Address1, 
          ADR.Adresse2 AS Address2, 
          ADR.Postnr AS ZIP, 
          PST.[By] AS City, 
          MED.CPRnr AS CPRno, 
          MED.Køn AS Gender, 
          MED.Telefon AS Phone01, 
          MED.TlfNote1 AS Phone02Type, 
          MED.Tlf1 AS Phone02, 
          MED.TlfNote2 AS Phone03Type, 
          MED.Tlf2 AS Phone03, 
          MED.TlfNote3 AS Phone04Type, 
          MED.Tlf3 AS Phone04, 
          MED.TlfSMS AS PhoneMobile, 
          MED.[E-mail] AS Email, 
          MED.SPFelt1 AS SPField01,
          MED.SPFelt2 AS SPField02, 
          MED.SPFelt3 AS SPField03, 
          MED.SPFelt4 AS SPField04, 
          MED.SPFelt5 AS SPField05, 
          MED.SPFelt6 AS SPField06, 
          MED.SPFelt7 AS SPField07, 
          MED.SPFelt8 AS SPField08, 
          MED.SPFelt9 AS SPField09, 
          MED.SPFelt10 AS SPField10, 
          MED.SPFelt11 AS SPField11, 
          MED.SPFelt12 AS SPField12, 
          MED.SPFelt13 AS SPField13, 
          MED.SPFelt14 AS SPField14, 
          PE.SidsteKontakt AS LastContact, 
          PE.AntalKontakt AS ContactTimes, 
          PE.KontaktDage AS ContactDays, 
          PE.KontaktEfter AS ContactAfter, 
          PE.PhonerIgen AS ContactAfterPhonerID, 
          B1.Navn AS ContactAfterPhonerName, 
          PE.PhonerNote, 
          PE.Stemning AS Mood, 
          PE.Status, 
          PE.PhonerAft AS LastPhonerID, 
          B2.Navn AS LastPhonerName, 
          PE.SlutNote AS EndNote, 
          PE.SlutDato AS EndDate, 
          PI.PhonerImportID AS ImportID, 
          PID.Status AS ImportStatus, 
          PI.ImportFileName, PI.ImportTime, 
          PP.SvarerIkkeTid
       FROM
          dbo.PhonerEmner PE
             LEFT JOIN dbo.PhonerProjekt PP
                ON PE.FK_ProID = PP.PhonerProID 
             LEFT JOIN dbo.Bruger B1 
                ON PE.PhonerIgen = B1.BrugerID
             LEFT JOIN dbo.PhonerImportData PID 
                ON PE.PhonerEmID = PID.FK_PhonerEmID
                LEFT JOIN dbo.PhonerImport PI 
                   ON PID.FK_PhonerImportID = PI.PhonerImportID
             LEFT JOIN dbo.Bruger B2 
                ON PE.PhonerAft = B2.BrugerID 
             LEFT JOIN dbo.Medlemsdata MED 
                ON PE.FK_Vennenr = MED.Vennenr 
                LEFT JOIN dbo.Bruger B
                   ON MED.OpretteID = B.BrugerID
                LEFT JOIN dbo.Bruger B3 
                   ON MED.ÆndretID = B3.BrugerID
                LEFT JOIN dbo.MedlemsAdresse ADR 
                   ON MED.FK_AdrID = ADR.AdrID 
                   LEFT JOIN dbo.Postnumre PST
                      ON ADR.Postnr = PST.Postnummer
    

    【讨论】:

    • 我实施了您的建议,并确保已建立上述索引。完全没有变化。可读性低的原因是由于Microsoft SQL Management Studio的视图设计器。
    • @JackW.Jensen,我了解设计师试图为您思考。您是否将其作为一个单独的视图并从中查询...或者您是否创建了第二个视图但从原来的看法。听起来很糟糕,但有时我什至会脑抽筋:)
    • 它是一个作为搜索工具基础的视图。搜索工具构建 where 条件并从视图中检索结果。
    • 大声笑我才明白你的评论。我确实针对新视图进行了测试;-)
    【解决方案3】:

    鉴于行数越少运行速度越慢,我怀疑是数据内容,而不是架构。

    如果一个数据集在大多数 OUTER JOIN 上找到匹配项,而另一个没有,这可能会导致运行时的某些差异。加入时,未命中只会 ping 索引,但命中会导致表读取。

    【讨论】:

    • 这也是我的第一个怀疑。但它更多的是相反的方式。例如项目 107 有 35 个关联的导入文件,而项目 137 只有 3 个。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-23
    • 2016-01-10
    • 2016-02-05
    • 1970-01-01
    • 2023-03-15
    • 1970-01-01
    相关资源
    最近更新 更多