如何使用嵌套循环加速查询

时间:2014-01-16 17:38:04

标签: sql-server sql-server-2008-r2 query-optimization

我有这个查询有效,但很慢

SELECT
  ID_NODE,
  -- this case slows down the query!!!
  CASE WHEN (EXISTS (SELECT MV.ID_CHILD FROM MYVIEW MV INNER JOIN MYTABLE1 MT1 ON MT1.ID_NODE = MV.ID_CHILD WHERE MV.ID_PARENT = CA.ID_NODE AND ID_FATHER IS NOT NULL)) THEN 'Y' ELSE 'N' END AS HAVE_CHILDREN,
  OTHER_FIELDS
FROM
  MYTABLE2 

更新:在第一个回答后,我意识到我的示例并不完美,所以我修改了它做了2次更改(CAMT1,然后写{{1} }而不是MT1.ID_FATHER

ID_FATHER

更新结束

基本上我想要一个 SELECT ID_NODE, -- this case slows down the query!!! CASE WHEN (EXISTS (SELECT MV.ID_CHILD FROM MYVIEW MV INNER JOIN MYTABLE1 MT1 ON MT1.ID_NODE = MV.ID_CHILD WHERE MV.ID_PARENT = MT2.ID_NODE AND MT1.ID_FATHER IS NOT NULL)) THEN 'Y' ELSE 'N' END AS HAVE_CHILDREN, OTHER_FIELDS FROM MYTABLE2 结果“这个节点有孩子吗?

在执行计划中,我只看到一个警告:

  

嵌套循环(内连接))43%

您能否建议改进查询?

作为exterme解决方案,我可以将'y'/'n'值存储在表中作为新字段,但我不喜欢这样,因为它是“通向错误的公路”。

Bounty注意事项:

我在这里发布原始表,视图(用CREATE语句制作)和查询以帮助提供回复:

HAVE_CHILDREN

3 个答案:

答案 0 :(得分:2)

SELECT
  CA.ID_CESPITE,
  CASE WHEN child.[Count] > 0 THEN 'Y' ELSE 'N' END AS HAVE_CHILD_PRG,
  CA.ID_CESPITE_PADRE
FROM
  CES_ANAGRAFICA CA
LEFT OUTER JOIN CES_PERMESSI CP
  ON ((CA.ID_CESPITE = CP.ID_CESPITE))
INNER JOIN CES_TIPI_CESPITE CTCS
  ON CA.ID_TIPO_CESPITE = CTCS.ID_TIPI_INFRSTR
LEFT OUTER JOIN V_UTENTI_DIPENDENTI VUD
  ON CA.ID_RESPONSABILE = VUD.ID_DIPENDENTE
CROSS APPLY (
  SELECT [Count] = COUNT(*)
  FROM V_CESPITE_TREE VCA
  JOIN MAN_PRG_OPERAZIONI MPO
    ON MPO.ID_CESPITE = VCA.ID_CHILD
  WHERE VCA.ID_PARENT = CA.ID_CESPITE
    AND MPO.ID_FATHER is not null
) child

这应该稍微好一点,因为它不必执行聚合函数。

SELECT
  CA.ID_CESPITE,
  CASE WHEN child.[Exists] = 1 THEN 'Y' ELSE 'N' END AS HAVE_CHILD_PRG,
  CA.ID_CESPITE_PADRE
FROM
  CES_ANAGRAFICA CA
LEFT OUTER JOIN CES_PERMESSI CP
  ON ((CA.ID_CESPITE = CP.ID_CESPITE))
INNER JOIN CES_TIPI_CESPITE CTCS
  ON CA.ID_TIPO_CESPITE = CTCS.ID_TIPI_INFRSTR
LEFT OUTER JOIN V_UTENTI_DIPENDENTI VUD
  ON CA.ID_RESPONSABILE = VUD.ID_DIPENDENTE
OUTER APPLY (
  SELECT TOP (1) 1 [Exists]
  FROM V_CESPITE_TREE VCA
  JOIN MAN_PRG_OPERAZIONI MPO
    ON MPO.ID_CESPITE = VCA.ID_CHILD
  WHERE VCA.ID_PARENT = CA.ID_CESPITE
    AND MPO.ID_FATHER is not null
) child

答案 1 :(得分:0)

使用CTE;它会加载你的内连接一次,然后缓存它。

注意我不知道CA.ID_NODE来自哪里,因为你没有解释。 你的内部查询也加入了MyTable1,但是你没有关联MyTable2和子查询。

假设您提供的内容,伪代码应该是这样的: (如果您使用相关信息更新您的问题,我会更新此答案以反映它。)

<强>更新

这是基于您的架构更新和一些示例数据的更新版本。我确认这不再导致重复。真正的问题是你检查它并确保它提高了性能,又降低了执行时间。

; with hasChildCte(ID_CESPITE, ID_PARENT)
As (
        SELECT VCA.ID_CHILD,
            vca.ID_PARENT
        FROM V_CESPITE_TREE VCA 
          INNER JOIN MAN_PRG_OPERAZIONI MPO 
            ON MPO.ID_CESPITE = VCA.ID_CHILD 
        WHERE ID_FATHER 
            IS NOT NULL
)

Select
    CA.ID_CESPITE,
    Case 
        When Exists (
            Select ID_PARENT 
            From hasChildCte cte 
        Where cte.ID_PARENT = ca.ID_CESPITE
        ) Then 'Y'
        Else 'N'
    End As HAVE_CHILDREN,
    CA.ID_CESPITE_PADRE
From CES_ANAGRAFICA CA

另请注意,如果您没有在所有已加入的列上都有索引,那么将它们放入其中将是一个很好的举措。这将有助于进一步加快查询速度,尤其是如果您正在使用大量的数据

更新2

关于CTE执行不止一次的评论让我思考,显然由SQL服务器决定是否缓存CTE,而不是总是缓存。在许多情况下,CTE只会执行一次,但有时候它与SQL服务器中的视图类似,并且不会被缓存。

因此,我修改了代码以使用table variable代替。但是,我没有足够的测试数据来查看哪种表现更好或更快。

试试这个,看看它是否会产生更快的查询执行时间。另请注意,无论您选择哪种重构方法和性能改进,最好使用JOIN中使用的列上的索引正确设置数据库。这会显着增加查询执行时间,而且必须更新索引的插入成本很低。

更新的非CTE代码,改为使用表变量:

Declare @HasChildren table (ID_CESPITE int, ID_PARENT int)

Insert into @HasChildren
Select VCA.ID_CHILD,
    vca.ID_PARENT
From V_CESPITE_TREE VCA 
    Inner Join MAN_PRG_OPERAZIONI MPO 
    On MPO.ID_CESPITE = VCA.ID_CHILD 
Where ID_FATHER 
    Is Not Null

Select
    CA.ID_CESPITE,
    Case 
        When Exists (
            Select ID_PARENT 
            From @HasChildren c
        Where c.ID_PARENT = ca.ID_CESPITE
        ) Then 'Y'
        Else 'N'
    End As HAVE_CHILDREN,
    CA.ID_CESPITE_PADRE
From CES_ANAGRAFICA CA

答案 2 :(得分:0)

优化器可以更轻松地处理以下内容。

SELECT DISTINCT
  ID_NODE,
  CASE WHEN MTSub.ID_CHILD is null then 'N' else 'Y' END,
 OTHERFIELDS
FROM MYTABLE2
LEFT JOIN  
(SELECT MV.ID_CHILD FROM MYVIEW MV INNER JOIN MYTABLE1 MT1 ON MT1.ID_NODE = MV.ID_CHILD WHERE MV.ID_PARENT = MT2.ID_NODE AND MT1.ID_FATHER IS NOT NULL) MTSub