sql - 如何将行中的一些值转置为列?
问题描述
我有一张存储人们轮班信息的表格。它的结构是:
SHIFT_TABLE
-----------
ShiftID PeopleID ShiftDate ShiftStart ShiftEnd
1 41 2020-08-24 2020-08-24 06:00 2020-08-24 14:00
2 41 2020-08-26 2020-08-26 06:00 2020-08-26 14:00
3 41 2020-08-27 2020-08-27 19:00 2020-08-28 03:00
4 41 2020-08-29 2020-08-29 10:00 2020-08-29 16:00
5 58 2020-08-24 2020-08-24 14:00 2020-08-24 21:30
6 58 2020-08-25 2020-08-25 14:00 2020-08-25 23:00
7 58 2020-08-30 2020-08-30 08:00 2020-08-30 18:00
Columns Data types
------- ----------
ShiftDate date
ShiftStart datetime
ShiftEnd datetime
用户只发送一个日期(没有时间),新的存储过程将返回该周(从周日到周六)所有人的班次信息。因此,如果使用从一周中选择任何日期,例如星期一或星期三,结果将完全相同。
例如,如果用户发送2020-08-25
到 SP,将返回除 ShiftID 为 7 的行之外的所有行。
发送2020-08-25
时,结果应如下所示:
Person 2020-08-23 2020-08-24 2020-08-25 2020-08-26 2020-08-27 2020-08-28 2020-08-29
41 OFF 06:00-14:00 OFF 06:00-14:00 19:00-03:00 OFF 10:00-16:00
58 OFF 14:00-21:30 14:00-21:30 OFF OFF OFF OFF
如果使用日期名称而不是日期作为列名,我很好,因为这种方法可以避免动态 SQL:
Person Sunday Monday Tuesday Wednesday Thursday Friday Saturday
41 OFF 06:00-14:00 OFF 06:00-14:00 19:00-03:00 OFF 10:00-16:00
58 OFF 14:00-21:30 14:00-21:30 OFF OFF OFF OFF
我想避免使用动态 SQL 或 XML解决方案。我可以获得工作日编号(1 代表星期日,2 代表星期一,等等)。我不确定这是否会有所帮助。有没有办法只ShiftStart
用. 我已经可以过滤结果集了。列名每周都会更改。我想不惜一切代价避免动态 SQL 的一些建议。ShiftEnd
PoepleID
ShiftDate
使用 pivot 可能有一些方法可以做到这一点,但它似乎是一种较慢的方法。
解决方案
解释
如果您不想使用动态 sql 并且可以将“星期一,星期二...星期日”(或每天的等效数字 1=星期一,2=星期二...)作为列标题而不是实际日期像“2020.08.23 2020.08.24 2020.08.25 2020.08.26 2020.08.27 2020.08.28 2020.08.29”这样的值,那么您的要求是可以实现的:
查询
declare @WorkDate date = '2020.08.25', @StartFilter date, @EndFilter date
declare @shift_table table ( ShiftID int identity(1,1), PeopleID int, ShiftDate varchar(max), ShiftStart datetime, ShiftEnd datetime )
insert into @shift_table (PeopleID, ShiftDate, ShiftStart, ShiftEnd )
select 41, '2020.08.24', '2020-08-24 06:00', '2020-08-24 14:00'
union select 41, '2020.08.26', '2020-08-26 06:00', '2020-08-26 14:00'
union select 41, '2020.08.27', '2020-08-27 19:00', '2020-08-28 03:00'
union select 41, '2020.08.29', '2020-08-29 10:00', '2020-08-29 16:00'
union select 58, '2020.08.24', '2020-08-24 14:00', '2020-08-24 21:30'
union select 58, '2020.08.25', '2020-08-25 14:00', '2020-08-25 23:00'
union select 58, '2020.08.30', '2020-08-30 08:00', '2020-08-30 18:00'
-- get the week start and end date filters based on the @WorkDate passed in
SELECT @StartFilter = DATEADD(dd, -(DATEPART(dw, @WorkDate)-1), @WorkDate) /* week start */,
@EndFilter = DATEADD(dd, 7-(DATEPART(dw, @WorkDate)), @WorkDate) /* week end */
select
Person,
min(Monday) as [Monday],
min(Tuesday) as [Tuesday],
min(Wednesday) as [Wednesday],
min(Thursday) as [Thursday],
min(Friday) as [Friday],
min(Saturday) as [Saturday],
min(Sunday) as [Sunday]
from (
select
PeopleID as Person,
isnull([Monday],'OFF') as [Monday],
isnull([Tuesday],'OFF') as [Tuesday],
isnull([Wednesday],'OFF') as [Wednesday],
isnull([Thursday],'OFF') as [Thursday],
isnull([Friday],'OFF') as [Friday],
isnull([Saturday],'OFF') as [Saturday],
isnull([Sunday],'OFF') as [Sunday]
from (
select
*,
DATENAME(dw,cast(ShiftDate as date)) [Day],
format(ShiftStart,'HHmm','en-us') + '-' + format(ShiftEnd,'HHmm','en-us') ShiftTime
from @shift_table
where cast(ShiftDate as date) between @StartFilter and @EndFilter
) src
pivot
(
max(ShiftTime)
for [Day] in ([Monday], [Tuesday], [Wednesday], [Thursday], [Friday], [Saturday], [Sunday])
) piv
) P
group by P.Person
结果
推荐阅读
- stata - Stata,根据与其他观察值的相对位置制作变量
- ruby-on-rails - form_with 不会在 rails 6 中发布所有数据
- apache - HTTPS 连接列出我的目录而不是执行我的应用程序
- javascript - 将 DataTable 表添加到现有表的数组中
- .htaccess - 如何使用 htaccess 删除 index.php
- c# - 为 asp-alert-message 模式的消息添加换行符
- apache-spark - 在 PySpark 中查找图中两个顶点之间的距离
- python - 无法使用 Selenium 和 BeautifulSoup 抓取文本
- java - java.lang.IllegalArgumentException:页面指令:导入的值无效
- c# - 手动填充实体 GUID 时被覆盖