У меня есть строка x-y+z
. Значения для x, y и z будут храниться в таблице. СкажемЗамена/замена значения в строке
x 10
y 15
z 20
Эта строка должна быть изменена как 10-15+20
.
В любом случае я могу достичь этого, используя plsql или sql?
У меня есть строка x-y+z
. Значения для x, y и z будут храниться в таблице. СкажемЗамена/замена значения в строке
x 10
y 15
z 20
Эта строка должна быть изменена как 10-15+20
.
В любом случае я могу достичь этого, используя plsql или sql?
с помощью простой Pivot мы можем сделать
DECLARE @Table1 TABLE
(name varchar(1), amount int )
;
INSERT INTO @Table1
(name , amount )
VALUES
('x', 10),
('y', 15),
('Z', 25);
Script
Select CAST([X] AS VARCHAR) +'-'+CAST([Y] AS VARCHAR)+'+'+CAST([Z] AS VARCHAR) from (
select * from @Table1)T
PIVOT (MAX(amount) FOR name in ([X],[y],[z]))p
, но такого рода требование не будет существовать в реальном сценарии. Я думаю, что для практической цели у вас есть разместил этот вопрос – mohan111
нет не для практических целей, его проектное требование, странное –
подход может быть следующее, предполагая таблицу, как это:
create table stringToNumbers(str varchar2(16), num number);
insert into stringToNumbers values ('x', 10);
insert into stringToNumbers values ('y', 20);
insert into stringToNumbers values ('zz', 30);
insert into stringToNumbers values ('w', 40);
Первый разметить ваш введите следующую строку:
SQL> with test as (select 'x+y-zz+w' as string from dual)
2 SELECT 'operand' as typ, level as lev, regexp_substr(string, '[+-]+', 1, level) as token
3 FROM test
4 CONNECT BY regexp_instr(string, '[a-z]+', 1, level+1) > 0
5 UNION ALL
6 SELECT 'value', level, regexp_substr(string, '[^+-]+', 1, level) as token
7 FROM test
8 CONNECT BY regexp_instr(string, '[+-]', 1, level - 1) > 0
9 order by lev asc, typ desc;
TYP LEV TOKEN
------- ---------- --------------------------------
value 1 x
operand 1 +
value 2 y
operand 2 -
value 3 zz
operand 3 +
value 4 w
В примере я использовал строчные буквы и только знаки +/-; вы можете легко отредактировать его, чтобы обработать что-то более сложное; Кроме того, я предполагаю, что входная строка хорошо сформирована.
Затем вы можете присоединиться к таблице декодирования токенизированной строке, построению конкатенации:
SQL> select listagg(nvl(to_char(num), token)) within group (order by lev asc, typ desc)
2 from (
3 with test as (select 'x+y-zz+w' as string from dual)
4 SELECT 'operand' as typ, level as lev, regexp_substr(string, '[+-]+', 1, level) as token
5 FROM test
6 CONNECT BY regexp_instr(string, '[a-z]+', 1, level+1) > 0
7 UNION ALL
8 SELECT 'value', level, regexp_substr(string, '[^+-]+', 1, level) as token
9 FROM test
10 CONNECT BY regexp_instr(string, '[+-]', 1, level - 1) > 0
11 order by lev asc, typ desc
12 ) tokens
13 LEFT OUTER JOIN stringToNumbers on (str = token);
LISTAGG(NVL(TO_CHAR(NUM),TOKEN))WITHINGROUP(ORDERBYLEVASC,TYPDESC)
--------------------------------------------------------------------------------
10+20-30+40
Это предполагает, что каждый буквальная в вас строка ввода имеет значение corrensponding в таблице. Вы даже можете обрабатывать случай строк без номера corrensponding, например, назначая 0:
SQL> select listagg(
2 case
3 when typ = 'operand' then token
4 else to_char(nvl(num, 0))
5 end
6 ) within group (order by lev asc, typ desc)
7 from (
8 with test as (select 'x+y-zz+w-UNKNOWN' as string from dual)
9 SELECT
.. ...
16 ) tokens
17 LEFT OUTER JOIN stringToNumbers on (str = token);
LISTAGG(CASEWHENTYP='OPERAND'THENTOKENELSETO_CHAR(NVL(NUM,0))END)WITHINGROUP(ORD
--------------------------------------------------------------------------------
10+20-30+40-0
Создать такую функцию:
create table ttt1
(name varchar(1), amount int )
;
INSERT INTO ttt1 VALUES ('x', 10);
INSERT INTO ttt1 VALUES ('y', 15);
INSERT INTO ttt1 VALUES ('z', 25);
CREATE OR REPLACE FUNCTION replace_vars (in_formula VARCHAR2)
RETURN VARCHAR2
IS
f VARCHAR2 (2000) := UPPER (in_formula);
BEGIN
FOR c1 IN ( SELECT UPPER (name) name, amount
FROM ttt1
ORDER BY name DESC)
LOOP
f := REPLACE (f, c1.name, c1.amount);
END LOOP;
return f;
END;
select replace_vars('x-y+z') from dual
Вот еще один способ подойти к проблеме, которая пытается это сделать все в SQL. Хотя это и не обязательно самый гибкий или быстрый, возможно, вы можете получить некоторые идеи по-другому, чтобы подойти к проблеме. Он также показывает способ выполнения окончательной формулы для получения ответа. См. Комментарии ниже.
Предполагается, что все переменные присутствуют в таблице переменных.
-- First build the table that holds the values. You won't need to do
-- this if you already have them in a table.
with val_tbl(x, y, z) as (
select '10', '15', '20' from dual
),
-- Table to hold the formula.
formula_tbl(formula) as (
select 'x-y+z' from dual
),
-- This table is built from a query that reads the formula a character at a time.
-- When a variable is found using the case statement, it is queried in the value
-- table and it's value is returned. Otherwise the operator is returned. This
-- results in a row for each character in the formula.
new_formula_tbl(id, new_formula) as (
select level, case regexp_substr(formula, '(.|$)', 1, level, NULL, 1)
when 'x' then
(select x from val_tbl)
when 'y' then
(select y from val_tbl)
when 'z' then
(select z from val_tbl)
else regexp_substr(formula, '(.|$)', 1, level, NULL, 1)
end
from formula_tbl
connect by level <= regexp_count(formula, '.')
)
-- select id, new_formula from new_formula_tbl;
-- This puts the rows back into a single string. Order by id (level) to keep operands
-- and operators in the right order.
select listagg(new_formula) within group (order by id) formula
from new_formula_tbl;
FORMULA
----------
10-15+20
Кроме того, вы можете получить результат формулы пропускания listagg() для следующей XMLQUERY функции():
select xmlquery(replace(listagg(new_formula) within group (order by id), '/', ' div ')
returning content).getNumberVal() as result
from new_formula_tbl;
RESULT
----------
15
как об использовании функции замены в вашем SQL? что-то вроде: select replace ('x + y-z', 'x', (выберите varValue из myTab, где id = 'x') – Plirkee