Как я могу сослаться на значения моего подзапроса в IIF или другом подзапросе, без необходимости записывать все заново? - PullRequest
2 голосов
/ 26 июня 2019

Я написал запрос, который использует 3 подзапроса, чтобы вернуть 3 значения для каждого сотрудника в основном запросе. Мне нужно добавить 4-е значение для каждого сотрудника, которое зависит или рассчитывается из 3 значений подзапроса.

Я смог сделать это только переписав подзапросы в моем операторе IIF, но, поскольку он довольно тяжел для базы данных, это привело к падению производительности более чем в три раза по сравнению с временем выполнения запроса. .

SELECT a.emp_id, a.emp_name,
    IIF((SELECT AVG(productivity_score) FROM productivity as b WHERE a.emp_id = b.emp_id) > 100, 'Y', 'N') as [prod],
    IIF((SELECT AVG(lateness_score) FROM lateness as c WHERE a.emp_id = c.emp_id) > 80, 'Y', 'N') as [late],
    IIF((SELECT AVG(attendance_score) FROM attendance as d WHERE a.emp_id = d.emp_id) > 80, 'Y', 'N') as [attn],

-- ** status of all 3 here ** --
    IIF(IIF((SELECT AVG(productivity_score) FROM productivity as b WHERE a.emp_id = b.emp_id) > 100, 'Y', 'N') = 'Y'
    AND IIF((SELECT AVG(lateness_score) FROM lateness as c WHERE a.emp_id = c.emp_id) > 80, 'Y', 'N') = 'Y'
    AND IIF((SELECT AVG(attendance_score) FROM attendance as d WHERE a.emp_id = d.emp_id) > 80, 'Y', 'N') = 'Y',
    'Y','N') as [eligibility]
FROM employee as a;

То, что я хочу, - это написать так:

-- ** status of all 3 here ** --
    IIF([prod] = 'Y'
    AND [late] = 'Y'
    AND [attn] = 'Y', 'Y','N') as [eligibility]
FROM employee as a;

Есть ли способ написать его, чтобы не допустить повторного запуска подзапросов, или я должен писать этот запрос совершенно по-другому?

Ответы [ 3 ]

1 голос
/ 26 июня 2019

Вы можете попробовать переписать запрос с помощью объединений, что также даст вам возможное повышение производительности:

SELECT
    a.emp_id,
    a.emp_name,
    CASE WHEN p.prod_score_avg > 100 THEN 'Y' ELSE 'N' END AS prod,
    CASE WHEN l.late_score_avg > 80 THEN 'Y' ELSE 'N' END AS late
    CASE WHEN at.attn_score_avg > 80 THEN 'Y' ELSE 'N' END AS attn,
    CASE WHEN p.prod_score_avg > 100 AND
              l.late_score_avg > 80 AND
              at.attn_score_avg > 80 THEN 'Y' ELSE 'NO' END AS eligibility
FROM employee a
LEFT JOIN
(
    SELECT emp_id, AVG(productivity_score) AS prod_score_avg
    FROM productivity
    GROUP BY emp_id
) p
    ON a.emp_id = p.emp_id
LEFT JOIN
(
    SELECT emp_id, AVG(lateness_score) AS late_score_avg
    FROM lateness
    GROUP BY emp_id
) l
    ON a.emp_id = l.emp_id
LEFT JOIN
(
    SELECT emp_id, AVG(attendance_score) AS attn_score_avg
    FROM attendance
    GROUP BY emp_id
) at
    ON a.emp_id = at.emp_id

Обратите внимание, что основным недостатком вашего текущего подхода является то, что вы выполняете агрегирование в предложении select, используя коррелированные подзапросы над тремя другими таблицами. В худшем случае это заставит SQL Server сканировать каждую из этих трех таблиц один раз для каждой записи в таблице employee. Перемещая эту логику агрегирования в отдельные подзапросы, SQL Server должен агрегировать только один раз по каждой таблице.

1 голос
/ 26 июня 2019

JOIN результаты вместе вместо использования подзапросов:

SELECT e.emp_id, e.emp_name,
       (CASE WHEN p.avg_productivity_score > 100 THEN 'Y' ELSE 'N' END) as productivity,
       (CASE WHEN l.avg_lateness_score > 100 THEN 'Y' ELSE 'N' END) as lateness,    
       (CASE WHEN l.avg_attendance_score > 100 THEN 'Y' ELSE 'N' END) as attendance,    
      . . . 
FROM employee e LEFT JOIN
     (SELECT p.emp_id, AVG(p.productivity_score) as avg_productivity_score
      FROM productivity p
      GROUP BY p.emp_id
     ) p
     ON p.emp_id = e.emp_id LEFT JOIN
     (SELECT l.emp_id, AVG(l.lateness_score) as avg_lateness_score
      FROM lateness l
      GROUP BY l.emp_id
     ) p
     ON l.emp_id = e.emp_id LEFT JOIN
     (SELECT a.emp_id, AVG(a.attendance_score) as avg_attendance_score
      FROM attendance a
      GROUP BY a.emp_id
     ) p
     ON a.emp_id = e.emp_id
0 голосов
/ 26 июня 2019

Я согласен, так как все они имеют общий идентификатор, left join здесь больше подходит, но просто чтобы ответить на ваш вопрос о ссылках на подзапросы без переписывания, вы можете попробовать cross apply, чтобы сделать это.Я считаю, что это может сделать для хорошего повторного использования определенных подзапросов и Tidier для чтения:

SELECT 
    a.emp_id, 
    a.emp_name,
    [prod].val as [prod],
    [late].val as [late],
    [attn].val as [attn],
    IIF([prod].val = 'Y' AND [late].val = 'Y' AND [attn].val = 'Y','Y','N') as [eligibility]
FROM
    employee as a
    cross apply
        (select IIF((SELECT AVG(productivity_score) FROM productivity as b WHERE a.emp_id = b.emp_id) > 100, 'Y', 'N')) as [prod](val)
    cross apply
        (select IIF((SELECT AVG(lateness_score) FROM lateness as c WHERE a.emp_id = c.emp_id) > 80, 'Y', 'N')) as [late](val)
    cross apply
        (select IIF((SELECT AVG(attendance_score) FROM attendance as d WHERE a.emp_id = d.emp_id) > 80, 'Y', 'N') as [attn](val)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...