Oracle SQL:检查指定的单词是否存在于逗号分隔的字符串中
Oracle SQL : Check if specified words are present in comma separated string
我有一个 SQL 函数,它 return 给我一串逗号分隔的国家/地区代码。
我已经在另一个 table 中配置了一些特定的代码,以后我可能会删除或添加更多代码。
我想检查逗号分隔的字符串是否只是那些特定国家/地区代码的组合。也就是说,如果该字符串甚至有一个国家代码而不是指定的国家代码,它应该 return true.
假设我在静态数据中配置了两行table GB 和CH。然后我需要以下结果:
String from function
result
GB
false
CH
false
GB,CH
false
CH,GB
false
GB,FR
true
FR,ES
true
ES,CH
true
CH,GB,ES
true
我是Oracle 19c,只能使用这个版本的功能。另外,我希望它得到优化。就像我可以检查字符串中值的数量,然后计算每个特定代码。如果不匹配,则显然存在一些其他代码。但是我不想使用循环。
谁能给我一个更好的选择。
您可以将 csv 列转换为 table 并使用 EXISTS。例如
with tbl(id,str) as
(
SELECT 1,'GB,CH' FROM DUAL UNION ALL
SELECT 2,'GB,CH,FR' FROM DUAL UNION ALL
SELECT 3,'GB' FROM DUAL
),
countries (code) as
(SELECT 'GB' FROM DUAL UNION ALL
SELECT 'CH' FROM DUAL
)
select t.* ,
case when exists (
select 1
from xmltable(('"' || REPLACE(str, ',', '","') || '"')) s
where trim(s.column_value) not in (select code from countries)
)
then 'true' else 'false' end flag
from tbl t
一个方案是逐个匹配国家代码,然后根据提供的字面量作为参数判断是否存在多出的non-matched个国家
下面的 FULL JOIN 将有助于考虑上述逻辑
WITH
FUNCTION with_function(i_countries VARCHAR2) RETURN VARCHAR2 IS
o_val VARCHAR2(10);
BEGIN
SELECT CASE WHEN SUM(NVL2(t.country_code,0,1))=0 THEN 'false'
ELSE 'true'
END
INTO o_val
FROM (SELECT DISTINCT REGEXP_SUBSTR(i_countries,'[^ ,]+',1,level) AS country
FROM dual
CONNECT BY level <= REGEXP_COUNT(i_countries,',')+1) tt
FULL JOIN t
ON tt.country = t.country_code;
RETURN o_val;
END;
SELECT with_function(<comma-seperated-parameter-list>) AS result
FROM dual
假设静态 table 中的所有国家代码以及 comma-separated 字符串中的所有标记始终恰好是 two-letter 字符串,您可以这样做:
with
static_data(country_code) as (
select 'GB' from dual union all
select 'CH' from dual
)
, sample_inputs(string_from_function) as (
select 'GB' from dual union all
select 'CH' from dual union all
select 'GB,CH' from dual union all
select 'CH,GB' from dual union all
select 'GB,FR' from dual union all
select 'FR,ES' from dual union all
select 'ES,CH' from dual union all
select 'CH,GB,ES' from dual
)
select string_from_function,
case when regexp_replace(string_from_function,
',| |' || (select listagg(country_code, '|')
within group (order by null)
from static_data))
is null then 'false' else 'true' end as result
from sample_inputs
;
输出:
STRING_FROM_FUNCTION RESULT
---------------------- --------
GB false
CH false
GB,CH false
CH,GB false
GB,FR true
FR,ES true
ES,CH true
CH,GB,ES true
正则表达式将静态数据 table 中的逗号 space 和每个 two-letter 国家代码替换为 null
。如果整个事情的结果是null
,那么csv中的所有编码都在静态table中;这就是你需要测试的。
假设保证像 GBCH 这样的代币(对于像“Great Barrier Country Heat”这样的国家)不会被错误地认为是 OK,因为 GB 和 CH 分别是 OK。
这是一种解决方案
with cte as
(select distinct
s,regexp_substr(s, '[^,]+',1, level) code from strings
connect by regexp_substr(s, '[^,]+', 1, level) is not null
)
select
s string,min(case when exists
(select * from countries
where cod = code) then 'yes'
else 'no'end) all_found
from cte
group by s
order by s;
STRING | ALL_FOUND
:----- | :--------
CH | yes
CH,GB | yes
ES | no
ES,CH | no
FR | no
GB | yes
GB,CH | yes
GB,ES | no
db<>fiddle here
如果静态 table 中的值数量较少,那么最简单的方法可能不是从函数中拆分值,而是从静态 table 中生成所有值的组合使用:
SELECT SUBSTR(SYS_CONNECT_BY_PATH(value, ','), 2) AS combination
FROM static_table
CONNECT BY NOCYCLE PRIOR value != value;
其中,对于示例数据:
CREATE TABLE static_table(value) AS
SELECT 'GB' FROM DUAL UNION ALL
SELECT 'CH' FROM DUAL;
输出:
COMBINATION
GB
GB,CH
CH
CH,GB
然后你可以使用一个简单的CASE
表达式将你的字符串输出到组合:
SELECT function_value,
CASE
WHEN function_value IN (SELECT SUBSTR(SYS_CONNECT_BY_PATH(value, ','), 2)
FROM static_table
CONNECT BY NOCYCLE PRIOR value != value)
THEN 'false'
ELSE 'true'
END AS not_matched
FROM string_from_function;
其中,对于示例数据:
CREATE TABLE string_from_function(function_value) AS
SELECT 'GB' FROM DUAL UNION ALL
SELECT 'CH' FROM DUAL UNION ALL
SELECT 'GB,CH' FROM DUAL UNION ALL
SELECT 'CH,GB' FROM DUAL UNION ALL
SELECT 'GB,FR' FROM DUAL UNION ALL
SELECT 'FR,ES' FROM DUAL UNION ALL
SELECT 'ES,CH' FROM DUAL UNION ALL
SELECT 'CH,GB,ES' FROM DUAL;
输出:
FUNCTION_VALUE
NOT_MATCHED
GB
false
CH
false
GB,CH
false
CH,GB
false
GB,FR
true
FR,ES
true
ES,CH
true
CH,GB,ES
true
db<>fiddle here
我有一个 SQL 函数,它 return 给我一串逗号分隔的国家/地区代码。
我已经在另一个 table 中配置了一些特定的代码,以后我可能会删除或添加更多代码。
我想检查逗号分隔的字符串是否只是那些特定国家/地区代码的组合。也就是说,如果该字符串甚至有一个国家代码而不是指定的国家代码,它应该 return true.
假设我在静态数据中配置了两行table GB 和CH。然后我需要以下结果:
String from function | result |
---|---|
GB | false |
CH | false |
GB,CH | false |
CH,GB | false |
GB,FR | true |
FR,ES | true |
ES,CH | true |
CH,GB,ES | true |
我是Oracle 19c,只能使用这个版本的功能。另外,我希望它得到优化。就像我可以检查字符串中值的数量,然后计算每个特定代码。如果不匹配,则显然存在一些其他代码。但是我不想使用循环。
谁能给我一个更好的选择。
您可以将 csv 列转换为 table 并使用 EXISTS。例如
with tbl(id,str) as
(
SELECT 1,'GB,CH' FROM DUAL UNION ALL
SELECT 2,'GB,CH,FR' FROM DUAL UNION ALL
SELECT 3,'GB' FROM DUAL
),
countries (code) as
(SELECT 'GB' FROM DUAL UNION ALL
SELECT 'CH' FROM DUAL
)
select t.* ,
case when exists (
select 1
from xmltable(('"' || REPLACE(str, ',', '","') || '"')) s
where trim(s.column_value) not in (select code from countries)
)
then 'true' else 'false' end flag
from tbl t
一个方案是逐个匹配国家代码,然后根据提供的字面量作为参数判断是否存在多出的non-matched个国家
下面的 FULL JOIN 将有助于考虑上述逻辑
WITH
FUNCTION with_function(i_countries VARCHAR2) RETURN VARCHAR2 IS
o_val VARCHAR2(10);
BEGIN
SELECT CASE WHEN SUM(NVL2(t.country_code,0,1))=0 THEN 'false'
ELSE 'true'
END
INTO o_val
FROM (SELECT DISTINCT REGEXP_SUBSTR(i_countries,'[^ ,]+',1,level) AS country
FROM dual
CONNECT BY level <= REGEXP_COUNT(i_countries,',')+1) tt
FULL JOIN t
ON tt.country = t.country_code;
RETURN o_val;
END;
SELECT with_function(<comma-seperated-parameter-list>) AS result
FROM dual
假设静态 table 中的所有国家代码以及 comma-separated 字符串中的所有标记始终恰好是 two-letter 字符串,您可以这样做:
with
static_data(country_code) as (
select 'GB' from dual union all
select 'CH' from dual
)
, sample_inputs(string_from_function) as (
select 'GB' from dual union all
select 'CH' from dual union all
select 'GB,CH' from dual union all
select 'CH,GB' from dual union all
select 'GB,FR' from dual union all
select 'FR,ES' from dual union all
select 'ES,CH' from dual union all
select 'CH,GB,ES' from dual
)
select string_from_function,
case when regexp_replace(string_from_function,
',| |' || (select listagg(country_code, '|')
within group (order by null)
from static_data))
is null then 'false' else 'true' end as result
from sample_inputs
;
输出:
STRING_FROM_FUNCTION RESULT
---------------------- --------
GB false
CH false
GB,CH false
CH,GB false
GB,FR true
FR,ES true
ES,CH true
CH,GB,ES true
正则表达式将静态数据 table 中的逗号 space 和每个 two-letter 国家代码替换为 null
。如果整个事情的结果是null
,那么csv中的所有编码都在静态table中;这就是你需要测试的。
假设保证像 GBCH 这样的代币(对于像“Great Barrier Country Heat”这样的国家)不会被错误地认为是 OK,因为 GB 和 CH 分别是 OK。
这是一种解决方案
with cte as (select distinct s,regexp_substr(s, '[^,]+',1, level) code from strings connect by regexp_substr(s, '[^,]+', 1, level) is not null ) select s string,min(case when exists (select * from countries where cod = code) then 'yes' else 'no'end) all_found from cte group by s order by s;
STRING | ALL_FOUND :----- | :-------- CH | yes CH,GB | yes ES | no ES,CH | no FR | no GB | yes GB,CH | yes GB,ES | no
db<>fiddle here
如果静态 table 中的值数量较少,那么最简单的方法可能不是从函数中拆分值,而是从静态 table 中生成所有值的组合使用:
SELECT SUBSTR(SYS_CONNECT_BY_PATH(value, ','), 2) AS combination
FROM static_table
CONNECT BY NOCYCLE PRIOR value != value;
其中,对于示例数据:
CREATE TABLE static_table(value) AS
SELECT 'GB' FROM DUAL UNION ALL
SELECT 'CH' FROM DUAL;
输出:
COMBINATION GB GB,CH CH CH,GB
然后你可以使用一个简单的CASE
表达式将你的字符串输出到组合:
SELECT function_value,
CASE
WHEN function_value IN (SELECT SUBSTR(SYS_CONNECT_BY_PATH(value, ','), 2)
FROM static_table
CONNECT BY NOCYCLE PRIOR value != value)
THEN 'false'
ELSE 'true'
END AS not_matched
FROM string_from_function;
其中,对于示例数据:
CREATE TABLE string_from_function(function_value) AS
SELECT 'GB' FROM DUAL UNION ALL
SELECT 'CH' FROM DUAL UNION ALL
SELECT 'GB,CH' FROM DUAL UNION ALL
SELECT 'CH,GB' FROM DUAL UNION ALL
SELECT 'GB,FR' FROM DUAL UNION ALL
SELECT 'FR,ES' FROM DUAL UNION ALL
SELECT 'ES,CH' FROM DUAL UNION ALL
SELECT 'CH,GB,ES' FROM DUAL;
输出:
FUNCTION_VALUE NOT_MATCHED GB false CH false GB,CH false CH,GB false GB,FR true FR,ES true ES,CH true CH,GB,ES true
db<>fiddle here