技术问询:如何从日期列名中仅提取日部分?附月度考勤存储过程代码
解决方案
要从动态生成的日期列名中仅保留日部分,核心是在构建考勤报告的动态列列表时,把完整日期格式化为仅显示日的形式。结合你的存储过程场景,这里是具体的修改方案:
1. 生成仅含日部分的列名列表
首先从你的DATERANGE CTE中提取日期,将其转换为日的字符串形式(比如'01'、'15'),同时用方括号包裹列名避免SQL语法错误。根据你的SQL Server版本,选择对应的拼接方式:
适用于SQL Server 2017及以上(支持STRING_AGG)
DECLARE @Columns NVARCHAR(MAX) SELECT @Columns = STRING_AGG(QUOTENAME(FORMAT(DT, 'dd')), ', ') FROM DATERANGE
适用于SQL Server 2016及以下
DECLARE @Columns NVARCHAR(MAX) SELECT @Columns = STUFF(( SELECT ', ' + QUOTENAME(FORMAT(DT, 'dd')) FROM DATERANGE ORDER BY DT FOR XML PATH(''), TYPE ).value('.', 'NVARCHAR(MAX)'), 1, 2, '')
2. 修改动态PIVOT语句
将生成的日列名整合到动态PIVOT逻辑中,替换原来的完整日期列名。以下是整合后的完整存储过程示例(需根据你的实际表结构调整表名和字段):
USE [Attendace] GO /****** Object: StoredProcedure [dbo].[PerDayAttendance] Script Date: 04/11/2018 20:16:05 ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO ALTER PROCEDURE [dbo].[PerDayAttendance] @STARTDATE DATE, @ENDDATE DATE AS BEGIN SET NOCOUNT ON; WITH DATERANGE AS ( SELECT DT = DATEADD(DD, 0, @STARTDATE) WHERE DATEADD(DD, 1, @STARTDATE) <= @ENDDATE UNION ALL SELECT DATEADD(DD, 1, DT) FROM DATERANGE WHERE DATEADD(DD, 1, DT) <= @ENDDATE ) -- 生成仅含日部分的列名 DECLARE @Columns NVARCHAR(MAX) IF EXISTS (SELECT 1 FROM sys.all_objects WHERE name = 'STRING_AGG' AND type = 'FN') BEGIN SELECT @Columns = STRING_AGG(QUOTENAME(FORMAT(DT, 'dd')), ', ') FROM DATERANGE END ELSE BEGIN SELECT @Columns = STUFF(( SELECT ', ' + QUOTENAME(FORMAT(DT, 'dd')) FROM DATERANGE ORDER BY DT FOR XML PATH(''), TYPE ).value('.', 'NVARCHAR(MAX)'), 1, 2, '') END -- 构建动态PIVOT SQL DECLARE @DynamicSQL NVARCHAR(MAX) SET @DynamicSQL = N' SELECT EmployeeID, EmployeeName, ' + @Columns + ' FROM ( SELECT emp.EmployeeID, emp.EmployeeName, ISNULL(att.AttendanceStatus, ''Absent'') AS AttendanceStatus, -- 处理无考勤记录的情况 FORMAT(d.DT, ''dd'') AS DayOfMonth FROM EmployeeMaster emp -- 替换为你的员工表名 CROSS JOIN DATERANGE d LEFT JOIN AttendanceRecords att ON emp.EmployeeID = att.EmployeeID AND att.AttendanceDate = d.DT ) AS SourceData PIVOT ( MAX(AttendanceStatus) FOR DayOfMonth IN (' + @Columns + ') ) AS PivotTable ORDER BY EmployeeID' -- 执行动态SQL并传递参数 EXEC sp_executesql @DynamicSQL, N'@STARTDATE DATE, @ENDDATE DATE', @STARTDATE = @STARTDATE, @ENDDATE = @ENDDATE END GO
关键细节说明
- 用
FORMAT(DT, 'dd')会得到两位数字的日(如01、09),如果想要一位数字格式(如1、9),可以替换为CAST(DAY(DT) AS VARCHAR(2))。 QUOTENAME()函数用于包裹列名,避免日为纯数字时触发SQL语法错误(比如列名1需要写成[1])。- 示例中用
ISNULL处理了员工无考勤记录的情况,你可以根据实际需求调整默认值(比如'未打卡')。
内容的提问来源于stack exchange,提问作者Anushree Sarees Pvt Ltd
相关产品推荐
相关产品推荐

