Sql 查找记录中的差距
我有一个Oracle表,表中有标签号、时间戳和方向,可以是输入也可以是输出。 有时候,读者会失败,会有差距 对于相同的标语牌编号,我应该:Sql 查找记录中的差距,sql,oracle,Sql,Oracle,我有一个Oracle表,表中有标签号、时间戳和方向,可以是输入也可以是输出。 有时候,读者会失败,会有差距 对于相同的标语牌编号,我应该: Placard Timestamp Direction 2533 15:36 IN 2533 15:41 OUT 但有时,我会: Placard Timestamp Direction 2533 15:36 IN 2533 15:41
Placard Timestamp Direction
2533 15:36 IN
2533 15:41 OUT
但有时,我会:
Placard Timestamp Direction
2533 15:36 IN
2533 15:41 IN
2533 15:49 OUT
表示第一个输入的读出失败,或
Placard Timestamp Direction
2533 15:36 IN
2533 15:41 OUT
2533 15:52 OUT
意味着第二次通过的读取失败
为了填补空白,我只需在结果中插入一条记录,如果是遗漏,则为+1分钟;如果是遗漏,则为-1分钟;我将添加第四个字段,指示是正常记录还是错误记录。例如,预期输出为:
Placard Timestamp Direction Status
2533 15:36 IN OK
2533 15:41 OUT OK
2533 15:51 IN Error IN
2533 15:52 OUT OK
2533 15:36 IN OK
2533 15:37 OUT Error OUT
我希望我的解释是正确的
我不能提供任何SQL,因为我不知道怎么做。非常感谢您的帮助。您需要使用分析函数,但也需要联合来创建新行。请看一下,我试图将所有可能性都放在测试数据中:
with t(Placard ,ts ,Direction ,Status) as (
select 2533 , to_timestamp('15:36','hh24:mi') , 'IN' , 'OK' from dual union all
select 2533 , to_timestamp('15:41','hh24:mi') , 'IN' , 'OK' from dual union all
select 2533 , to_timestamp('15:49','hh24:mi') , 'OUT' , 'OK' from dual UNION ALL
select 2533 , to_timestamp('15:55','hh24:mi') , 'OUT' , 'OK' from dual UNION ALL
select 2533 , to_timestamp('16:00','hh24:mi') , 'IN' , 'OK' from dual)
--test data
(SELECT PLACARD,TO_CHAR(CASE WHEN (DIRECTION = 'IN') THEN TS+INTERVAL '1' MINUTE ELSE TS_NEXT-INTERVAL '1' minute end,'HH24:MI') TS,
CASE WHEN DIRECTION = 'IN' THEN 'OUT' ELSE 'IN' END DIRECTION,
'ERROR '||CASE WHEN DIRECTION = 'IN' THEN 'OUT' ELSE 'IN' END STATUS
FROM
(select t1.*,lead(TS) over(order by TS) TS_NEXT,
case when (lead(t1.direction) over(order by TS) = direction) or
(direction = 'IN' AND lead(t1.direction) over(order by TS) IS NULL) then 'Y' else 'N' end WRONG from t t1)
WHERE WRONG = 'Y')
UNION ALL SELECT PLACARD,TO_CHAR(TS,'HH24:MI'),DIRECTION,STATUS FROM T
ORDER BY TS;
输出:
PLACARD TS DIRECTION STATUS
---------- ----- --------- ---------
2533 15:36 IN OK
2533 15:37 OUT ERROR OUT
2533 15:41 IN OK
2533 15:49 OUT OK
2533 15:54 IN ERROR IN
2533 15:55 OUT OK
2533 16:00 IN OK
2533 16:01 OUT ERROR OUT
8 rows selected.
您需要使用分析函数,但也需要union来创建新行。请看一下,我试图将所有可能性都放在测试数据中:
with t(Placard ,ts ,Direction ,Status) as (
select 2533 , to_timestamp('15:36','hh24:mi') , 'IN' , 'OK' from dual union all
select 2533 , to_timestamp('15:41','hh24:mi') , 'IN' , 'OK' from dual union all
select 2533 , to_timestamp('15:49','hh24:mi') , 'OUT' , 'OK' from dual UNION ALL
select 2533 , to_timestamp('15:55','hh24:mi') , 'OUT' , 'OK' from dual UNION ALL
select 2533 , to_timestamp('16:00','hh24:mi') , 'IN' , 'OK' from dual)
--test data
(SELECT PLACARD,TO_CHAR(CASE WHEN (DIRECTION = 'IN') THEN TS+INTERVAL '1' MINUTE ELSE TS_NEXT-INTERVAL '1' minute end,'HH24:MI') TS,
CASE WHEN DIRECTION = 'IN' THEN 'OUT' ELSE 'IN' END DIRECTION,
'ERROR '||CASE WHEN DIRECTION = 'IN' THEN 'OUT' ELSE 'IN' END STATUS
FROM
(select t1.*,lead(TS) over(order by TS) TS_NEXT,
case when (lead(t1.direction) over(order by TS) = direction) or
(direction = 'IN' AND lead(t1.direction) over(order by TS) IS NULL) then 'Y' else 'N' end WRONG from t t1)
WHERE WRONG = 'Y')
UNION ALL SELECT PLACARD,TO_CHAR(TS,'HH24:MI'),DIRECTION,STATUS FROM T
ORDER BY TS;
输出:
PLACARD TS DIRECTION STATUS
---------- ----- --------- ---------
2533 15:36 IN OK
2533 15:37 OUT ERROR OUT
2533 15:41 IN OK
2533 15:49 OUT OK
2533 15:54 IN ERROR IN
2533 15:55 OUT OK
2533 16:00 IN OK
2533 16:01 OUT ERROR OUT
8 rows selected.
您需要识别输入/输出值对,其中一些可能缺失。在结果集中创建一个额外的行并不简单(如Aramillo所示,至少不需要敲表两次);您可以使用lead和lag来发现行不存在,但创建行则是另一回事 一种方法是使用lead和lag将数据压缩到in/out对中,这将为缺少的值留下空白,填补空白,然后再次扩展。使用稍微修改的起始数据:
select placard, timestamp, direction
from t42 order by placard, timestamp;
PLACARD TIMESTAMP DIRECTION
---------- --------- ---------
2533 15:36 IN
2533 15:41 OUT
2533 15:52 OUT
2533 15:56 IN
2533 16:02 IN
2533 16:07 OUT
2533 16:10 IN
您可以在后面和前面达到峰值:
select placard, timestamp, direction,
lag(direction) over (partition by placard order by timestamp) as last_dir,
lead(direction) over (partition by placard order by timestamp) as next_dir,
lag(timestamp) over (partition by placard order by timestamp) as last_ts,
lead(timestamp) over (partition by placard order by timestamp) as next_ts
from t42;
PLACARD TIMESTAMP DIRECTION LAST_DIR NEXT_DIR LAST_TS NEXT_TS
---------- --------- --------- -------- -------- ------- -------
2533 15:36 IN OUT 15:41
2533 15:41 OUT IN OUT 15:36 15:52
2533 15:52 OUT OUT IN 15:41 15:56
2533 15:56 IN OUT IN 15:52 16:02
2533 16:02 IN IN OUT 15:56 16:07
2533 16:07 OUT IN IN 16:02 16:10
2533 16:10 IN OUT 16:07
和用例语句作为一种手动轴心:
with t as (
...
)
select placard, timestamp, direction,
case when direction = 'IN' then timestamp
when last_dir is null or last_dir = 'OUT'
then timestamp - interval '1' minute
else last_ts end as in_ts,
case when direction = 'OUT' then timestamp
when next_dir is null or next_dir = 'IN'
then timestamp + interval '1' minute
else next_ts end as out_ts,
case when direction = 'OUT' and (last_dir is null or last_dir = 'OUT')
then 'Error IN' else 'OK' end as in_error,
case when direction = 'IN' and (next_dir is null or next_dir = 'IN')
then 'Error OUT' else 'OK' end as out_error
from t
order by placard, timestamp;
PLACARD TIMESTAMP DIRECTION IN_TS OUT_TS IN_ERROR OUT_ERROR
---------- --------- --------- ----- ------ -------- ---------
2533 15:36 IN 15:36 15:41 OK OK
2533 15:41 OUT 15:36 15:41 OK OK
2533 15:52 OUT 15:51 15:52 Error IN OK
2533 15:56 IN 15:56 15:57 OK Error OUT
2533 16:02 IN 16:02 16:07 OK OK
2533 16:07 OUT 16:02 16:07 OK OK
2533 16:10 IN 16:10 16:11 OK Error OUT
现在,对于最初有两个值的对,它有重复的值,对于没有的对,它有一分钟的调整值;丢失原始方向行和时间戳行,您可以进入核心对:
with t as (
...
)
select distinct placard,
case when direction = 'IN' then timestamp
when last_dir is null or last_dir = 'OUT'
then timestamp - interval '1' minute
else last_ts end as in_ts,
case when direction = 'OUT' then timestamp
when next_dir is null or next_dir = 'IN'
then timestamp + interval '1' minute
else next_ts end as out_ts,
case when direction = 'OUT' and (last_dir is null or last_dir = 'OUT')
then 'Error IN' else 'OK' end as in_error,
case when direction = 'IN' and (next_dir is null or next_dir = 'IN')
then 'Error OUT' else 'OK' end as out_error
from t
order by placard, in_ts;
PLACARD IN_TS OUT_TS IN_ERROR OUT_ERROR
---------- ----- ------ -------- ---------
2533 15:36 15:41 OK OK
2533 15:51 15:52 Error IN OK
2533 15:56 15:57 OK Error OUT
2533 16:02 16:07 OK OK
2533 16:10 16:11 OK Error OUT
最后,您可以为输入和输出记录将其反打印回单独的行:
with t as (
...
)
select * from (
select distinct placard,
case when direction = 'IN' then timestamp
when last_dir is null or last_dir = 'OUT'
then timestamp - interval '1' minute
else last_ts end as in_ts,
case when direction = 'OUT' then timestamp
when next_dir is null or next_dir = 'IN'
then timestamp + interval '1' minute
else next_ts end as out_ts,
case when direction = 'OUT' and (last_dir is null or last_dir = 'OUT')
then 'Error IN' else 'OK' end as in_error,
case when direction = 'IN' and (next_dir is null or next_dir = 'IN')
then 'Error OUT' else 'OK' end as out_error
from t
)
unpivot ((timestamp, error) for direction in
((in_ts, in_error) as 'IN', (out_ts, out_error) as 'OUT'))
order by placard, timestamp;
PLACARD DIRECTION TIMESTAMP ERROR
---------- --------- --------- ---------
2533 IN 15:36 OK
2533 OUT 15:41 OK
2533 IN 15:51 Error IN
2533 OUT 15:52 OK
2533 IN 15:56 OK
2533 OUT 15:57 Error OUT
2533 IN 16:02 OK
2533 OUT 16:07 OK
2533 IN 16:10 OK
2533 OUT 16:11 Error OUT
,包括第二个标牌。您需要识别输入/输出值对,其中一些可能缺失。在结果集中创建一个额外的行并不简单(如Aramillo所示,至少不需要敲表两次);您可以使用lead和lag来发现行不存在,但创建行则是另一回事 一种方法是使用lead和lag将数据压缩到in/out对中,这将为缺少的值留下空白,填补空白,然后再次扩展。使用稍微修改的起始数据:
select placard, timestamp, direction
from t42 order by placard, timestamp;
PLACARD TIMESTAMP DIRECTION
---------- --------- ---------
2533 15:36 IN
2533 15:41 OUT
2533 15:52 OUT
2533 15:56 IN
2533 16:02 IN
2533 16:07 OUT
2533 16:10 IN
您可以在后面和前面达到峰值:
select placard, timestamp, direction,
lag(direction) over (partition by placard order by timestamp) as last_dir,
lead(direction) over (partition by placard order by timestamp) as next_dir,
lag(timestamp) over (partition by placard order by timestamp) as last_ts,
lead(timestamp) over (partition by placard order by timestamp) as next_ts
from t42;
PLACARD TIMESTAMP DIRECTION LAST_DIR NEXT_DIR LAST_TS NEXT_TS
---------- --------- --------- -------- -------- ------- -------
2533 15:36 IN OUT 15:41
2533 15:41 OUT IN OUT 15:36 15:52
2533 15:52 OUT OUT IN 15:41 15:56
2533 15:56 IN OUT IN 15:52 16:02
2533 16:02 IN IN OUT 15:56 16:07
2533 16:07 OUT IN IN 16:02 16:10
2533 16:10 IN OUT 16:07
和用例语句作为一种手动轴心:
with t as (
...
)
select placard, timestamp, direction,
case when direction = 'IN' then timestamp
when last_dir is null or last_dir = 'OUT'
then timestamp - interval '1' minute
else last_ts end as in_ts,
case when direction = 'OUT' then timestamp
when next_dir is null or next_dir = 'IN'
then timestamp + interval '1' minute
else next_ts end as out_ts,
case when direction = 'OUT' and (last_dir is null or last_dir = 'OUT')
then 'Error IN' else 'OK' end as in_error,
case when direction = 'IN' and (next_dir is null or next_dir = 'IN')
then 'Error OUT' else 'OK' end as out_error
from t
order by placard, timestamp;
PLACARD TIMESTAMP DIRECTION IN_TS OUT_TS IN_ERROR OUT_ERROR
---------- --------- --------- ----- ------ -------- ---------
2533 15:36 IN 15:36 15:41 OK OK
2533 15:41 OUT 15:36 15:41 OK OK
2533 15:52 OUT 15:51 15:52 Error IN OK
2533 15:56 IN 15:56 15:57 OK Error OUT
2533 16:02 IN 16:02 16:07 OK OK
2533 16:07 OUT 16:02 16:07 OK OK
2533 16:10 IN 16:10 16:11 OK Error OUT
现在,对于最初有两个值的对,它有重复的值,对于没有的对,它有一分钟的调整值;丢失原始方向行和时间戳行,您可以进入核心对:
with t as (
...
)
select distinct placard,
case when direction = 'IN' then timestamp
when last_dir is null or last_dir = 'OUT'
then timestamp - interval '1' minute
else last_ts end as in_ts,
case when direction = 'OUT' then timestamp
when next_dir is null or next_dir = 'IN'
then timestamp + interval '1' minute
else next_ts end as out_ts,
case when direction = 'OUT' and (last_dir is null or last_dir = 'OUT')
then 'Error IN' else 'OK' end as in_error,
case when direction = 'IN' and (next_dir is null or next_dir = 'IN')
then 'Error OUT' else 'OK' end as out_error
from t
order by placard, in_ts;
PLACARD IN_TS OUT_TS IN_ERROR OUT_ERROR
---------- ----- ------ -------- ---------
2533 15:36 15:41 OK OK
2533 15:51 15:52 Error IN OK
2533 15:56 15:57 OK Error OUT
2533 16:02 16:07 OK OK
2533 16:10 16:11 OK Error OUT
最后,您可以为输入和输出记录将其反打印回单独的行:
with t as (
...
)
select * from (
select distinct placard,
case when direction = 'IN' then timestamp
when last_dir is null or last_dir = 'OUT'
then timestamp - interval '1' minute
else last_ts end as in_ts,
case when direction = 'OUT' then timestamp
when next_dir is null or next_dir = 'IN'
then timestamp + interval '1' minute
else next_ts end as out_ts,
case when direction = 'OUT' and (last_dir is null or last_dir = 'OUT')
then 'Error IN' else 'OK' end as in_error,
case when direction = 'IN' and (next_dir is null or next_dir = 'IN')
then 'Error OUT' else 'OK' end as out_error
from t
)
unpivot ((timestamp, error) for direction in
((in_ts, in_error) as 'IN', (out_ts, out_error) as 'OUT'))
order by placard, timestamp;
PLACARD DIRECTION TIMESTAMP ERROR
---------- --------- --------- ---------
2533 IN 15:36 OK
2533 OUT 15:41 OK
2533 IN 15:51 Error IN
2533 OUT 15:52 OK
2533 IN 15:56 OK
2533 OUT 15:57 Error OUT
2533 IN 16:02 OK
2533 OUT 16:07 OK
2533 IN 16:10 OK
2533 OUT 16:11 Error OUT
,包括第二张海报。使用分析函数
LAG
。是的,我一直在读这些,但仍然觉得有点难理解脚本。使用分析函数LAG
。是的,我一直在读这些,但仍然觉得有点难理解脚本。好的,因此@Aramillo使在结果集中创建行看起来很简单。*8-)但这只会在表中出现一次,这对您可能并不重要。还忘了说CTE不是真正必要的,因为超前/滞后可以在主查询中重复,但我发现代码分离出来后可读性更强。这很重要。我必须通过我们公司的一个复杂的变更请求来处理每一个查询,并且在投入生产之前必须得到批准,而且查询的次数越少,就越重要。很多好的,您的查询非常有效。有一件事。当标语牌更改时,最后一条是IN,文本记录的时间戳为NULL,错误为OK。思想?非常感谢@user1753599-抱歉,我有点假设第一个和最后一个记录是正确的,因为每个公告都是正确的。修正了,没问题。完美的非常感谢。亚历克斯,最后一个问题。因为我需要“规范化”查询,所以我需要在每个列中输入和输出时间戳,每次访问公告牌。我的意思是,如果有6对输入/输出,那意味着3次访问。我试图使用coalesce,但它将来自同一个标语牌的所有访问分组。我没有一个“访问”字段可以在小组中使用。您是否认为有可能在每对记录中添加一个访问id(连续编号)?不管两条记录上的数字是否相同?再次感谢。好的,@Aramillo使在结果集中创建行看起来很简单。*8-)但这只会在表中出现一次,这对您可能并不重要。还忘了说CTE不是真正必要的,因为超前/滞后可以在主查询中重复,但我发现代码分离出来后可读性更强。这很重要。我必须通过我们公司的一个复杂的变更请求来处理每一个查询,并且在投入生产之前必须得到批准,而且查询的次数越少,就越重要。很多好的,您的查询非常有效。有一件事。当标语牌更改时,最后一条是IN,文本记录的时间戳为NULL,错误为OK。思想?非常感谢@user1753599-抱歉,我有点假设第一个和最后一个记录是正确的,因为每个公告都是正确的。修正了,没问题。完美的非常感谢。亚历克斯,最后一个问题。因为我需要“规范化”查询,所以我需要在每个列中输入和输出时间戳,每次访问公告牌。我的意思是,如果有6对输入/输出,那意味着3次访问。我试图使用coalesce,但它将来自同一个标语牌的所有访问分组。我没有一个“访问”字段可以在小组中使用。您认为是否有可能添加访问id(连续编号)