我有一个包含两列的表格:
OLD_REVISIONS |NEW_REVISIONS
-----------------------------------
1,25,26,24 |1,26,24,25
1,56,55,54 |1,55,54
1 |1
1,2 |1
1,96,95,94 |1,96,94,95
1 |1
1 |1
1 |1
1 |1
1,2 |1,2
1 |1
1 |1
1 |1
1 |1
对于每一行,都会有一份文件修订清单(以逗号分隔)
逗号分隔列表在两列中可能相同但订单/排序可能不同 - 例如
2,1 |1,2
我想找到OLD_REVISIONS列中最高版本低于NEW_REVISIONS中最高版本的所有实例
以下符合该标准
OLD_REVISIONS |NEW_REVISIONS
-----------------------------------
1,2 |1
1,56,55,54 |1,55,54
我尝试了使用MINUS选项的解决方案(将表连接到自身),但即使列表相同但顺序错误,它也会返回差异
我尝试了GREATEST函数(即greatest(new_Revisions) < greatest(old_revisions)
),但我不确定为什么最大(OLD_REVISIONS)总是只返回逗号分隔值。它不返回最大值。我怀疑它是在比较字符串,因为列是VARCHAR。
此外,MAX
函数需要一个数字。
我可以通过另一种方式实现上述目标吗?我正在寻找一个纯SQL选项,所以我可以打印出结果(或者可以打印出结果的PL / SQL选项)
抱歉没有提到这个但是对于NEW_REVISIONS我确实在表格中有数据,其中每个修订都在一个单独的行中:
"DOCNUMBER" "REVISIONNUMBER"
67 1
67 24
67 25
67 26
75 1
75 54
75 55
75 56
78 1
79 1
79 2
83 1
83 96
83 94
仅仅提供一些内容,几周前我怀疑有修改消失了。 为了对此进行调查,我决定对所有文档的所有修订进行计数,然后拍摄快照以便稍后进行比较,看看是否确实缺少修订。
我拍摄的快照包含以下列:
docnumber, count, revisions
使用listagg函数将修订存储在逗号分隔列表中。
我现在遇到的麻烦就是现场表,新版本已经添加,所以当我使用MINUS比较主表和快照时,我会有所不同,因为 主表中的新修订版。
即使在实际表中,修订是单独的行,但在快照表中我没有单独的行。
我在考虑以相同格式重新创建快照的唯一方法,并比较它们以确定主表中的最大修订是否低于快照表中的最大修订(因此我试图找出如何查找以逗号分隔的字符串输出最大值)
答案 0 :(得分:3)
享受。
select xmlcast(xmlquery(('max((' || OLD_REVISIONS || '))') RETURNING CONTENT) as int) as OLD_REVISIONS_max
,xmlcast(xmlquery(('max((' || NEW_REVISIONS || '))') RETURNING CONTENT) as int) as NEW_REVISIONS_max
from t
;
答案 1 :(得分:2)
一种方法是使用regexp_substr
在逗号分隔上拆分列,并检查最大值和最小值是否不同。
with rownums as (select t.*,row_number() over(order by old_revisions) rn from t)
select old_revisions,new_revisions
from rownums
where rn in (select rn
from rownums
group by rn
connect by regexp_substr(old_revisions, '[^,]+', 1, level) is not null
or regexp_substr(new_revisions, '[^,]+', 1, level) is not null
having max(cast(regexp_substr(old_revisions,'[^,]+', 1, level) as int))
<> max(cast(regexp_substr(new_revisions,'[^,]+', 1, level) as int))
)
答案 2 :(得分:2)
假设您的基表有一个id
列(什么的版本?) - 这是一个基于拆分行的解决方案。
修改:如果你喜欢这个解决方案,请查看vkp的解决方案,这比我的更好。我在回答他的答案时解释为什么他的解决方案更好。
with
t ( id, old_revisions, new_revisions ) as (
select 101, '1,25,26,24', '1,26,24,25' from dual union all
select 102, '1,56,55,54', '1,55,54' from dual union all
select 103, '1' , '1' from dual union all
select 104, '1,2' , '1' from dual union all
select 105, '1,96,95,94', '1,96,94,95' from dual union all
select 106, '1' , '1' from dual union all
select 107, '1' , '1' from dual union all
select 108, '1' , '1' from dual union all
select 109, '1' , '1' from dual union all
select 110, '1,2' , '1,2' from dual union all
select 111, '1' , '1' from dual union all
select 112, '1' , '1' from dual union all
select 113, '1' , '1' from dual union all
select 114, '1' , '1' from dual
)
-- END of TEST DATA; the actual solution (SQL query) begins below.
select id, old_revisions, new_revisions
from (
select id, old_revisions, new_revisions, 'old' as flag,
to_number(regexp_substr(old_revisions, '\d+', 1, level)) as rev_no
from t
connect by level <= regexp_count(old_revisions, ',') + 1
and prior id = id
and prior sys_guid() is not null
union all
select id, old_revisions, new_revisions, 'new' as flag,
to_number(regexp_substr(new_revisions, '\d+', 1, level)) as rev_no
from t
connect by level <= regexp_count(new_revisions, ',') + 1
and prior id = id
and prior sys_guid() is not null
)
group by id, old_revisions, new_revisions
having max(case when flag = 'old' then rev_no end) !=
max(case when flag = 'new' then rev_no end)
order by id -- ORDER BY is optional
;
ID OLD_REVISION NEW_REVISION
--- ------------ ------------
102 1,56,55,54 1,55,54
104 1,2 1
答案 3 :(得分:1)
评论说标准化数据。我同意,但我也理解这可能是不可能的。我会尝试下面的查询:
select greatest(val1, val2), t1.r from (
select max(val) val1, r from (
select regexp_substr(v1,'[^,]+', 1, level) val, rowid r from tab1
connect by regexp_substr(v1, '[^,]+', 1, level) is not null
) group by r) t1
inner join (
select max(val) val2, r from (
select regexp_substr(v2,'[^,]+', 1, level) val, rowid r from tab1
connect by regexp_substr(v2, '[^,]+', 1, level) is not null
) group by r) t2
on (t1.r = t2.r);
经过测试:
create table tab1 (v1 varchar2(100), v2 varchar2(100));
insert into tab1 values ('1,3,5','1,4,7');
insert into tab1 values ('1,3,5','1,2,9');
insert into tab1 values ('1,3,5','1,3,5');
insert into tab1 values ('1,3,5','1,4');
似乎工作正常。我留下了rowid作为参考。我猜你桌上有一些内容。
编辑完成后,我会将查询更改为:
select greatest(val1, val2), t1.r from (
select max(val) val1, r from (
select regexp_substr(v1,'[^,]+', 1, level) val, DOCNUMBER r from tab1
connect by regexp_substr(v1, '[^,]+', 1, level) is not null
) group by DOCNUMBER) t1
inner join (
select max(DOCNUMBER) val2, DOCNUMBER r from NEW_REVISIONS) t2
on (t1.r = t2.r);
答案 4 :(得分:1)
您可以使用listagg函数以相同的顺序将修订版本放在一起来比较每个值。
SELECT listagg(o,',') WITHIN GROUP (ORDER BY o) old_revisions,
listagg(n,',') WITHIN GROUP (ORDER BY n) new_revisions
FROM (
SELECT DISTINCT rowid r,
regexp_substr(old_revisions, '[^,]+', 1, LEVEL) o,
regexp_substr(new_revisions, '[^,]+', 1, LEVEL) n
FROM table
WHERE regexp_substr(old_revisions, '[^,]+', 1, LEVEL) IS NOT NULL
CONNECT BY LEVEL<=(SELECT greatest(MAX(regexp_count(old_revisions,',')),MAX(regexp_count(new_revisions,',')))+1 c FROM table)
)
GROUP BY r
HAVING listagg(o,',') WITHIN GROUP (ORDER BY o)<>listagg(n,',') WITHIN GROUP (ORDER BY n);
答案 5 :(得分:1)
这可能是一种方式:
select
OLD_REVISIONS,
NEW_REVISIONS
from
REVISIONS t,
table(cast(multiset(
select level
from dual
connect by level <= length (regexp_replace(t.OLD_REVISIONS, '[^,]+')) + 1
) as sys.OdciNumberList
)
) levels_old,
table(cast(multiset(
select level
from dual
connect by level <= length (regexp_replace(t.NEW_REVISIONS, '[^,]+')) + 1
)as sys.OdciNumberList
)
) levels_new
group by t.ROWID,
OLD_REVISIONS,
NEW_REVISIONS
having max(to_number(trim(regexp_substr(t.OLD_REVISIONS, '[^,]+', 1, levels_old.column_value)))) >
max(to_number(trim(regexp_substr(t.new_REVISIONS, '[^,]+', 1, levels_new.column_value))))
这使用双字符串拆分来从每个字段中选择值,然后只需找到两个集合中的最大值与您的要求相匹配的行。 您应该通过在GROUP BYclause中添加一些唯一键来编辑它,如果您没有在桌面上有任何唯一键,则应该使用rowid。
答案 6 :(得分:0)
您可以编写解析字符串并返回最大数字的PL / SQL函数
select max_num( '1,26,24,25') max_num from dual;
MAX_NUM
----------
26
查询非常简单:
select OLD_REVISIONS NEW_REVISIONS
from revs
where max_num(OLD_REVISIONS) < max_num(NEW_REVISIONS);
没有验证和错误处理的原型功能
create or replace function max_num(str_in VARCHAR2) return NUMBER as
i number;
x varchar2(1);
n number := 0;
max_n number := 0;
pow number := 0;
begin
for i in 0.. length(str_in)-1 loop
x := substr(str_in,length(str_in)-i,1);
if x = ',' then
-- check max number
if n > max_n then
max_n := n;
end if;
-- reset
n := 0;
pow := 0;
else
n := n + to_number(x)*power(10,pow);
pow := pow +1;
end if;
end loop;
return(max_n);
end;
/