SQL - 为这个 SELECT CASE 工作的最佳方法是啥?
Posted
技术标签:
【中文标题】SQL - 为这个 SELECT CASE 工作的最佳方法是啥?【英文标题】:SQL - What is the best way to work for this SELECT CASE?SQL - 为这个 SELECT CASE 工作的最佳方法是什么? 【发布时间】:2014-02-04 14:36:05 【问题描述】:我有一个考勤表和一个包含活动类型的表
我想将每个人每月的活动分组到不同的列中
示例表:
出席
个人 ID 日期 门入 门出 活动ID
活动
身份证 名称
值是 Activity。名称是:Present、Absence、Ill 和 Holiday
我想获得类似的视图(按个人 ID 和月份分组
个人ID 月 年份 缺席 假期 生病 少于 4 小时 超过 4 小时 超过 5 小时
创建此分组的最佳方法是什么? 我已经有两条 SQL 语句,但似乎都太不友好了
谁能帮帮我??? 谢谢
SELECT year(att.Date) as cYear, month(att.Date) as cMonth, att.PersonID, COUNT(1) as Absence, 0 as Holiday, 0 as Ill, 0 as LessThan4Hours, 0 as MoreThan4Hours, 0 as MoreThan5Hours
FROM Attendance att
inner join Activities act on att.ActivityID = act.ID
WHERE act.Name = 'Absence'
GROUP BY year(att.Date), month(att.Date), att.PersonID
UNION
SELECT year(att.Date) as cYear, month(att.Date) as cMonth, att.PersonID, 0 as Absence, COUNT(1) as Holiday, 0 as Ill, 0 as LessThan4Hours, 0 as MoreThan4Hours, 0 as MoreThan5Hours
FROM Attendance att
inner join Activities act on att.ActivityID = act.ID
WHERE act.Name = 'Holiday'
GROUP BY year(att.Date), month(att.Date), att.PersonID
UNION
SELECT year(att.Date) as cYear, month(att.Date) as cMonth, att.PersonID, 0 as Absence, 0 as Holiday, count(1) as Ill, 0 as LessThan4Hours, 0 as MoreThan4Hours, 0 as MoreThan5Hours
FROM Attendance att
inner join Activities act on att.ActivityID = act.ID
WHERE act.Name = 'Ill'
GROUP BY year(att.Date), month(att.Date), att.PersonID
UNION
SELECT year(att.Date) as cYear, month(att.Date) as cMonth, att.PersonID, 0 as Absence, 0 as Holiday, 0 as Ill, COUNT(1) as LessThan4Hours, 0 as MoreThan4Hours, 0 as MoreThan5Hours
FROM Attendance att
inner join Activities act on att.ActivityID = act.ID
WHERE att.GateOut is not null
AND (DATEDIFF(n, att.GateIn, att.GateOut)/60) <= 4
AND act.Name = 'Present'
GROUP BY year(att.Date), month(att.Date), att.PersonID
UNION
SELECT year(att.Date) as cYear, month(att.Date) as cMonth, att.PersonID, 0 as Absence, 0 as Holiday, 0 as Ill, 0 as LessThan4Hours, COUNT(1) as MoreThan4Hours, 0 as MoreThan5Hours
FROM Attendance att
inner join Activities act on att.ActivityID = act.ID
WHERE att.GateOut is not null
AND (DATEDIFF(n, att.GateIn, att.GateOut)/60) > 4
AND (DATEDIFF(n, att.GateIn, att.GateOut)/60) <= 5
AND act.Name = 'Present'
GROUP BY year(att.Date), month(att.Date), att.PersonID
UNION
SELECT year(att.Date) as cYear, month(att.Date) as cMonth, att.PersonID, 0 as Absence, 0 as Holiday, 0 as Ill, 0 as LessThan4Hours, 0 as MoreThan4Hours, COUNT(1) as MoreThan5Hours
FROM Attendance att
inner join Activities act on att.ActivityID = act.ID
WHERE att.GateOut is not null
AND (DATEDIFF(n, att.GateIn, att.GateOut)/60) > 5
AND act.Name = 'Present'
GROUP BY year(att.Date), month(att.Date), att.PersonID
我也在 SELECT CASE 中解决了这个问题,可能更糟?
SELECT a.PersonID, a.cYear, a.cMonth, SUM(a.Absence) AS Absence, SUM(a.Holiday) AS Holiday, SUM(a.Ill) AS Ill, SUM(a.LessThan4Hours) AS LessThan4Hours, SUM(a.MoreThan4Hours) AS MoreThan4Hours, SUM(a.MoreThan5Hours) AS MoreThan5Hours
FROM
(
SELECT year(att.Date) as cYear, month(att.Date) as cMonth, att.PersonID, CASE
WHEN act.Name = 'Ill' THEN 1
WHEN act.Name = 'Holiday' THEN 0
WHEN act.Name = 'Absence' THEN 0
WHEN GateOut = null THEN 0
WHEN (DATEDIFF(n, gatein, gateout)/60) > 5 THEN 0
WHEN (DATEDIFF(n, gatein, gateout)/60) > 4 THEN 0
WHEN (DATEDIFF(n, gatein, gateout)/60) <= 4 THEN 0
ELSE 0
END AS Ill,
CASE
WHEN act.Name = 'Ill' THEN 0
WHEN act.Name = 'Holiday' THEN 1
WHEN act.Name = 'Absence' THEN 0
WHEN GateOut = null THEN 0
WHEN (DATEDIFF(n, gatein, gateout)/60) > 5 THEN 0
WHEN (DATEDIFF(n, gatein, gateout)/60) > 4 THEN 0
WHEN (DATEDIFF(n, gatein, gateout)/60) <= 4 THEN 0
ELSE 0
END AS Holiday,
CASE
WHEN act.Name = 'Ill' THEN 0
WHEN act.Name = 'Holiday' THEN 0
WHEN act.Name = 'Absence' THEN 1
WHEN GateOut = null THEN 0
WHEN (DATEDIFF(n, gatein, gateout)/60) > 5 THEN 0
WHEN (DATEDIFF(n, gatein, gateout)/60) > 4 THEN 0
WHEN (DATEDIFF(n, gatein, gateout)/60) <= 4 THEN 0
ELSE 0
END AS Absence,
CASE
WHEN act.Name = 'Ill' THEN 0
WHEN act.Name = 'Holiday' THEN 0
WHEN act.Name = 'Absence' THEN 0
WHEN GateOut = null THEN 0
WHEN (DATEDIFF(n, gatein, gateout)/60) > 5 THEN 1
WHEN (DATEDIFF(n, gatein, gateout)/60) > 4 THEN 0
WHEN (DATEDIFF(n, gatein, gateout)/60) <= 4 THEN 0
ELSE 0
END AS MoreThan5Hours,
CASE
WHEN act.Name = 'Ill' THEN 0
WHEN act.Name = 'Holiday' THEN 0
WHEN act.Name = 'Absence' THEN 0
WHEN GateOut = null THEN 0
WHEN (DATEDIFF(n, gatein, gateout)/60) > 5 THEN 0
WHEN (DATEDIFF(n, gatein, gateout)/60) > 4 THEN 1
WHEN (DATEDIFF(n, gatein, gateout)/60) <= 4 THEN 0
ELSE 0
END AS MoreThan4Hours,
CASE
WHEN act.Name = 'Ill' THEN 0
WHEN act.Name = 'Holiday' THEN 0
WHEN act.Name = 'Absence' THEN 0
WHEN GateOut = null THEN 0
WHEN (DATEDIFF(n, gatein, gateout)/60) > 5 THEN 0
WHEN (DATEDIFF(n, gatein, gateout)/60) > 4 THEN 0
WHEN (DATEDIFF(n, gatein, gateout)/60) <= 4 THEN 1
ELSE 0
END AS LessThan4Hours
from Attendance att
inner join Activities act on att.ActivityID = act.ID
) a
GROUP BY a.PersonID, a.cYear, a.cMonth
ORDER BY a.cYear DESC, a.cMonth DESC, a.PersonID
【问题讨论】:
【参考方案1】:这可能更容易阅读和理解。内部“PreChk”查询遍历记录并将它们标记为不同的类型和时间,然后应用按您的要求分组的摘要。
SELECT
PreChk.PersonID,
PreChk.cYear,
PreChk.cMonth,
SUM(PreChk.Absence) AS Absence,
SUM(PreChk.Holiday) AS Holiday,
SUM(PreChk.Ill) AS Ill,
SUM( case when PreChk.HrsDif > 5 then 1 else 0 end ) as MoreThan5Hours,
SUM( case when PreChk.HrsDif > 4 AND PreChk.HrsDif < 5 then 1 else 0 end ) as MoreThan4Hours,
SUM( case when PreChk.HrsDif < 4 then 1 else 0 end ) as LessThan4Hours
from
( SELECT
att.PersonID,
year(att.Date) as cYear,
month(att.Date) as cMonth,
CASE WHEN act.Name = 'Ill' THEN 1 else 0 end as Ill,
CASE WHEN act.Name = 'Holiday' THEN 1 else 0 end as Holiday,
CASE WHEN act.Name = 'Absence' THEN 1 else 0 end as Absence,
CASE when GateOut = null
THEN 0
ELSE DATEDIFF(n, gatein, gateout)/60) end as HrsDif
from
Attendance att
inner join Activities act
on att.ActivityID = act.ID ) PreChk
GROUP BY
PreChk.PersonID,
PreChk.cYear,
PreChk.cMonth
ORDER BY
PreChk.cYear DESC,
PreChk.cMonth DESC,
PreChk.PersonID
【讨论】:
我不知道您可以将 CASE 作为不同的列名!太棒了! @Cedric,单击帮助/游览选项以了解礼仪和问题标记。这有助于其他人知道问题何时“解决”,这样其他人就不会浪费时间尝试解决已经完成的问题,并有助于其他人在工作中遇到类似问题时了解 DID 的工作原理。【参考方案2】:这里有一个sqlfiddle 展示了这个方法
select personid,
datepart(month, date) as Month,
datepart(year, date) as year,
case when [present] is not null then 1 else 0 end as present,
case when [absence] is not null then 1 else 0 end as absence,
case when [III] is not null then 1 else 0 end as III,
case when [holiday] is not null then 1 else 0 end as holiday,
sum(case when datediff(hour, gatein, gateout) <= 4 then 1 else 0 end) as LessThan4Hours,
sum(case when datediff(hour, gatein, gateout) > 4 and datediff(hour, gatein, gateout) <= 5 then 1 else 0 end) as GreaterThan4Hours,
sum(case when datediff(hour, gatein, gateout) > 5 then 1 else 0 end) as GreaterThan5Hours
from
(
select a.*, act.name
from attendance a
join activity act on a.activityid = act.activityid
) as source
pivot
(
max(name)
FOR name in ([present],[absence],[III],[holiday])
) as p
group by personid,
datepart(month, date),
datepart(year, date),
case when [present] is not null then 1 else 0 end,
case when [absence] is not null then 1 else 0 end,
case when [III] is not null then 1 else 0 end,
case when [holiday] is not null then 1 else 0 end
【讨论】:
很有趣,但返回的结果并不完全符合我的预期。但这鼓励我了解有关在 sql 中进行透视的更多信息。还是谢谢以上是关于SQL - 为这个 SELECT CASE 工作的最佳方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章
SELECT CASE SQL 中的 DISTINCT COUNT
SQL Server 2008 - SELECT 子句中的 Case / If 语句 [重复]
spark2.1:使用df.select(when(a===b,1).otherwise)替换(case when a===b then 1 else 0 end)