SQL将相关子查询转换为JOIN

时间:2016-12-22 00:51:28

标签: sql sql-server

以下 T-SQL 查询在子查询的where子句中使用SubmittedBy.PartyId上的相关子查询。

此代码位于存储过程中,其中@SpecificBusinessGroup和@SpecificBusinessCategory是传递给存储过程的变量。如果变量作为null传入,则查询需要几秒钟才能运行。如果变量包含数据,则此查询需要一个多小时才能运行。这就是为什么我确定这个问题是针对这个相关子查询的原因。我需要帮助将其转换为JOIN的派生表,但是我很难过。

非常感谢任何帮助。非常感谢。

SELECT *

FROM 
    PremiumFinanceLoan pfl
    LEFT JOIN CustomerAccount c ON pfl.CustomerAccountId = c.PartyId 
    LEFT JOIN Entity SubmittedBy ON c.SubmittedById = SubmittedBy.PartyId
    LEFT JOIN ...

WHERE
    (@SpecificBusinessGroup IS NULL OR @SpecificBusinessCategory IS NULL OR EXISTS         
    (          
        select null    -- That way we will get one row for each group          
        from EntityBusinessCategoryMap as EBC         
            join BusinessCategory as BC on BC.BusinessCategoryId = EBC.BusinessCategoryId           
            join BusinessGroup as BG on BG.BusinessGroupId = BC.BusinessGroupId         
        where    
        (@SpecificBusinessGroup IS NULL OR          
            ( EBC.EntityId = SubmittedBy.PartyId AND BG.BusinessGroupId = @SpecificBusinessGroup))           
        AND (@SpecificBusinessCategory IS NULL OR          
            ( EBC.EntityId = SubmittedBy.PartyId AND BC.BusinessCategoryId = @SpecificBusinessCategory))             
    )) 

以下是我的解决方案,但我认为它不正确,我认为它不等同于相关子查询:

SELECT *

FROM 
    PremiumFinanceLoan pfl
    LEFT JOIN CustomerAccount c ON pfl.CustomerAccountId = c.PartyId 
    LEFT JOIN Entity SubmittedBy ON c.SubmittedById = SubmittedBy.PartyId

    LEFT JOIN 
    (
        select ebc.EntityId
        from EntityBusinessCategoryMap ebc
            join BusinessCategory bc on bc.BusinessCategoryId = ebc.BusinessCategoryId
            join BusinessGroup bg on bg.BusinessGroupId = bc.BusinessGroupId
        where
            (@SpecificBusinessGroup is null or bg.BusinessGroupId = @SpecificBusinessGroup)
            and (@SpecificBusinessCategory is null or bc.BusinessCategoryId = @SpecificBusinessCategory)
        group by ebc.EntityId
    ) gr on gr.EntityId = SubmittedBy.PartyId

    LEFT JOIN ...   

WHERE
    AND gr.EntityId IS NOT NULL -- Equivalent to EXISTS from the correlated subquery ???

3 个答案:

答案 0 :(得分:0)

您可以尝试使用UNION使用两个查询,然后您不需要使用GROUP BY

LEFT JOIN 
(
    select ebc.EntityId 
    from EntityBusinessCategoryMap ebc
        left join BusinessCategory bc on bc.BusinessCategoryId = ebc.BusinessCategoryId
    where
        (@SpecificBusinessCategory is null or bc.BusinessCategoryId = @SpecificBusinessCategory)
    UNION
    select ebc.EntityId 
    from EntityBusinessCategoryMap ebc
        left join BusinessGroup bg on bg.BusinessGroupId = ebc.BusinessGroupId
    where
        (@SpecificBusinessGroup is null or bg.BusinessGroupId = @SpecificBusinessGroup)
) gr on gr.EntityId = SubmittedBy.PartyId

答案 1 :(得分:0)

此:

  

如果变量作为null传入,则查询需要几秒钟   跑步。如果变量包含数据,则此查询需要一个多小时   跑。

表示您遇到了parameter sniffing问题。有几种方法可以绕过它,具体取决于您的SQL Server版本:

  1. 最旧的是为存储过程参数创建代理变量,并在查询中使用它们。听起来很疯狂,它确实有效。
  2. 您可以指定OPTIMIZE FOR UNKNOWN查询提示,以便SQL Server构建更平均的执行计划。 Here是对它的简短描述。
  3. 最后的选项是在存储过程定义中指定WITH RECOMPILE(或查询中的OPTION(RECOMPILE)),但我不推荐它 - 这不应该是除非所有警告都被理解,否则使用它,因为不断的重新编译会给你的系统带来很大的压力。

答案 2 :(得分:0)

找到解决方案。几乎将 EXISTS 更改为 IN 。通过使用EXISTS,这会强制子查询相关,因为您使用子查询中的SubmittedBy.PartyId来引用子查询之外的表。通过使用IN,可以将其转换为非相关子查询。现在表现很好。

SELECT *

FROM 
    PremiumFinanceLoan pfl
    LEFT JOIN CustomerAccount c ON pfl.CustomerAccountId = c.PartyId 
    LEFT JOIN Entity SubmittedBy ON c.SubmittedById = SubmittedBy.PartyId
    LEFT JOIN ...

WHERE
    (@SpecificBusinessGroup IS NULL OR @SpecificBusinessCategory IS NULL OR SubmittedBy.PartyId IN         
    (          
        select DISTINCT EBC.EntityId    -- That way we will get one row for each group          
        from EntityBusinessCategoryMap as EBC         
        inner join BusinessCategory as BC on BC.BusinessCategoryId = EBC.BusinessCategoryId           
        inner join BusinessGroup as BG on BG.BusinessGroupId = BC.BusinessGroupId         
        where    
        (@SpecificBusinessGroup IS NULL OR BG.BusinessGroupId = @SpecificBusinessGroup)          
        AND (@SpecificBusinessCategory IS NULL OR BC.BusinessCategoryId = @SpecificBusinessCategory)           
    ))