Oracle sql 动态比较 table/ 数据集中的行

Oracle sql compare rows within table / data set dynamically

以下问题:

我想比较 table 中的行。

假设我有一个测试用例 table 具有以下数据星座:

ID  Result  Date

123 success 29.04.2021
123 error   28.04.2021
123 success 27.04.2021
123 success 26.04.2021
123 error   25.04.2021

234 success 29.04.2021
234 success 28.04.2021
234 success 27.04.2021
234 success 26.04.2021
234 error   25.04.2021

我希望我的查询 return 仅查询过去 3 天结果相同的那些 ID。 但是我想在不使用 PL/SQL 的情况下动态处理 days 参数。 这意味着 - 如果我需要比较最近 5 天,我只想更改 sql 语句中的参数。 能实现吗?

回到我上面的例子:

我希望我的查询返回:

ID
234

此致

从 Oracle 12c 开始,您可以使用 MATCH_RECOGNIZE:

SELECT id
FROM   table_name
MATCH_RECOGNIZE (
  PARTITION BY id
  ORDER BY "DATE"
  ONE ROW PER MATCH
  PATTERN ( same{3} $ )
  DEFINE same AS FIRST( result ) = result
)

或者,在早期版本中,您可以使用:

SELECT id
FROM   (
  SELECT id,
         ROW_NUMBER() OVER ( PARTITION BY id ORDER BY "DATE" DESC )
           AS id_rn,
         ROW_NUMBER() OVER ( PARTITION BY id, result ORDER BY "DATE" DESC )
           AS result_rn
  FROM   table_name
)
WHERE id_rn = 3
AND   result_rn = 3

其中,对于示例数据:

CREATE TABLE table_name ( ID, Result, "DATE" ) AS
SELECT 123, 'success', DATE '2021-04-29' FROM DUAL UNION ALL
SELECT 123, 'error',   DATE '2021-04-28' FROM DUAL UNION ALL
SELECT 123, 'success', DATE '2021-04-27' FROM DUAL UNION ALL
SELECT 123, 'success', DATE '2021-04-26' FROM DUAL UNION ALL
SELECT 123, 'error',   DATE '2021-04-25' FROM DUAL UNION ALL
SELECT 234, 'success', DATE '2021-04-29' FROM DUAL UNION ALL
SELECT 234, 'success', DATE '2021-04-28' FROM DUAL UNION ALL
SELECT 234, 'success', DATE '2021-04-27' FROM DUAL UNION ALL
SELECT 234, 'success', DATE '2021-04-26' FROM DUAL UNION ALL
SELECT 234, 'error',   DATE '2021-04-25' FROM DUAL;

输出:

ID
234

db<>fiddle here

您可以使用简单的 group by + having 子句:

select id
from t
where dt>=sysdate-3
group by id
having count(distinct result)=1 -- just one distinct `result`
and count(*)=3 -- only 3 rows >= sysdate-3
and count(distinct dt)=3 -- all 3 days are present;

带有测试数据的完整测试:

with t(ID, Result, Dt) as (
select 123, 'success' , to_date('29.04.2021','dd.mm.yyyy') from dual union all
select 123, 'error  ' , to_date('28.04.2021','dd.mm.yyyy') from dual union all
select 123, 'success' , to_date('27.04.2021','dd.mm.yyyy') from dual union all
select 123, 'success' , to_date('26.04.2021','dd.mm.yyyy') from dual union all
select 123, 'error  ' , to_date('25.04.2021','dd.mm.yyyy') from dual union all
select 234, 'success' , to_date('29.04.2021','dd.mm.yyyy') from dual union all
select 234, 'success' , to_date('28.04.2021','dd.mm.yyyy') from dual union all
select 234, 'success' , to_date('27.04.2021','dd.mm.yyyy') from dual union all
select 234, 'success' , to_date('26.04.2021','dd.mm.yyyy') from dual union all
select 234, 'error  ' , to_date('25.04.2021','dd.mm.yyyy') from dual
)
select id
from t
where dt>=sysdate-3
group by id
having count(distinct result)=1 -- just one distinct `result`
and count(*)=3 -- only 3 rows >= sysdate-3
and count(distinct dt)=3 -- all 3 days are present
;

结果:

        ID
----------
       234

根据以上评论中的说明修改查询:

I want to check last 3 days starting from today. If I only have two entries then I would like to compare only two entries...

select id
from t
where dt>=sysdate-3
group by id
having count(distinct result)=1 -- just one distinct `result`
;

完整测试用例:

with t(ID, Result, Dt) as (
select 123, 'success' , to_date('29.04.2021','dd.mm.yyyy') from dual union all
select 123, 'error  ' , to_date('28.04.2021','dd.mm.yyyy') from dual union all
select 123, 'success' , to_date('27.04.2021','dd.mm.yyyy') from dual union all
select 123, 'success' , to_date('26.04.2021','dd.mm.yyyy') from dual union all
select 123, 'error  ' , to_date('25.04.2021','dd.mm.yyyy') from dual union all
select 234, 'success' , to_date('29.04.2021','dd.mm.yyyy') from dual union all
select 234, 'success' , to_date('28.04.2021','dd.mm.yyyy') from dual union all
select 234, 'success' , to_date('27.04.2021','dd.mm.yyyy') from dual union all
select 234, 'success' , to_date('26.04.2021','dd.mm.yyyy') from dual union all
select 234, 'error  ' , to_date('25.04.2021','dd.mm.yyyy') from dual
)
select id
from t
where dt>=sysdate-3
group by id
having count(distinct result)=1 -- just one distinct `result`
;
----------------------
--Results:

        ID
----------
       234

PS。最好以这种格式给出测试数据:

with t(ID, Result, Dt) as (
select 123, 'success' , to_date('29.04.2021','dd.mm.yyyy') from dual union all
select 123, 'error  ' , to_date('28.04.2021','dd.mm.yyyy') from dual union all
select 123, 'success' , to_date('27.04.2021','dd.mm.yyyy') from dual union all
select 123, 'success' , to_date('26.04.2021','dd.mm.yyyy') from dual union all
select 123, 'error  ' , to_date('25.04.2021','dd.mm.yyyy') from dual union all
select 234, 'success' , to_date('29.04.2021','dd.mm.yyyy') from dual union all
select 234, 'success' , to_date('28.04.2021','dd.mm.yyyy') from dual union all
select 234, 'success' , to_date('27.04.2021','dd.mm.yyyy') from dual union all
select 234, 'success' , to_date('26.04.2021','dd.mm.yyyy') from dual union all
select 234, 'error  ' , to_date('25.04.2021','dd.mm.yyyy') from dual
)
select *
from t;

假设你每天每个id一行,那么你可以使用lag():

select t.*
from (select t.*,
             lag(result, 2) over (partition by id order by date) as date_2,
             lag(result, 2) over (partition by id, result order by date) as date_result_2
      from t
     ) t
where date_2 = date_result_2;

这 returns 前两行(假设每天每个 id 一行)的所有行具有相同的结果。

您可以在您关心的时间范围内添加过滤器。这可能应该在外部查询中。

它的作用是查看过去的两行,但有两种不同的方式:

  • id一次。
  • idresult
  • 一次

如果它们相同,则前两行的结果相同,中间的行也相同。