繁体   English   中英

Oracle - SQL 查询根据列值将数据从一个表分装到另一个表?

[英]Oracle - SQL query to bin the data from one table to another based on a column value?

什么 SQL 查询用于从Table1生成Table2 (表如下所示)?

Table2需要根据Table1.Status字段将Table1中的时间戳分为开始和结束时间。 结果Table2.Status应该在每一行中在 0 和 1 之间切换。

Table2.StartTime = Table1.Status Table1.TimeStamp 1 变为 0 或反之亦然。

Table2.EndTime = ( Table1.TimeStamp of Table1.Status next change 的第一个值) - 1 毫秒

Table1中的第一条记录可以从任何Status - 1 或 0 开始。

Table1

TimeStamp                               Status
11-NOV-11 12.45.45.445000000 AM           1
11-NOV-11 12.46.45.445000000 AM           1
11-NOV-11 12.47.45.445000000 AM           1
11-NOV-11 12.48.45.445000000 AM           0
11-NOV-11 12.49.45.445000000 AM           0
11-NOV-11 12.50.45.445000000 AM           1
11-NOV-11 12.51.45.445000000 AM           0
11-NOV-11 12.52.45.445000000 AM           0
11-NOV-11 12.53.45.445000000 AM           1
...
Table2

StartTime                                        EndTime                     Status
11-NOV-11 12.45.45.445000000 AM      11-NOV-11 12.48.45.444000000 AM            1
11-NOV-11 12.48.45.445000000 AM      11-NOV-11 12.50.45.444000000 AM            0
11-NOV-11 12.50.45.445000000 AM      11-NOV-11 12.51.45.444000000 AM            1
11-NOV-11 12.51.45.445000000 AM      11-NOV-11 12.53.45.444000000 AM            0
...

任何帮助,将不胜感激。

select min(t2.TimeStamp) Start_time
  , lead(min(t2.TimeStamp), 1)over(order by min(t2.TimeStamp))  - INTERVAL '0.001' SECOND End_time
  , t2.status
from (
  select t1.TimeStamp TimeStamp, STATUS, group_tag, first_value(group_tag ignore nulls)over(order by t1.TimeStamp ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) group_tag_gap_filling
  from (
      select t.TimeStamp, Status, case when lead(status, 1, 2)over(order by t.TimeStamp) != status then row_number()over(order by t.TimeStamp)end group_tag
      from Table1 t
    )t1
  )t2
group by t2.group_tag_gap_filling, t2.status
;

首先,在内联视图“t1”中,我创建临时组然后其次,我填补所有临时组的空白然后我使用最小聚合 function 提取需要的内容,并引导分析 function,然后我从 end_time 中提取 0.001 秒

这是一个差距和孤岛问题,略有不同。 不同之处在于,您不想报告每个岛屿的最新时间戳记记录,而是想报告紧随该最新记录之后的记录。 因此,我们可以使用行号差法,也可以计算时间戳列的前导。

WITH cte AS (
    SELECT t.*, ROW_NUMBER() OVER (ORDER BY ts) rn1,
                ROW_NUMBER() OVER (PARTITION BY Status ORDER BY ts) rn2,
                LEAD(ts) OVER (ORDER BY ts) AS ts_lead
    FROM Table1 t
)

SELECT MIN(ts) AS StartTime, MAX(ts_lead) AS EndTime
FROM cte
GROUP BY Status, rn1 - rn2
HAVING MAX(ts_lead) IS NOT NULL
ORDER BY MIN(ts);

从下面的演示链接截屏

演示

您可以使用analytical函数和GROUP BY实现它,如下所示:

SELECT MIN(TIMESTAMP) AS STARTTIME, MAX(TIMESTAMP) AS ENDTIME, STATUS 
  FROM
       (SELECT T.TIMESTAMP, T.STATUS, SUM(CHANGE_POINT) OVER (ORDER BY TIMESTAMP) AS SM 
          FROM
               (SELECT T.TIMESTAMP, T.STATUS, 
                       CASE WHEN LAG(STATUS) OVER (ORDER BY TIMESTAMP) <> STATUS THEN 1 END AS CHANGE_POINT
                  FROM YOUR_TABLE T
                ) T
        )
GROUP BY SM, STATUS

您还可以使用MATCH_RECOGNIZE子句,如下所示:

SELECT * FROM YOUR_TABLE
MATCH_RECOGNIZE
(
 ORDER BY TIMESTAMP
  MEASURES
    FIRST( TIMESTAMP ) AS STARTDATE,
    LAST( TIMESTAMP ) AS ENDDATE,
    FIRST( STATUS ) AS STATUS
  ONE ROW PER MATCH
  PATTERN( same_field+ )
  DEFINE same_field AS FIRST(STATUS) = STATUS 
);

有很多方法可以解决间隙和孤岛问题。 在这种情况下,最简单的方法是两个 window 函数和过滤:

select status, ts as start_time, lead(ts) over (order by ts) as end_time
from (select t1.*, lag(status) over (order by ts) as prev_status
      from table1 t1
     ) t1
where prev_status is null or prev_status <> status
order by start_time;

逻辑很简单:

  • 保留状态更改的每一行,过滤掉所有其他行。
  • 使用lead()获取结束时间戳。

是一个 db<>fiddle。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM