Oracle:根据数据集替换选项字符串 - 这可能吗?
Posted
技术标签:
【中文标题】Oracle:根据数据集替换选项字符串 - 这可能吗?【英文标题】:Oracle : replace string of options based on data set - is this possible? 【发布时间】:2021-01-12 14:11:37 【问题描述】:表格中的列如下所示:
PATTERN
([option1]+[option2])*([option3]+[option4])
([option1]+[option2])*([option3]+[option4])*([option6]+[option7])
[option1]+[option6]
([option1]+[option2])*([option8]+[option9])
([option1]+[option2])*[option4]
[option10]
每个选项都有一些价值。
有一个表 - 我们称它为 option_set
,记录看起来像
OPTION VALUE
option1 3653265
option2 26452
option3 73552
option3 100
option4 1235
option5 42565
option6 2330
option7 544
option9 2150
我想将选项名称替换为第一个表中的数字,如果存在当然,如果不存在则=0。 我已经在 PLSQL 中完成了这个(获取模式,遍历每个选项,如果存在 - regexp_replace), 但我想知道这是否可以在 SQL 中完成? 我的目标是替换当前 OPTION_SET 的所有模式的值并仅获取所有方程式都大于 0 的记录。当然 - 我无法在 SQL 中运行这个方程式,所以我想到了类似
for rec in
(
SELECT...
)
loop
execute immediate '...';
if above_equation > 0 then ..
end loop;
任何想法将不胜感激
【问题讨论】:
和
是否包围了整个表达式而不出现在表达式本身中?
是的,它们是 和 ,在这个过程中它们被剪掉了。如果您有一些不包含括号的想法,请随时删除它们
您的“选项”表有两次选项 3。这没有任何意义。
我的错误。对不起
【参考方案1】:
您可以在 SQL 中使用递归 CTE 执行类似循环的查询,在每次迭代时替换新标记,这样您就可以替换所有标记。
我知道在 Oracle 的 SQL 语句中执行动态查询的唯一方法是 DBMS_XMLGEN
包,因此您可以在不使用 PL/SQL 的情况下评估表达式并按结果值进行过滤。但是对于具有模式和选项的低基数表来说,所有这些都是可行的。
代码如下:
身份证 |图案 | REPL_PATTERN | CALCULATED_VALUE -: | :------------------------------------------------ ------------------ | :---------------------------------------------------- | :--------------- 1 | ([选项1]+[选项2])*([选项3]+[选项4]) | (3653265+26452)*(73552+1235) | 275194995279 2 | ([option1]+[option2])*([option3]+[option4])*([option6]+[option7]) | (3653265+26452)*(73552+1235)*(2330+544) | 790910416431846 3 | [选项1]+[选项6] | 3653265+2330 | 3655595 5 | ([选项1]+[选项2])*[选项4] | (3653265+26452)*1235 | 4544450495with a as ( select 1 as id, '([option1]+[option2])*([option3]+[option4])' as pattern from dual union all select 2 as id, '([option1]+[option2])*([option3]+[option4])*([option6]+[option7])' as pattern from dual union all select 3 as id, '[option1]+[option6]' as pattern from dual union all select 4 as id, '([option1]+[option2])*([option8]+[option9])' as pattern from dual union all select 5 as id, '([option1]+[option2])*[option4]' as pattern from dual union all select 6 as id, '[option10]]' as pattern from dual ) , opt as ( select 'option1' as opt, 3653265 as val from dual union all select 'option2' as opt, 26452 as val from dual union all select 'option3' as opt, 73552 as val from dual union all select 'option3' as opt, 100 as val from dual union all select 'option4' as opt, 1235 as val from dual union all select 'option5' as opt, 42565 as val from dual union all select 'option6' as opt, 2330 as val from dual union all select 'option7' as opt, 544 as val from dual union all select 'option9' as opt, 2150 as val from dual ) , opt_ordered as ( /*Order options to iterate over*/ select opt.*, row_number() over(order by 1) as rn from opt ) , rec (id, pattern, repl_pattern, lvl) as ( select id, pattern, pattern as repl_pattern, 0 as lvl from a union all select r.id, r.pattern, /*Replace each part at new step*/ replace(r.repl_pattern, '[' || o.opt || ']', o.val), r.lvl + 1 from rec r join opt_ordered o on r.lvl + 1 = o.rn ) , out_prepared as ( select rec.*, case when instr(repl_pattern, '[') = 0 /*When there's no more not parsed expressions, then we can try to evaluate them*/ then dbms_xmlgen.getxmltype( 'select ' || replace(replace(repl_pattern, '', ''), '', '') || ' as v from dual' ) /*Otherwise SQL statement will fail*/ end as parsed_expr from rec /*Retrieve the last step*/ where lvl = (select max(rn) from opt_ordered) ) select id, pattern, repl_pattern, extractvalue(parsed_expr, '/ROWSET/ROW/V') as calculated_value from out_prepared o where extractvalue(parsed_expr, '/ROWSET/ROW/V') > 0
db小提琴here
【讨论】:
太棒了!完美的!一个问题——你能向我解释一下递归查询吗?这是如何工作的,或者你知道一些好的教程吗? @q4za4 在in the documentation 上有一个简短的描述。在那篇文章的最后,你可以找到一些例子。简而言之:有一个 anchor 成员,它创建基础数据,递归从这里开始,还有一个 recursive member,它在from
中使用相同的 with
名称或join
子句增加递归步骤。在每一步,由递归成员生成的数据被添加到该with
子查询的现有数据中。当新步骤没有新数据时,递归停止。【参考方案2】:
这是执行此操作的一种方法。有很多东西要打开,所以请坚持住。
我将测试数据包含在with
子句中。当然,你不需要那个;只需删除两个“表”并在查询中使用您的实际表名和列名。
从 Oracle 12.1 开始,我们可以直接在顶部的with
子句中定义 PL/SQL 函数;如果我们这样做,查询必须以斜杠 (/) 结束,而不是通常的分号 (;)。如果你的版本早于 12.1,你可以单独定义函数。我使用的函数采用“算术表达式”(表示复合算术运算的字符串)并将其值作为数字返回。它使用本机动态 SQL(“立即执行”语句),这将导致查询相对较慢,因为为每一行解析不同的游标。如果速度成为问题,可以更改它,使用绑定变量(这样光标只解析一次)。
with
子句中的递归查询将每个占位符替换为“选项”表的相应值。如果“占位符”在表中没有对应的选项,或者有但对应的值为null
,我使用 0。 (请注意,您的示例数据显示 option3
两次;这没有任何意义,我从示例数据中删除了一次。)
我没有一次替换一个占位符,而是采取了相反的方法;假设模式可能很长,但“选项”的数量很少,这应该更有效。即:在每一步,我一次替换所有出现的'[optionN]'
(对于给定的N
)。在递归查询之外,我将“不存在”选项的所有占位符替换为 0。
请注意,递归 with
子句需要 Oracle 11.2。如果您的版本比这更早(尽管不应该如此),还有其他方法;您可能还需要在 PL/SQL 中执行此操作。
所以,这里是 - 一个单一的 SELECT
查询整个事情:
with
function expr_eval(pattern varchar2) return number as
x number;
begin
execute immediate 'select ' || pattern || ' from dual' into x;
return x;
end;
p (id, pattern) as (
select 1, '([option1]+[option2])*([option3]+[option4])' from dual union all
select 2, '([option1]+[option2])*([option3]+[option4])*([option6]+[option7])' from dual union all
select 3, '[option1]+[option6]' from dual union all
select 4, '([option1]+[option2])*([option8]+[option9])' from dual union all
select 5, '([option1]+[option2])*[option4]' from dual union all
select 6, '[option10]' from dual union all
select 7, '[option2]/([option3]+[option8])-(300-[option2])/(0.1 *[option3])' from dual
)
, o (opt, val) as (
select 'option1', 3653265 from dual union all
select 'option2', 26452 from dual union all
select 'option3', 100 from dual union all
select 'option4', 1235 from dual union all
select 'option5', 42565 from dual union all
select 'option6', 2330 from dual union all
select 'option7', 544 from dual union all
select 'option9', 2150 from dual
)
, n (opt, val, rn, ct) as (
select opt, val, rownum, count(*) over ()
from o
)
, r (id, pattern, rn, ct) as (
select id, substr(pattern, 2, length(pattern) - 2), 1, null
from p
union all
select r.id, replace(r.pattern, '[' || n.opt || ']', nvl(to_char(n.val), 0)),
r.rn + 1, n.ct
from r join n on r.rn = n.rn
)
, ae (id, pattern) as (
select id, regexp_replace(pattern, '\[[^]]*]', '0')
from r
where rn = ct + 1
)
select id, expr_eval(pattern) as result
from ae
order by id
/
输出:
ID RESULT
---- ---------------
1 4912422195
2 14118301388430
3 3655595
4 7911391550
5 4544450495
6 0
7 2879.72
【讨论】:
太棒了!完美的!一个问题——你能向我解释一下递归查询吗?这是如何工作的,或者你知道一些好的教程吗? @q4za4 - 要查看它的作用,请在r
的定义之后添加select * from r
(并注释掉查询的其余部分,从ae
开始)。这将向您展示递归查询的作用。然后查看ae
的定义,看看我们真正想要从r
得到什么。除此之外,您还需要阅读有关递归查询的内容 - 此处无法仅在简短评论中对其进行解释。以上是关于Oracle:根据数据集替换选项字符串 - 这可能吗?的主要内容,如果未能解决你的问题,请参考以下文章
如何根据替换字符串在 Oracle Apex 中调整安装脚本?