简体   繁体   English

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

[英]Oracle sql compare rows within table / data set dynamically

following issue:以下问题:

I want to compare the rows within a table with each other.我想将表中的行相互比较。

Let's assume I have a testcase table with below data constellation:假设我有一个包含以下数据星座的测试用例表:

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

I want my query to return only those ID's where the result was identical for the last 3 days.我希望我的查询仅返回过去 3 天结果相同的那些 ID。 But I want to handle the days parameter dynamically without using PL/SQL.但我想在不使用 PL/SQL 的情况下动态处理 days 参数。 That mean's - if I need to compare the last 5 days I just want to change the paramter in my sql statement.这意味着 - 如果我需要比较过去 5 天,我只想更改 sql 语句中的参数。 Can that be realized?这能实现吗?

Back to my example above:回到我上面的例子:

I want my query to give back:我希望我的查询回馈:

ID
234

Best regards此致

From Oracle 12c, you can use MATCH_RECOGNIZE :从 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
)

Or, in earlier versions, you can use:或者,在早期版本中,您可以使用:

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

Which, for the sample data:其中,对于样本数据:

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;

Outputs:输出:

ID ID
234 234

db<>fiddle here db<> 在这里摆弄

You can use just simple group by + having clause:您可以只使用简单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;

Full test cast with test data:带有测试数据的完整测试模型:

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
;

Results:结果:

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

Modified query as per a clarification in the comment above:根据上述评论中的说明修改了查询:

I want to check last 3 days starting from today.我想检查从今天开始的最后 3 天。 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`
;

Full test case:完整的测试用例:

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. PS。 It's better to give test data in this format:最好以这种格式给出测试数据:

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;

Assuming you have one row per id per day, then you can use lag() :假设您每天每个 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;

This returns all rows where the previous two rows (days assuming that there is one row per id per day) have the same result.这将返回前两行(假设每天每个 id 有一行)具有相同结果的所有行。

You can add filters on the time frame you care about.您可以在您关心的时间范围内添加过滤器。 This should probably be in the outer query.这可能应该在外部查询中。

What this does is look two rows in the past, but in two different ways:这样做是在过去看两行,但有两种不同的方式:

  • Once per id .每个id一次。
  • Once per id and result每个idresult一次

When these are the same, then the row two rows back has the same result and the row in-between does as well.当这些相同时,后面两行的结果相同,中间的行也一样。

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

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