WHERE 子句中的 OR 会降低 sql 查询性能(sql server)

Posted

技术标签:

【中文标题】WHERE 子句中的 OR 会降低 sql 查询性能(sql server)【英文标题】:OR in WHERE clause is slowing down sql query performance ( sql server) 【发布时间】:2015-01-26 21:50:23 【问题描述】:

我发现我们在应用程序中使用的一个存储过程存在性能问题。

这是一个非常大的存储过程,我已将其缩小到我看到性能问题的部分。

它在 where 子句中(复制如下)。查询估计计划显示这部分大约占80%。

逻辑是@AssignedToIds@AssignedToRoleIds可以为null,如果是null,我们就拉取所有记录。 临时表可以有多行。非常感谢您对提高性能的任何帮助。

#AssignedTo#AssignedToRole 是临时表。

#AssignedTo在表中只有一个值,#AssignedToRole为空

SQL:-

SELECT DISTINCT TOP 2000 t.Member_Party_PartyId AS Member_Party_PartyId
FROM Task t
WHERE t.IsDeleted = 0
    AND (
        t.DueDate >= @DueStart
        OR @DueStart IS NULL
        )
    AND (
        t.DueDate <= @DueEnd
        OR @DueEnd IS NULL
        )
    AND (
        (
            @FilterType = 'MyPatients'
            AND t.AssignedUserId = @UserId
            )
        OR @FilterType != 'MyPatients'
        )
    AND (@FilterType != 'MyRole')
    AND (
        (
            @FilterType = 'MyGroup'
            AND t.AssignedUserId IN (
                SELECT PartyId
                FROM #OrgMembers
                )
            )
        OR @FilterType != 'MyGroup'
        )
    AND (
        (
            @FilterType = 'Custom'
            AND vpad.Provider IN (
                SELECT PartyId
                FROM #OrgMembers
                )
            )
        OR @FilterType != 'Custom'
        )
    AND (
        (
            @ActiveCase = 1
            AND cases.CaseId IS NOT NULL
            )
        OR @ActiveCase = 0
        )
    AND (
        t.TaskStatusId IN (
            SELECT TaskStatusId
            FROM #TaskStatus
            )
        )
    AND (
        t.TaskCategoryId IN (
            SELECT TaskCategoryId
            FROM #TaskCategory
            )
        OR @TaskCategoryIds IS NULL
        )
    AND (
        t.TaskPriorityId IN (
            SELECT TaskPriorityId
            FROM #TaskPriority
            )
        OR @TaskPriorityIds IS NULL
        )
    AND (
        rm.RegistryId IN (
            SELECT RegistryId
            FROM #Registry
            )
        OR @RegistryIds IS NULL
        )
    AND (
        (
            fg.CareMeasureId IN (
                SELECT CareMeasureId
                FROM #CareMeasure
                )
            AND exclusion.MemberId IS NULL
            )
        OR @CareMeasureIds IS NULL
        )
    AND (
        vpad.OrganizationId IN (
            SELECT OrganizationId
            FROM #Organization
            )
        OR (
            SELECT count(OrganizationId)
            FROM #Organization
            ) = 0
        )
    AND (
        vpad.Provider IN (
            SELECT ProviderId
            FROM #Provider
            )
        OR @ProviderIds IS NULL
        )
    AND (
        cases.CaseTypeId IN (
            SELECT CaseTypeId
            FROM #CaseType
            )
        OR @CaseIds IS NULL
        )
    AND
    --(case when @AssignedToIds  Is Not Null And then t.AssignedUserId in (select AssignedToId from #AssignedTo))
    (
        (
            t.AssignedUserId IN (
                SELECT AssignedToId
                FROM #AssignedTo
                )
            OR (
                @AssignedToIds IS NULL
                AND @AssignedToRoleIds IS NULL
                )
            )
        OR (
            t.AssignedRoleId IN (
                SELECT AssignedRoleId
                FROM #AssignedToRole
                )
            OR (
                @AssignedToRoleIds IS NULL
                AND @AssignedToIds IS NULL
                )
            )
        )
    AND (
        vpad.OrganizationId IN (
            SELECT OrganizationId
            FROM #UserOrgs
            )
        OR (
            (
                SELECT count(OrganizationId)
                FROM #UserOrgs
                ) = 0
            )
        OR (@RoleType <> 'Manager')
        )
    AND (
        (
            mhp.MemberHealthPlanTypeId IN (
                SELECT HealthPlanId
                FROM #HealthPlan
                )
            AND hpds.HierarchyOrder IS NOT NULL
            )
        OR @HealthPlanIds IS NULL
        )
OPTION (RECOMPILE);

【问题讨论】:

为什么选择子选择?为什么不加入反对他们呢? 无法加入。这些是条件连接。如果变量 --@--AssignedToIds 或 --@--AssignedToRoleIds 有值,我只需要加入临时表。 您有前 x 行,但没有排序依据。如果没有 order by,您将无法知道将返回哪些行。 看来必须有一种更简洁的方式来写这个。通常我不推荐动态 SQL,但在这种情况下可能值得研究一下? 谢谢。如果我没有从论坛中得到任何其他想法,我会尝试一下。 【参考方案1】:

你可以试试添加

option(recompile) 

到该 SQL 查询的末尾。看看会不会加快一点。

【讨论】:

我想我们可能需要在这里查看整个查询。您可能必须借助带有索引的临时表来进行检查以加快速度或其他方式。但是,仅使用 where 语句很难做出这样的假设。 感谢收看。这是完整的查询【参考方案2】:

对于这个 where 子句中有这么多条件的个人而言,找出性能问题出在哪里将是一场噩梦。

如果是我,我会考虑将此查询拆分为更小的查询,以便您处理不断减少的子集。

例如得到刚刚的结果

INSERT INTO myWorkingTable (some columns here....)
SELECT 
    DISTINCT TOP 2000 
    t.Member_Party_PartyId AS Member_Party_PartyId
FROM  
    Task t 
WHERE 
    t.IsDeleted = 0 

然后从这些结果中处理下一组查询,并在可能的情况下包含您的任何条件逻辑。

例如。 例如,您的逻辑:

(
t.DueDate >= @DueStart 
OR 
    @DueStart IS NULL
) 

可能是

IF(@DueStart IS NOT NULL)
BEGIN 
 --LEAVE ONLY THOSE ITEMS WHERE @DueStart >= dueDate
 DELETE FROM myWorkingTable WHERE t.DueDate < @DueStart 
END 

因此,可以在“主”查询之外执行其他类似条件。

然后您最终可以运行一个执行计划来检查完整的查询,然后应用该计划建议的任何建议索引。

我知道它不能直接回答这个问题,但是对于这种单一的东西,有人几乎不可能只说“你的问题出在这个位上”

虽然在 where 子句中执行 NULL 检查可能会很昂贵。

【讨论】:

以上是关于WHERE 子句中的 OR 会降低 sql 查询性能(sql server)的主要内容,如果未能解决你的问题,请参考以下文章

慢查询问题常见的优化方法

SQL相关子查询与非相关子查询

删除 SQL 查询中 Where 子句中的条件

深入理解CQL中的Where子句

优化where子句中的SQL子查询

WHERE 子句中的 SQL 查询子选择优化 (SQL Server)