使用相同条件但不同列并使用聚合函数编写查询
Write query with same conditions but different columns and using aggregate functions
我正在制作一个系统,可以根据客户需求分配分析师。我编写了一个程序来搜索要分配给需求的理想分析师。
主要表格有:
- grc_analyst(Table 解决需求的分析师)
- grc_requirement(Table 的要求)与 grc_analyst 的多对一关系。这些要求有一个字段“due_date”,表示必须解决要求的截止日期。
寻找分析师的规则如下:
- 必须计算与每个分析师相关的需求数量。
- 如果每个人的数字都相同,也就是说,分析师分配的需求数量相同,那么 select 其需求的平均到期日期距当前日期最远的分析师。
- 如果数量不同,即分析师分配的需求数量不同,则 select 分配的需求量最少的分析师。
代码如下:
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
如您所见,我使用了三个查询,但所有三个查询的“来自”部分都是相同的(具有相同条件的相同连接表)。此过程符合规则并且有效,但我想减少查询次数,因为存在重复的代码。
提前致谢!
这有帮助吗?
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 table 的左连接在日期差异平均值上为空时,我也不知道规则是什么。如果这个结果是错误的,那应该被修复。
为避免重复代码,创建查询公共部分的视图,然后在您的 3 个查询中使用它,例如
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;
实际上我相信你可以用一个查询完成你想要的,利用 window 函数 FIRST_VALUE
和一个 sub-query 你可以获得你想要的值而无需完全重复查询。这也应该表现得更好。
原来你不能在COUNT OVER ()
中使用DISTINCT
所以我们必须使用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
语句中使用它。
我正在制作一个系统,可以根据客户需求分配分析师。我编写了一个程序来搜索要分配给需求的理想分析师。
主要表格有:
- grc_analyst(Table 解决需求的分析师)
- grc_requirement(Table 的要求)与 grc_analyst 的多对一关系。这些要求有一个字段“due_date”,表示必须解决要求的截止日期。
寻找分析师的规则如下:
- 必须计算与每个分析师相关的需求数量。
- 如果每个人的数字都相同,也就是说,分析师分配的需求数量相同,那么 select 其需求的平均到期日期距当前日期最远的分析师。
- 如果数量不同,即分析师分配的需求数量不同,则 select 分配的需求量最少的分析师。
代码如下:
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
如您所见,我使用了三个查询,但所有三个查询的“来自”部分都是相同的(具有相同条件的相同连接表)。此过程符合规则并且有效,但我想减少查询次数,因为存在重复的代码。
提前致谢!
这有帮助吗?
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 table 的左连接在日期差异平均值上为空时,我也不知道规则是什么。如果这个结果是错误的,那应该被修复。
为避免重复代码,创建查询公共部分的视图,然后在您的 3 个查询中使用它,例如
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;
实际上我相信你可以用一个查询完成你想要的,利用 window 函数 FIRST_VALUE
和一个 sub-query 你可以获得你想要的值而无需完全重复查询。这也应该表现得更好。
原来你不能在COUNT OVER ()
中使用DISTINCT
所以我们必须使用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
语句中使用它。