【问题标题】:How To Separate Data Into Two Columns [duplicate]如何将数据分成两列[重复]
【发布时间】:2018-12-18 11:40:49
【问题描述】:

考虑下面的表结构和示例数据——

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 的日期将是 InTimeStatusInOut 为 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

【问题讨论】:

    标签: sql sql-server tsql sql-server-2008-r2


    【解决方案1】:

    您需要row_number()s 并使用它们的差异来进行条件聚合,这也称为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);
    

    编辑:如果您想在InTime 不存在时显示OutTime,那么您可以使用子查询:

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

    【讨论】:

    • 知道如何处理,如果日期不存在,例如 (3, '2018-05-23 08:44', 1)
    • @SurajKumar。 . .在这种情况下,它将显示null 值。
    • 当 InTime 不存在时,您的查询会得到错误的结果,例如 for - (5, '2018-05-23 08:44', 2)。然而,这不会发生在现实生活中。
    • @SurajKumar。 . .这具有所需的行为,当然如果InTime 不存在,那么它将显示null 值。如果InTime 不存在,我认为您实际上需要OutTime
    • 结果错误。它不必要地组合行。 dbfiddle.uk/…
    【解决方案2】:

    试试这个 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
    

    【讨论】:

      【解决方案3】:

      这是一个结束时间与开始时间匹配的问题:

      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
      

      Demo on DB<>Fiddlesome tests

      【讨论】:

      • 我使用的是 SQL Server 2008 r2,但 LAG 不可用。
      • @SurajKumar 查看修改后的答案。
      【解决方案4】:

      我已经尝试在使用内部连接更新后找出解决方案,如下所示。这里我跳过了 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
      

      这里是演示-Spltting Column

      【讨论】:

        猜你喜欢
        • 2019-11-25
        • 2017-06-03
        • 1970-01-01
        • 1970-01-01
        • 2021-01-07
        • 2017-05-07
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多