我可以修改索引或视图以改进'is null'子句性能

时间:2016-01-21 19:05:19

标签: sql tsql

我有一个在WHERE中没有is null的情况下在4秒内运行的查询,但使用is null需要差不多一分钟。我已经了解了null检查对性能的影响,但在这种情况下,我无法修改正在运行的查询。

select 
    view_scores.* 
from 
    view_scores
    inner join licenses AS l on view_scores.studentId = l.account_id
where view_scores.archived_date is null 
    and l.school_id = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee' 
    and l.is_current = 1 
    and l.expiration_date >= SYSDATETIME()

view_scores是一个聚合其他表中数据的其他视图的视图,其中一个最终保存archived_date字段。该字段中的空值表示尚未归档。同样,数据结构超出了我的控制范围。我目前可以改变的是所涉及的视图的内部和表的索引。我是否有希望在不更改查询或架构的情况下显着改进对archived_date的空检查?

使用此SQL

创建

view_scores

SELECT 
    ueh.user_id AS studentId,
    vu.first_name + ' ' + vu.last_name AS studentName,
    ueh.archived_date as archived_date,
    MIN([ueh].[date_taken]) AS [started_date],
    MAX(ueh.date_taken) AS last_date,
    SUM(CAST([ueh].[actual_time] AS FLOAT) / 600000000) AS [total_time_minutes],
    SUM([exercise_scores].[earned_score]) AS [earned_score],
    SUM([exercise_scores].[possible_score]) AS [possible_score],
    AVG([exercise_scores].[percent_score]) AS [percent_score],
    COUNT(ueh.exercise_id) AS total_exercises
FROM [user_exercise_history] AS [ueh]
LEFT JOIN 
    (
        SELECT 
            coding_exercise_score.exercise_id AS exercise_id,
            coding_exercise_score.assessment_id AS assessment_id,
            coding_exercise_score.user_id AS user_id,
            coding_exercise_score.archived_date AS archived_date,
            score.earned AS earned_score,
            score.possible AS possible_score,
            CASE score.possible
                WHEN 0 THEN 0
                WHEN score.earned THEN 100
                ELSE 9.5 * POWER(CAST(score.earned AS DECIMAL) / score.possible * 100, 0.511)
            END AS percent_score
        FROM coding_exercise_score
        INNER JOIN
            coding_exercise_score_detail AS score_detail
            ON coding_exercise_score.id = score_detail.exercise_score_id
        INNER JOIN
            score
            ON score.id = score_detail.score_id
        WHERE score_detail.is_best_score = 'True'

        UNION

        SELECT
            mc_score.exercise_id AS exercise_id,
            mc_score.assessment_id AS assessment_id,
            mc_score.user_id AS user_id,
            mc_score.archived_date AS archived_date,
            score.earned AS earned_score,
            score.possible AS possible_score,
            CASE score.possible
                WHEN 0 THEN 0
                WHEN score.earned THEN 100
                ELSE 9.5 * POWER(CAST(score.earned AS DECIMAL) / score.possible * 100, 0.511)
            END AS percent_score
        FROM
            multiple_choice_exercise_score AS mc_score
        INNER JOIN score
            ON score.id = mc_score.score_id
    ) AS [exercise_scores] 
        ON
            (
                (ueh.exercise_id = [exercise_scores].exercise_id 
                    AND ueh.user_id = [exercise_scores].user_id 
                    AND (
                        (ueh.assessment_id IS NULL AND [exercise_scores].assessment_id IS NULL) 
                        OR ueh.assessment_id = [exercise_scores].assessment_id
                        )
                    AND (ueh.archived_date IS NULL) 
                )
            )
INNER JOIN entity_account AS vu ON ((ueh.user_id = vu.account_id)) 
INNER JOIN (
    select
        g.group_id,
        g.entity_name,
        g.entity_description,
        g.created_on_date,
        g.modified_date,
        g.created_by,
        g.modified_by,
        agj.account_id
    from entity_group as g
    inner join
        account_group_join as agj
    on agj.group_id = g.group_id
    where g.entity_name <> 'Administrators'
        and g.entity_name <> 'Group 1'
        and g.entity_name <> 'Group 2'
        and g.entity_name <> 'Group 3'
        and g.entity_name <> 'Group 4'
        and g.entity_name <> 'Group 5'
) AS g ON ueh.user_id = g.account_id
WHERE ueh.status = 'Completed'  
GROUP BY ueh.user_id, vu.first_name, vu.last_name, ueh.archived_date

user_exercise_history.archived_date AS archived_date是最终执行空检查的字段。我可以以任何我想要的方式修改视图,并以我想要的任何方式编制索引,但这就是它。

带有空检查的执行计划包括一组非常疯狂的排序和与分数和coding_exercise_score_detail相关的哈希匹配。

Execution Plan Details

3 个答案:

答案 0 :(得分:0)

您可以在视图上放置索引 Create Indexed Views
在view_scores.archived_date上尝试索引

答案 1 :(得分:0)

通常,JOIN ON条件和WHEREORDER BY中涉及的所有列都应编入索引以获得更好的性能。由于您说view_scores是一个视图,因此请检查实际表中的列archived_date是否已编入索引。如果没有,那么您应该考虑在该列上创建索引。

您也可以考虑将该条件添加到视图创建逻辑本身。

view_scores.archived_date is null

答案 2 :(得分:0)

ON      ueh.exercise_id = [exercise_scores].exercise_id 
    AND ueh.user_id     = [exercise_scores].user_id 
    AND ueh.archived_date IS NULL 
    AND ( (     ueh.assessment_id IS NULL   
            AND [exercise_scores].assessment_id IS NULL
          ) 
          OR ueh.assessment_id = [exercise_scores].assessment_id
        )

我会看这个 或者在加入中通常很慢 不会使用的选择和ID

ON      ueh.exercise_id = [exercise_scores].exercise_id 
    AND ueh.user_id     = [exercise_scores].user_id 
    AND ueh.archived_date IS NULL 
    AND isnull(ueh.assessment_id, -1) = isnull([exercise_scores].assessment_id, -1)