Oracle:根据数据集替换选项字符串 - 这可能吗?
Oracle : replace string of options based on data set - is this possible?
我在 table 中的列如下所示:
PATTERN
{([option1]+[option2])*([option3]+[option4])}
{([option1]+[option2])*([option3]+[option4])*([option6]+[option7])}
{[option1]+[option6]}
{([option1]+[option2])*([option8]+[option9])}
{([option1]+[option2])*[option4]}
{[option10]}
每个选项都有多个值。
有一个 table - 我们称它为 option_set
并且记录看起来像
OPTION VALUE
option1 3653265
option2 26452
option3 73552
option3 100
option4 1235
option5 42565
option6 2330
option7 544
option9 2150
我想将第一个 table 中的选项名称替换为编号,当然如果存在,如果不存在则 =0。
我在 PLSQL 中完成了此操作(获取模式,检查每个选项,如果存在 - regexp_replace),
但我想知道这是否可以在 SQL 中完成??
我的目标是替换当前 OPTION_SET 的所有模式的值并仅获取所有方程都大于 0 的记录。当然 - 我不能 运行 SQL 中的这个方程, 所以我想到了
for rec in
(
SELECT...
)
loop
execute immediate '...';
if above_equation > 0 then ..
end loop;
如有任何想法,我们将不胜感激
您可以在 SQL 中使用递归 CTE 执行类似循环的查询,在每次迭代时替换新标记,这样您就可以替换所有标记。
我知道在 Oracle 中的 SQL 语句中执行动态查询的唯一方法是 DBMS_XMLGEN
包,因此您可以在没有 PL/SQL 的情况下计算表达式并按结果值过滤。但是所有这些对于具有模式和选项的低基数表都是可行的。
代码如下:
with 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
ID | PATTERN | REPL_PATTERN | CALCULATED_VALUE
-: | :------------------------------------------------------------------ | :---------------------------------------- | :---------------
1 | {([option1]+[option2])*([option3]+[option4])} | {(3653265+26452)*(73552+1235)} | 275194995279
2 | {([option1]+[option2])*([option3]+[option4])*([option6]+[option7])} | {(3653265+26452)*(73552+1235)*(2330+544)} | 790910416431846
3 | {[option1]+[option6]} | {3653265+2330} | 3655595
5 | {([option1]+[option2])*[option4]} | {(3653265+26452)*1235} | 4544450495
db<>fiddle here
这是一种方法。有很多东西要打开,所以要坚持。
我在 with
子句中包含了测试数据。当然,您不需要那个;只需删除两个“tables”并在查询中使用您实际的 table 和列名称。
从 Oracle 12.1 开始,我们可以直接在顶部的 with
子句中定义 PL/SQL 函数;如果我们这样做,查询必须以斜线 (/) 而不是通常的分号 (;) 结束。如果你的版本低于12.1,你可以单独定义函数。我使用的函数采用“算术表达式”(表示复合算术运算的字符串)和 returns 其值作为数字。它使用本机动态 SQL(“立即执行”语句),这将导致查询相对较慢,因为为每一行解析不同的游标。如果速度成为问题,这可以改变,使用绑定变量(这样游标只被解析一次)。
with
子句中的递归查询将每个占位符替换为“选项”table 的相应值。如果“占位符”在 table 中没有相应的选项,或者如果有但相应的值为 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
我在 table 中的列如下所示:
PATTERN
{([option1]+[option2])*([option3]+[option4])}
{([option1]+[option2])*([option3]+[option4])*([option6]+[option7])}
{[option1]+[option6]}
{([option1]+[option2])*([option8]+[option9])}
{([option1]+[option2])*[option4]}
{[option10]}
每个选项都有多个值。
有一个 table - 我们称它为 option_set
并且记录看起来像
OPTION VALUE
option1 3653265
option2 26452
option3 73552
option3 100
option4 1235
option5 42565
option6 2330
option7 544
option9 2150
我想将第一个 table 中的选项名称替换为编号,当然如果存在,如果不存在则 =0。 我在 PLSQL 中完成了此操作(获取模式,检查每个选项,如果存在 - regexp_replace), 但我想知道这是否可以在 SQL 中完成?? 我的目标是替换当前 OPTION_SET 的所有模式的值并仅获取所有方程都大于 0 的记录。当然 - 我不能 运行 SQL 中的这个方程, 所以我想到了
for rec in
(
SELECT...
)
loop
execute immediate '...';
if above_equation > 0 then ..
end loop;
如有任何想法,我们将不胜感激
您可以在 SQL 中使用递归 CTE 执行类似循环的查询,在每次迭代时替换新标记,这样您就可以替换所有标记。
我知道在 Oracle 中的 SQL 语句中执行动态查询的唯一方法是 DBMS_XMLGEN
包,因此您可以在没有 PL/SQL 的情况下计算表达式并按结果值过滤。但是所有这些对于具有模式和选项的低基数表都是可行的。
代码如下:
with 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
ID | PATTERN | REPL_PATTERN | CALCULATED_VALUE -: | :------------------------------------------------------------------ | :---------------------------------------- | :--------------- 1 | {([option1]+[option2])*([option3]+[option4])} | {(3653265+26452)*(73552+1235)} | 275194995279 2 | {([option1]+[option2])*([option3]+[option4])*([option6]+[option7])} | {(3653265+26452)*(73552+1235)*(2330+544)} | 790910416431846 3 | {[option1]+[option6]} | {3653265+2330} | 3655595 5 | {([option1]+[option2])*[option4]} | {(3653265+26452)*1235} | 4544450495
db<>fiddle here
这是一种方法。有很多东西要打开,所以要坚持。
我在 with
子句中包含了测试数据。当然,您不需要那个;只需删除两个“tables”并在查询中使用您实际的 table 和列名称。
从 Oracle 12.1 开始,我们可以直接在顶部的 with
子句中定义 PL/SQL 函数;如果我们这样做,查询必须以斜线 (/) 而不是通常的分号 (;) 结束。如果你的版本低于12.1,你可以单独定义函数。我使用的函数采用“算术表达式”(表示复合算术运算的字符串)和 returns 其值作为数字。它使用本机动态 SQL(“立即执行”语句),这将导致查询相对较慢,因为为每一行解析不同的游标。如果速度成为问题,这可以改变,使用绑定变量(这样游标只被解析一次)。
with
子句中的递归查询将每个占位符替换为“选项”table 的相应值。如果“占位符”在 table 中没有相应的选项,或者如果有但相应的值为 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