2016-08-11 3 views
0

У меня есть строка x-y+z. Значения для x, y и z будут храниться в таблице. СкажемЗамена/замена значения в строке

x 10 
y 15 
z 20 

Эта строка должна быть изменена как 10-15+20.

В любом случае я могу достичь этого, используя plsql или sql?

+0

как об использовании функции замены в вашем SQL? что-то вроде: select replace ('x + y-z', 'x', (выберите varValue из myTab, где id = 'x') – Plirkee

ответ

0

с помощью простой 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 
+0

, но такого рода требование не будет существовать в реальном сценарии. Я думаю, что для практической цели у вас есть разместил этот вопрос – mohan111

+0

нет не для практических целей, его проектное требование, странное –

0

подход может быть следующее, предполагая таблицу, как это:

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 
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 
0

Вот еще один способ подойти к проблеме, которая пытается это сделать все в 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 
Смежные вопросы