ORACLE SQL在基于另一个子字符串的大字符串中动态替换子字符串

时间:2019-03-04 22:30:39

标签: sql oracle replace

我有一个表,将SQL查询存储在字符串列中,如下所示。  (我在下面给出了3个示例行)

select sql_query_part_1_tx  from Table1;

sql_query_part_1_tx
--------------------------------------------------------
SELECT SUM(BAL_AM) FROM SCH.DERHCE03 WHERE 1=1  AND HIRHCC04<>'US' AND HIRHCE01 IN ('TDCD','TDOA','IRATD') AND HIREG039 IN ((660),(661),(604)) AND HIREG234 <> 345 ;
SELECT SUM(BAL_AM) FROM SCH.DEFD1520 WHERE 1=1  AND HIRHCV04='Y' AND HIRHCC04<>'US' AND HIRHCN17='FDIC' AND HIFD1516<>'Y' AND HIFD1527='FDIC';
SELECT SUM(OUTSTANDING_AM) FROM SCH.DEFD1520 WHERE 1=1  AND HIRHCV04='Y' AND HIRHCC04<>'US' AND HIRHCN17='FDIC' AND HIFD1516='Y';

因此,列sql_query_part_1_tx以字符串格式存储SQL查询。我的要求是读取这些查询并动态执行它们,并将结果存储在另一个表中。

在上面存储的查询中,挑战在于哪里存在<>(不等于)条件,它不获取具有NULL值的行(这是预期的)。但是我还需要具有NULL值的行。

例如:

SELECT SUM(BAL_AM) FROM SCH.DERHCE03 WHERE 1=1  AND HIRHCC04<>'US' AND HIRHCE01 IN ('TDCD','TDOA','IRATD') AND HIREG039 IN ((660),(661),(604)) AND HIREG234 <> 345 ;

在上面的查询字符串中,条件为HIRHCC04<>'US'。因此它将不会使用HIRHCC04 IS NULL来获取记录。

我想像下面一样更改上面的查询字符串,在条件中找到NVL的任何地方应用<>函数。

SELECT SUM(BAL_AM) FROM SCH.DERHCE03 WHERE 1=1  AND NVL(HIRHCC04,'###')<>'US' AND HIRHCE01 IN ('TDCD','TDOA','IRATD') AND HIREG039 IN ((660),(661),(604)) AND NVL(HIREG234,999999) <> 345 ;

这样我就可以获得NULL'HIRHCC04'的具有'HIREG234'值的行。

因此,我想更新我的表,该表使用NVLCOALESCE选项存储这些查询字符串。我希望我清楚。

注意:请注意,我们需要考虑数据类型和“禁止输入”条件。

2 个答案:

答案 0 :(得分:1)

以下查询应该可以解决问题:

SELECT REGEXP_REPLACE(sql_query_part_1_tx, '(\w+)\s*<>', 'COALESCE(\1, 9999) <>') 
FROM mytable;

正则表达式说明:

  • '(\w+)\s*<>'捕获<>左侧的单词(即连续的字母数字和下划线字符),并在两者之间使用可选的空格序列
  • 'COALESCE(\1, 9999) <>':将匹配的字符串替换为捕获的单词(列名),称为'\ 1'

demo on DB fiddle 及其示例数据将返回:

| REGEXP_REPLACE(SQL_QUERY_PART_1_TX,'(\W+)\S*<>','COALESCE(\1,9999)<>')                                                                                                                              |
| :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| SELECT SUM(BAL_AM) FROM SCH.DERHCE03 WHERE 1=1  AND COALESCE(HIRHCC04, 9999)<>'US' AND HIRHCE01 IN ('TDCD','TDOA','IRATD') AND HIREG039 IN ((660),(661),(604)) AND COALESCE(HIREG234, 9999)<> 345 ; |
| SELECT SUM(BAL_AM) FROM SCH.DEFD1520 WHERE 1=1  AND HIRHCV04='Y' AND COALESCE(HIRHCC04, 9999)<>'US' AND HIRHCN17='FDIC' AND COALESCE(HIFD1516, 9999)<>'Y' AND HIFD1527='FDIC';                      |
| SELECT SUM(OUTSTANDING_AM) FROM SCH.DEFD1520 WHERE 1=1  AND HIRHCV04='Y' AND COALESCE(HIRHCC04, 9999)<>'US' AND HIRHCN17='FDIC' AND HIFD1516='Y';                                                   |

顺便说一句,让我建议对您的替换逻辑进行优化。

此:

COALESCE(HIRHCC04, 9999)<>'US'

通常拼写更好:

(HIRHCC04 IS NULL OR HIRHCC04 <> 'US')

第二个表达式更明确,更高效,因为它将很高兴地使用要过滤的列上的现有索引(而第一个表达式则不会)。当要检查的列不是数字时,还可以避免潜在的转换问题。

可以使用以下正则表达式生成这些表达式:

SELECT REGEXP_REPLACE(
    sql_query_part_1_tx, 
    '(\w+)\s*<>\s*''(\w+)''', 
    '(\1 IS NULL OR \1 <> ''\2'')'
) 
FROM mytable;

Demo on DB Fiddle

| REGEXP_REPLACE(SQL_QUERY_PART_1_TX,'(\W+)\S*<>\S*''(\W+)''','(\1ISNULLOR\1<>''\2'')')                                                                                                          |
| :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| SELECT SUM(BAL_AM) FROM SCH.DERHCE03 WHERE 1=1  AND (HIRHCC04 IS NULL OR HIRHCC04 <> 'US') AND HIRHCE01 IN ('TDCD','TDOA','IRATD') AND HIREG039 IN ((660),(661),(604)) AND HIREG234 <> 345 ;   |
| SELECT SUM(BAL_AM) FROM SCH.DEFD1520 WHERE 1=1  AND HIRHCV04='Y' AND (HIRHCC04 IS NULL OR HIRHCC04 <> 'US') AND HIRHCN17='FDIC' AND (HIFD1516 IS NULL OR HIFD1516 <> 'Y') AND HIFD1527='FDIC'; |
| SELECT SUM(OUTSTANDING_AM) FROM SCH.DEFD1520 WHERE 1=1  AND HIRHCV04='Y' AND (HIRHCC04 IS NULL OR HIRHCC04 <> 'US') AND HIRHCN17='FDIC' AND HIFD1516='Y';                                      |

更新

这是一个更新的查询,还将处理NOT IN子句,并在<>运算符(以及NOT IN列表的左侧)中允许数字和字符串:

SELECT REGEXP_REPLACE(
    REGEXP_REPLACE(sql_query_part_1_tx, '(\w+)\s*<>\s*(''*\w+''*)', '(\1 IS NULL OR \1 <> \2)'), 
    '(\w+)\s*NOT IN\s*(\([^)]+\))', 
    '(\1 IS NULL OR \1 NOT IN \2)'
) FROM mytable;

Demo on DB Fiddle ;

| REGEXP_REPLACE(REGEXP_REPLACE(SQL_QUERY_PART_1_TX,'(\W+)\S*<>\S*(''*\W+''*)','(\1ISNULLOR\1<>\2)'),'(\W+)\S*NOTIN\S*(\([^)]+\))','(\1ISNULLOR\1NOTIN\2)')                                                                                    |
| :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| SELECT SUM(BAL_AM) FROM SCH.DERHCE03 WHERE 1=1  AND (HIRHCC04 IS NULL OR HIRHCC04 <> 'US') AND (HIRHCE01 IS NULL OR HIRHCE01 NOT IN ('TDCD','TDOA','IRATD')) AND HIREG039 IN ((660),(661),(604)) AND (HIREG234 IS NULL OR HIREG234 <> 345) ; |
| SELECT SUM(BAL_AM) FROM SCH.DEFD1520 WHERE 1=1  AND HIRHCV04='Y' AND (HIRHCC04 IS NULL OR HIRHCC04 <> 'US') AND HIRHCN17='FDIC' AND (HIFD1516 IS NULL OR HIFD1516 <> 'Y') AND HIFD1527='FDIC';                                               |
| SELECT SUM(OUTSTANDING_AM) FROM SCH.DEFD1520 WHERE 1=1  AND HIRHCV04='Y' AND (HIRHCC04 IS NULL OR HIRHCC04 <> 'US') AND HIRHCN17='FDIC' AND HIFD1516='Y';                                                                                    |

答案 1 :(得分:0)

这是该问题的原始版本:

  

在上面存储的查询中,挑战在于哪里存在<>(不等于)条件,它不获取具有NULL值的行(这是预期的)。但是我还需要带有NULL值的行。

这真是令人讨厌。我不能说我真的宽恕了。您的查询的书写方式不一致(在一个地方<>周围有空格,在另一个地方没有空格)。我怀疑可能比存储此类原始SQL更好地解决您的总体问题。

也就是说,我很高兴您遇到了问题。这很棘手,因为您同时拥有字符串和数字,这使事情变得棘手。你可能有约会。以下内容不适用于日期。但是,regexp_replace()可以做一些事情:

select regexp_replace(regexp_replace(q, ' ([^ <]+) ?<> ?''', ' NVL(\1, ''XXX'') <> '''), ' ([^ <)]+) ?<> ?[^'']', ' NVL(\1, -1) <> ')
from (select 'SELECT SUM(BAL_AM) FROM SCH.DERHCE03 WHERE 1=1 AND HIRHCC04<>''US'' AND HIRHCE01 IN (''TDCD'',''TDOA'',''IRATD'') AND HIREG039 IN ((660),(661),(604)) AND HIREG234 <> 345 ;' as q
     from dual) x

注意:这几乎是假设<>之前的引用是单列引用,而不是更复杂的表达式。