2015-09-07 1 views
0

я имею таблицу SQL, которая содержит дату, время, идентификатор сотрудника и тип удара (IN, OUT), данные, как следующие:дата преобразования строк в столбцы без поворота

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 

как я уже упоминал, тип времени не работают с шарниром, так и с что я могу заменить?

+0

Не могли бы вы [править] Ваш вопрос и добавить некоторые образцы данных и ожидаемых результатов, пожалуйста? –

+0

да, конечно, см. Edit –

+0

Почему бы вам не использовать 'MAX()' вместо 'AVG()'? –

ответ

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, '[email protected]+' 
From data as d 
Pivot (
    max(ptime) 
    for pdate in('[email protected]+') 
) 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 заказывает данные по дате и времени для каждого из них.
  • Основанный на этом упорядоченном списке, match_days используется для одновременного соединения IN и OUT, хотя OUT иногда находится на следующий день. Он получает правильную дату IN для каждой линии OUT.
  • 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 
+0

привет @Julien Vavasseur, а если у меня динамические столбцы, то не только эти 4 дня? в этом случае я буду использовать динамический запрос на оси, как я уже упоминал в моем question..so, как я мог настроить его с помощью кода? –

+0

Это может быть добавлено к вашей переменной @ @ query. Замените основной выбор по большей части моего запроса и вашим @@ cols. –

+0

hello @Julien Vavasseur, у меня проблема, когда у пользователя есть только две удары, удар на второй день, ваш ответ работает, если у сотрудника больше двух ударов в 3 удара в тот же день, а последний перфорация выделена на второй день, см. редактирование ответа –

0

можно преобразовать время в секундах, чем найти среднее значение и преобразовать его обратно в время попробовать использовать

convert(time,dateadd(second,AVG(datediff(second,0,time)),0)) 

вместо

AVG(time)