Напишите запрос с одинаковыми условиями, но с разными столбцами и с использованием агрегатных функций - PullRequest
0 голосов
/ 03 августа 2020

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

Основные таблицы:

  • grc_analyst (Таблица аналитиков, которые решают требования)
  • grc_requirement (Таблица требований) Связь «многие к одному» с grc_analyst. В требованиях есть поле due_date, которое представляет крайний срок, к которому требование должно быть выполнено.

Правила поиска аналитика следующие:

  • Число требований, связанных с каждым аналитиком, должны быть подсчитаны.
  • Если это число одинаково для всех, то есть аналитикам назначено одинаковое количество требований, то выберите аналитика, чей средний срок выполнения ваших требований самый дальний с текущей даты.
  • Если число другое, т. е. у аналитиков другое количество назначенных требований, выберите аналитика с наименьшим количеством назначенных требований.

Здесь это код:

    DECLARE @totalRequirementsAnalysts int
    DECLARE @idAnalyst int = null 
    
    SELECT @totalRequirementsAnalysts =count(distinct(a.requirements)) 
    FROM ( 
        SELECT 
            a.id,count(r.id) requirements
        FROM
            grc_analyst a
            INNER JOIN  grc_analyststate ea     ON a.id_analyststate = ea.id AND ea.code = 'A'
            INNER JOIN  grc_analyst_category ac ON a.id = ac.id_analyst 
            INNER JOIN  grc_category c              ON c.id = ac.id_category AND c.code = 'SOAP'        
            LEFT JOIN   grc_requirement r           ON a.id = r.id_analyst AND r.id_requirementstate in (
                SELECT id from grc_requirementstate er where er.code IN ('AS','ER','DL','DC') 
            )
        group by a.id 
    ) a
    
    
    IF  (@totalRequirementsAnalysts = 1)
    BEGIN 
        PRINT 'Analysts have the same amount of requirements assigned'
    
        SET @idAnalyst = (
            SELECT a.id from (
                SELECT TOP(1)
                    a.id,avg (DATEDIFF(DAY,getdate(),r.due_date))average_due_date
                FROM
                    grc_analyst a
                    INNER JOIN  grc_analyststate ea     ON a.id_analyststate = ea.id AND ea.code = 'A'
                    INNER JOIN  grc_analyst_category ac ON a.id = ac.id_analyst 
                    INNER JOIN  grc_category c              ON c.id = ac.id_category AND c.code = 'SOAP'        
                    LEFT JOIN   grc_requirement r           ON a.id = r.id_analyst AND r.id_requirementstate in (
                        SELECT id from grc_requirementstate er where er.code IN ('AS','ER','DL','DC') 
                    )
                group by a.id 
                order by average_due_date DESC
            ) a
        )
    
    END
    ELSE
    BEGIN
        PRINT 'Analysts have different number of requirements assigned'
        SET @idAnalyst = (
            SELECT a.id from (
                SELECT TOP(1)
                    a.id,count(r.id) requirements
                FROM
                    grc_analyst a
                    INNER JOIN  grc_analyststate ea     ON a.id_analyststate = ea.id AND ea.code = 'A'
                    INNER JOIN  grc_analyst_category ac ON a.id = ac.id_analyst 
                    INNER JOIN  grc_category c              ON c.id = ac.id_category AND c.code = 'SOAP'        
                    LEFT JOIN   grc_requirement r           ON a.id = r.id_analyst AND r.id_requirementstate in (
                        SELECT id from grc_requirementstate er where er.code IN ('AS','ER','DL','DC') 
                    )
                group by a.id 
                order by requirements ASC
            ) a
        )
    END
    
    SELECT ga.id from grc_analyst ga where ga.id = @idAnalyst

Как видите, я использую три запроса, но часть "от" одинакова для всех трех (одинаковые таблицы соединения с одинаковыми условиями). Эта процедура соответствует правилу и работает, но я хочу уменьшить количество запросов, так как есть повторяющийся код.

Tha заранее!

Ответы [ 3 ]

1 голос
/ 03 августа 2020

Чтобы избежать повторения кода, создайте представление общей части запроса, а затем используйте его в своих трех запросах, например,

CREATE VIEW dbo.grc_analyst_view
AS
SELECT a.id
    , COUNT(r.id) requirements
    , AVG(DATEDIFF(DAY,GETDATE(),r.due_date)) average_due_date
FROM grc_analyst a
INNER JOIN grc_analyststate ea ON a.id_analyststate = ea.id AND ea.code = 'A'
INNER JOIN grc_analyst_category ac ON a.id = ac.id_analyst 
INNER JOIN grc_category c ON c.id = ac.id_category AND c.code = 'SOAP'        
LEFT JOIN grc_requirement r ON a.id = r.id_analyst AND r.id_requirementstate in (
    SELECT id
    FROM grc_requirementstate er
    WHERE er.code IN ('AS','ER','DL','DC') 
)
GROUP BY a.id;
GO

-- QUERY 1
SELECT @totalRequirementsAnalysts = COUNT(DISTINCT(requirements)) 
FROM dbo.grc_analyst_view;

-- QUERY 2
SELECT TOP(1) id
FROM dbo.grc_analyst_view
ORDER BY average_due_date DESC

-- QUERY 3
SELECT TOP(1) id
FROM dbo.grc_analyst_view
ORDER BY requirements ASC;
0 голосов
/ 03 августа 2020

На самом деле я считаю, что вы можете выполнить sh то, что хотите, с помощью одного запроса, используя оконную функцию FIRST_VALUE и подзапрос, вы можете получить именно то значение, которое хотите, без повторения запроса вообще. Это также должно работать лучше.

Оказывается, вы не можете использовать DISTINCT в COUNT OVER (), поэтому мы должны использовать вместо CTE и CROSS APPLY.

with cte as (
  SELECT a.id
      , COUNT(DISTINCT r.id) Requirements
      , AVG(DATEDIFF(DAY,GETDATE(),r.due_date)) average_due_date
  FROM grc_analyst a
  INNER JOIN grc_analyststate ea ON a.id_analyststate = ea.id AND ea.code = 'A'
  INNER JOIN grc_analyst_category ac ON a.id = ac.id_analyst 
  INNER JOIN grc_category c ON c.id = ac.id_category AND c.code = 'SOAP'        
  LEFT JOIN grc_requirement r ON a.id = r.id_analyst AND r.id_requirementstate in (
      SELECT id
      FROM grc_requirementstate er
      WHERE er.code IN ('AS','ER','DL','DC') 
  )
  GROUP BY a.id
)
SELECT TOP 1 @idAnalyst = CASE WHEN N.DistinctRequirements = 1 THEN FIRST_VALUE(X.id) OVER (ORDER BY average_due_date ASC) ELSE FIRST_VALUE(X.id) OVER (ORDER BY Requirements ASC) END
FROM cte X
CROSS APPLY (
  SELECT COUNT(DISTINCT Requirements) DistinctRequirements
  FROM cte
) N;
-- While one should normally use an 'order by' clause with 'top' it is meaningless in this case as all rows return the same.

И если требуется напечатать, какой вариант был выбран, присвойте значение requirements переменной и используйте его в своем операторе IF.

0 голосов
/ 03 августа 2020

Это помогает?

WITH base AS
(
  SELECT 
    a.id,
    count(r.id) requirements,
    avg (DATEDIFF(DAY,getdate(),r.due_date)) average_due_date
  FROM grc_analyst a
  JOIN grc_analyststate ea     ON a.id_analyststate = ea.id AND ea.code = 'A'
  JOIN grc_analyst_category ac ON a.id = ac.id_analyst 
  JOIN grc_category c          ON c.id = ac.id_category AND c.code = 'SOAP'        
  LEFT JOIN grc_requirement r  ON a.id = r.id_analyst
  JOIN grc_requirementstate es ON r.id_requirementstate IS NULL OR 
                                  (r.id_requirementstate IS NOT NULL 
                                    AND r.id_requirementstate = er.id 
                                    AND er.code IN ('AS','ER','DL','DC') 
                                  )
  group by a.id 
)
-- etc

Если вам не нравятся CTE, вы можете использовать представление для вышеуказанного.

Также я не знаю, для чего это правило когда левое соединение таблицы grc_requirement равно нулю на средней разнице дат. Это следует исправить, если результат неверен.

...