将日期行转换为没有数据透视的列

时间:2015-09-07 06:56:45

标签: sql sql-server tsql crosstab

我有一个包含日期,时间,员工ID和打卡类型(IN,OUT)的sql表,数据如下:

EMPID   Date        Time        Punch type
123     2015-08-01  08:00 AM    EMPIN
123     2015-08-01  01:00 PM    EMPOUT
123     2015-08-01  02:30 PM    EMPIN
123     2015-08-01  07:30 PM    EMPOUT
123     2015-08-02  09:30 PM    EMPIN
123     2015-08-02  11:00 AM    EMPIN
123     2015-08-02  06:00 PM    EMPOUT
123     2015-08-03  08:00 AM    EMPIN
123     2015-08-03  06:00 PM    EMPOUT
123     2015-08-03  02:30 AM    EMPOUT
123     2015-08-04  08:00 AM    EMPIN
123     2015-08-04  06:00 PM    EMPOUT

我想在日期下显示打孔时间但没有数据透视表,因为我无法在类型时间上应用聚合函数,我已经尝试了数据透视但是它没有用。

我希望将它们显示为以下内容:

EMPID       Punch type   2015-08-01   2015-08-02   2015-08-03   2015-08-04
123         EMPIN        08:00 AM     11:00 AM     08:00 AM     08:00 AM
123         EMPOUT       01:00 PM     06:00 PM     06:00 PM     06:00 PM
123         EMPIN        02:30 PM     09:30 PM
123         EMPOUT       07:30 PM     02:30 AM

,我的代码是:

DECLARE @StartDate DATETIME = '20150801'
,@EndDate DATETIME = '20150831'
if NOT EXISTS (
SELECT * FROM tempdb.dbo.sysobjects o
WHERE o.xtype IN ('U')  
AND o.id = object_id(N'tempdb..#TABLE')
        )

CREATE TABLE #TABLE (date DATETIME,numberOFWorkingHour INTEGER,empID NVARCHAR(6),time time,funckey NVARCHAR(20))

insert #TABLE

SELECT DISTINCT CONVERT(NVARCHAR(12),a.date) AS Date
,dbo.GetWorkingHourPerDay(a.date,a.empID) as numberOFWorkingHour
,a.EMPID
,a.time
,a.FuncKey
FROM
@EmployeesTable et
LEFT JOIN PERS_Attendance a ON a.empID = et.empid
LEFT JOIN PERS_EmployeeProfile EmpP ON EmpP.ID = a.EmpID

WHERE a.Date BETWEEN @StartDate AND @EndDate

GROUP BY a.empID,a.Date,a.time,a.FuncKey
--select * FROM #TABLE order by date

DECLARE @cols AS VARCHAR(MAX)
DECLARE @query  AS VARCHAR(MAX)

SELECT @cols = STUFF((SELECT ',' + QUOTENAME(cast(CONVERT(VARCHAR(20), date,103 ) as varchar(10)))   
FROM
(  
SELECT a1.date FROM (
SELECT DISTINCT date
FROM #TABLE 

WHERE Date BETWEEN @StartDate AND @EndDate
)a1
) t

FOR XML PATH(''), TYPE  
).value('.', 'VARCHAR(MAX)')   
,1,1,'')  
SET @query = 
' SELECT EMPID,funckey,' + @cols + '
FROM   
(
SELECT t.EMPId,convert(varchar(20), t.date,103)date,t.time,t.funckey, t.numberOFWorkingHour
FROM #TABLE t
)src  
pivot
(  
AVG(time)
for date in (' + @cols + ')  
) p WHERE 1=1'

EXEC(@Query)

if object_id('tempdb..#TABLE') IS NOT NULL
BEGIN
DROP TABLE #TABLE
END

正如我所提到的,时间类型不适用于枢轴,所以我应该用它替换它?

1 个答案:

答案 0 :(得分:1)

此代码提供正确的输出:

create table #table(empid int, pdate date, ptime time, ptype varchar(10));
go
insert into #table(empid, pdate, ptime, ptype) values
    (123, '2015-08-01', '08:00 AM', 'EMPIN')
    , (123, '2015-08-01', '01:00 PM', 'EMPOUT')
    , (123, '2015-08-01', '02:30 PM', 'EMPIN')
    , (123, '2015-08-01', '07:30 PM', 'EMPOUT')
    , (123, '2015-08-02', '09:30 PM', 'EMPIN')
    , (123, '2015-08-02', '11:00 AM', 'EMPIN')
    , (123, '2015-08-02', '06:00 PM', 'EMPOUT')
    , (123, '2015-08-03', '08:00 AM', 'EMPIN')
    , (123, '2015-08-03', '06:00 PM', 'EMPOUT')
    , (123, '2015-08-03', '02:30 AM', 'EMPOUT')
    , (123, '2015-08-04', '08:00 AM', 'EMPIN')
    , (123, '2015-08-04', '06:00 PM', 'EMPOUT');

declare @cols nvarchar(max), @query nvarchar(max);
declare @StartDate date = '20150801', @EndDate date = '20150803'
declare @ParmDefinition nvarchar(200) = '@StartDate date, @EndDate date'

SELECT @cols = STUFF((
    SELECT ', ' + QUOTENAME(CONVERT(NVARCHAR(20), pdate,102)) 
        FROM (  
            SELECT DISTINCT pdate FROM #table
            WHERE pDate BETWEEN @StartDate AND @EndDate
        ) t
    FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')   
,1,1,'')
;

Select @cols 

Set @query = '
With order_by_empid(empid, pdate, ptime, ptype, n) as (
Select empid, pdate, ptime, ptype
    , n = ROW_NUMBER() over(partition by empid order by pdate, ptime)
    From #table
    WHERE pDate BETWEEN @StartDate AND @EndDate
), match_days(start, empid, pdate, ptime, ptype) as (
    Select o1.pdate, o2.empid, o2.pdate, o2.ptime, o2.ptype From order_by_empid as o1
    Inner Join order_by_empid as o2 on o1.n = o2.n-1
    Where o1.ptype = ''EMPIN''
    Union All
    Select o1.pdate, o1.empid, o1.pdate, o1.ptime, o1.ptype From order_by_empid as o1
    Where o1.ptype = ''EMPIN''
), data(empid, pdate, ptime, ptype, n) as(
    Select g.empid, g.start, g.ptime, g.ptype
        , n = ROW_NUMBER() over(partition by empid, start order by pdate, ptime)
    From match_days as g
)
Select piv.empid, piv.ptype, '+@cols+' 
From data as d
Pivot (
    max(ptime)
    for pdate in('+@cols+')
) as piv
order by piv.empid, piv.n;
';

exec sp_executesql @query, @ParmDefinition, @StartDate = @StartDate, @EndDate = @EndDate;

go
drop table #table

我使用了几个CTE来分解它。它更容易展示和解释它是如何工作的......

  • 第一次CTE order_by_empid按日期和时间为每个empid订购数据。
  • 根据此有序列表,match_days用于将IN和OUT配对在一起,即使OUT有时在第二天。它为每个OUT线获得正确的IN日期。
  • CTE data然后按正确的日期和时间对它们进行排序,然后可以使用Pivot来旋转它。

输出:

empid | ptype  | 2015-08-01       | 2015-08-02       | 2015-08-03       | 2015-08-04   
123   | EMPIN  | 14:30:00.0000000 | 21:30:00.0000000 | 08:00:00.0000000 | 08:00:00.0000000
123   | EMPOUT | 19:30:00.0000000 | 18:00:00.0000000 | 18:00:00.0000000 | 18:00:00.0000000