根据条件将一列转换为两列

Posted

技术标签:

【中文标题】根据条件将一列转换为两列【英文标题】:Convert one column to two column based on condition 【发布时间】:2020-07-07 08:03:38 【问题描述】:

我有一张像这样的表格:

TagName          DateTime        value
HA_06_ON    2020-07-07 08:52:14    1
HA_06_ON    2020-07-07 09:01:42    0
HA_06_ON    2020-07-07 09:02:17    1
HA_06_ON    2020-07-07 09:32:55    0
HA_06_ON    2020-07-07 09:33:21    1
HA_06_ON    2020-07-07 09:35:02    0
HA_06_ON    2020-07-07 09:35:27    1 
HA_06_ON    2020-07-07 09:35:44    0
HA_06_ON    2020-07-07 10:10:32    1
HA_06_ON    2020-07-07 10:10:40    0 

我想将此表转换为基于值(值 = 1 ==> 开始时间,值 = 0 ==> 结束时间)的表。

TagName        StartTime                EndTime
HA_06_ON   2020-07-07 08:52:14      2020-07-07 09:01:42
HA_06_ON   2020-07-07 09:02:17      2020-07-07 09:32:55
....

我尝试在 select 语句上使用 case,但像这样在每一列上返回 null

TagName           StartTime            EndTime
HA_06_ON      2020-07-07 08:57:07       NULL
HA_06_ON            NULL        2020-07-07 09:01:42
HA_06_ON      2020-07-07 09:02:17       NULL
HA_06_ON            NULL        2020-07-07 09:32:55
HA_06_ON      2020-07-07 09:33:21       NULL
HA_06_ON            NULL        2020-07-07 09:35:02

【问题讨论】:

这不是将一列转换为两列,这是一个间隙和孤岛问题 - 您正在尝试查找 value 指定的孤岛的开始/结束值。 Gaps & islands 是该类别问题的实际名称。你可以谷歌它找到解决方案。在这种情况下,SUM (value) OVER(partition by TagName ORDER BY DateTime) 将为您提供一个 IslandID,您可以使用它来分组和提取 MIN(DateTime)MAX(DateTime) 值。 SUM OVER 返回运行总数 value。给定 value 的 ... 值,最终成为递增的 Island ID 您不能对计算列进行分组,因此所有这些都应该包含在 CTE 中,例如 with islands AS (SELECT ... SUM() ... As IslandID FROM..) select TagName,MIN(DateTimeOffset),MAX(DateTime) from islands GROUP BY TagName,IslandID 顺序总是 1 -> 0 -> 1 -> 0 -> ... ?你能有 1 -> 0 -> 0 -> 1 吗?然后会发生什么? Here is a good place to read about Gaps and Islands @PanagiotisKanavos 非常感谢您的帮助!!!我已经搜索了差距和岛屿,并能够解决我的问题!!! 【参考方案1】:

由于您在这里没有分组列,我们必须做出一个假设:我们可以处理您的数据,假设如果我们按日期排序,1 和 0 将交替出现。否则没有解决方案,因为我们不知道如何将 1 与 0 关联起来。

鉴于我们可以假设这种排序,我们可以使用lag()lead() 来执行此操作。请注意,这假设您从 1 开始,并且每个 1 都有一个对应的 0。如果最后一个 1 没有对应的 0,则该行的 EndTime 将为空。

select  u.TagName, 
        u.StartTime, 
        u.EndTime 
from    (
        select  TagName, 
                StartTime = [DateTime] ,
                EndTime   = lead([DateTime], 1) over
                            (
                                partition by TagName
                                order by [Datetime] asc
                            ), 
                value 
        from t
        ) u
where   u.value = 1

【讨论】:

您需要通过tagname进行分区。 没错,我不应该假设一个 TagName。【参考方案2】:

只是因为我没有看到sum() over 选项

示例

Select TagName
      ,StartTime = min(DateTime)
      ,EndTime   = max(DateTime)
 From ( 
        Select *
              ,Grp = sum(value) over (partition by TagName order by DateTime) 
         From @YourTable
      ) A
 Group By TagName,Grp

退货

TagName     StartTime                   EndTime
HA_06_ON    2020-07-07 08:52:14.000     2020-07-07 09:01:42.000
HA_06_ON    2020-07-07 09:02:17.000     2020-07-07 09:32:55.000
HA_06_ON    2020-07-07 09:33:21.000     2020-07-07 09:35:02.000
HA_06_ON    2020-07-07 09:35:27.000     2020-07-07 09:35:44.000
HA_06_ON    2020-07-07 10:10:32.000     2020-07-07 10:10:40.000

【讨论】:

【参考方案3】:

有多种方法可以解决这个问题。如果我假设这些值是完全交错的,那么一种方法是聚合:

select tagname, min(datetime) as starttime, max(datetime) as endtime
from (select t.*,
             row_number() over (partition by tagname, value order by datetime) as seqnum
      from t
     ) t
group by tagname, seqnum;

【讨论】:

以上是关于根据条件将一列转换为两列的主要内容,如果未能解决你的问题,请参考以下文章

Flexbox 3 div,两列,一列两行

ACCESS有一个表,我想根据A列或是C列两列数据中的任意数据进行查询,怎么创建查询? 哪位高人可以指点下

在 Python 中通过多个分隔符将一列分成两列

如何将单个表格行转换为两列?

sql查询中,如何将某列 分成 两列。

Magento:将产品页面中的产品选项显示为两列中的列表元素