Как разделить данные на две колонки

Рассмотрим следующую структуру таблицы и образцы данных -

EmpID InputDateTime      StatusINOUT
-------------------------------------
1     2018-05-26 08:44     1
1     2018-05-26 08:44     2
2     2018-05-28 08:44     1
2     2018-05-28 12:44     2                   
1     2018-05-21 08:44     1
1     2018-05-21 10:44     2
2     2018-05-23 08:44     1
2     2018-05-23 08:44     2   

Теперь я хочу разделить столбец InputDateTime на два столбца, то есть INTIME(1) и OUTTIME(2). Логика, лежащая в основе этого, заключается в том, что дата, для которой StatusInOut равно 1, будет InTime, а для StatusInOut = 2 это значение даты будет OUTTIME(2).

Ожидаемый выходной формат показан ниже:

Empid   INTIME(1)          OUTIME(2)
--------------------------------------------
1      2018-05-26 08:44    2018-05-26 08:44
2      2018-05-28 08:44    2018-05-28 12:44
1      2018-05-21 08:44    2018-05-21 10:44
2      2018-05-23 08:44    2018-05-23 08:44

Это то, что я пробовал до сих пор

create table #tempStatus (EmpId int, intTime datetime, sStatus int)    
insert into #tempStatus    
values(1, '2018-05-26 08:44', 1),    
    (1, '2018-05-26 08:44', 2),    
    (2, '2018-05-28 08:44', 1),    
    (2, '2018-05-28 12:44', 2),        
    (1, '2018-05-21 08:44', 1),    
    (1, '2018-05-21 10:44', 2),        
    (2, '2018-05-23 08:44', 1),    
    (2, '2018-05-23 08:44', 2)    

select EmpId, MIN(intTime) as intTime, MIN(intTime) as OutTime into #tempA from (  
select EmpId, intTime, intTime as OutTime  
from #tempStatus where sStatus = 1  
)a   
group by EmpId, intTime  

select EmpId, MAX(outTime) as outTime into #tempB from(   
select EmpId, intTime as outTime  
from #tempStatus where sStatus = 2  
)b   
group by empId,outTime     

select * from #tempA order by EmpId  

drop table #tempA  
drop table #tempB  
DROP TABLE #tempStatus
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
2
0
146
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Ответ принят как подходящий

Вам нужны row_number() и используйте их различия для условного агрегирования. Это также называется проблемой Gaps and Islands:

select empid, 
       max(case when sStatus = 1 then intTime end) as INTIME,
       max(case when sStatus = 2 then intTime end) as OUTIME
from (select t.*, 
             row_number () over ( order by inttime) as seq1,
             row_number () over (partition by empid order by inttime) as seq2
      from #tempStatus t
     ) t
group by empid, (seq1-seq2);

РЕДАКТИРОВАТЬ : Если вы хотите отображать OutTime всякий раз, когда InTime его нет, вы можете использовать подзапрос:

select t.empid, 
       coalesce(INTIME, OUTIME) as INTIME,
       coalesce(OUTIME, INTIME) as OUTIME
from ( <query here> 
     ) t;

Любая идея, как справиться, если на дату нет времени, например, (3, '2018-05-23 08:44', 1)

Suraj Kumar 18.12.2018 12:55

@SurajKumar. . . В этом случае будет отображаться значение null.

Yogesh Sharma 18.12.2018 12:57

Ваш запрос дает неверный результат, когда InTime отсутствует, например, для - (5, '2018-05-23 08:44', 2). Однако в реальной жизни этого не происходит.

Suraj Kumar 18.12.2018 13:15

@SurajKumar. . . У этого есть желаемое поведение и, конечно, если InTime там нет, то он будет отображать значение null. Я думаю, вам действительно нужен OutTime, если InTime там нет.

Yogesh Sharma 18.12.2018 13:18

Результат неправильный. Он без необходимости объединяет строки. dbfiddle.uk/…

Salman A 18.12.2018 13:25

Подход тоже неверен. Это нет проблема промежутков и островов.

Salman A 18.12.2018 13:43

Это проблема соответствия времени окончания и времени начала:

WITH cte AS (
    SELECT EmpId, intTime, sStatus
         , ROW_NUMBER() OVER (PARTITION BY EmpId ORDER BY intTime) AS rn
    FROM #tempStatus
)
SELECT o.EmpId, i.intTime AS INTIME, o.intTime AS OUTIME
FROM cte o
LEFT JOIN cte AS i ON i.EmpId = o.EmpId AND i.rn = o.rn - 1 AND i.sStatus = 1
WHERE o.sStatus = 2

Демо на DB <> Fiddle и некоторые тесты

Я использую SQL Server 2008 r2, там LAG недоступен.

Suraj Kumar 18.12.2018 13:10

@SurajKumar см. Исправленный ответ.

Salman A 18.12.2018 13:32

Попробуйте этот PIVOT Пример данных

IF OBJECT_ID('Tempdb..#tempStatus')IS NOT NULL
DROP TABLE #tempStatus

CREATE TABLE #TEMPSTATUS (EMPID INT, INTTIME DATETIME, SSTATUS INT)    
INSERT INTO #TEMPSTATUS    
VALUES(1, '2018-05-26 08:44', 1),    
    (1, '2018-05-26 08:44', 2),    
    (2, '2018-05-28 08:44', 1),    
    (2, '2018-05-28 12:44', 2),        
    (1, '2018-05-21 08:44', 1),    
    (1, '2018-05-21 10:44', 2),        
    (2, '2018-05-23 08:44', 1),    
    (2, '2018-05-23 08:44', 2)    

Sql скрипт

SELECT Empid,[INTIME(1)],[OUTIME(2)]
FROM
(
    SELECT EmpId,intTime, CASE WHEN sStatus=1 THEN 'INTIME(1)' 
                       WHEN sStatus=2 THEN 'OUTIME(2)'  END INOutTimes
    FROM #tempStatus
    ) AS SRC
    PIVOT 
    (MAX(intTime) FOR INOutTimes IN ([INTIME(1)],[OUTIME(2)])
    ) AS PVT
UNION ALL
SELECT Empid,[INTIME(1)],[OUTIME(2)]
FROM
(
    SELECT EmpId,intTime, CASE WHEN sStatus=1 THEN 'INTIME(1)' 
                       WHEN sStatus=2 THEN 'OUTIME(2)'  END INOutTimes
    FROM #tempStatus
    ) AS SRC
    PIVOT 
    (MIN(intTime) FOR INOutTimes IN ([INTIME(1)],[OUTIME(2)])
    ) AS PVT

Результат

Empid    INTIME(1)                   OUTIME(2)
---------------------------------------------------------------
1       2018-05-26 08:44:00.000     2018-05-26 08:44:00.000
2       2018-05-28 08:44:00.000     2018-05-28 12:44:00.000
1       2018-05-21 08:44:00.000     2018-05-21 10:44:00.000
2       2018-05-23 08:44:00.000     2018-05-23 08:44:00.000

Я попытался найти решение после обновления с помощью внутреннего соединения, как показано ниже. Здесь я пропустил дату и строку, в которой нет InTime.

create table #tempStatus (EmpId int, intTime datetime, sStatus int)        
insert into #tempStatus        
values(1, '2018-05-26 08:44', 1),        
    (1, '2018-05-26 08:44', 2),        
    (2, '2018-05-28 08:44', 1),        
    (2, '2018-05-28 12:44', 2),            
    (1, '2018-05-21 08:44', 1),        
    (1, '2018-05-21 10:44', 2),            
    (2, '2018-05-23 08:44', 1),        
    (2, '2018-05-23 08:44', 2)  
    ,(3, '2018-05-23 08:44', 1)  

select EmpId, MIN(intTime) as intTime, MAX(intTime) as OutTime into #tempA from (      
select EmpId, intTime, intTime as OutTime      
from #tempStatus where sStatus = 1      
)a       
group by EmpId, intTime      

update s      
set s.OutTime = t.outTime      
from #tempA s      
left join     
(    
select EmpId, MAX(outTime) as outTime from(       
select EmpId, intTime as outTime      
from #tempStatus where sStatus = 2      
)b       
group by empId,outTime) t     
on s.EmpId = t.EmpId and Convert(Varchar,s.OutTime,112) =  Convert(Varchar,t.outTime,112)      

select * from #tempA order by EmpId      

drop table #tempA      
DROP TABLE #tempStatus

Вот демо - Разделение столбца

Другие вопросы по теме