![](/img/trans.png)
[英]Oracle SQL - how to find a match in a column with comma separated values?
[英]How to match values from 2 lists in oracle?
我想创建一个基于过滤器的varchar列,并且该值可能具有一个或多个以逗号分隔的字符串,如下所示
col a 12 b 489 c 456,486 d 489,45,789
如果条目为489
则返回值应为b and d
如果条目为45,489
则返回值应为d
而与值顺序无关,此45,489
外键列。 在oracle中如何做到这一点?
撇开数据模型的问题,如果您必须比较两个CSV字符串的内容,而忽略元素出现的顺序,则需要将其中至少一个拆分为多个组成部分。
通过在CTE中将用户提供的值拆分为单独的值,然后使用like
机制将该值连接到实际表中,然后计算有多少个匹配项,以此建立@Tim的like
比较。
with cte (val, cnt) as (
select regexp_substr('45,489', '(.*?)(,|$)', 1, level, null, 1),
regexp_count('45,489', ',') + 1
from dual
connect by level <= regexp_count('45,489', ',') + 1
)
select t.id, t.col
from your_table t
join cte on ',' || t.col || ',' like '%,' || cte.val || ',%'
group by t.id, t.col
having count(cte.val) = max(cte.cnt)
它仅返回连接值数量与连接值计数匹配的行。
在另一个CTE中使用示例数据进行快速演示:
with your_table (id, col) as (
select 'a', '12' from dual
union all select 'b', '489' from dual
union all select 'c', '456,486' from dual
union all select 'd', '489,45,789' from dual
union all select 'e', '1,489,2,45,3' from dual
union all select 'f', '1,489,2,45,3,489' from dual
),
cte (val, cnt) as (
select regexp_substr('45,489', '(.*?)(,|$)', 1, level, null, 1),
regexp_count('45,489', ',') + 1
from dual
connect by level <= regexp_count('45,489', ',') + 1
)
select t.id, t.col
from your_table t
join cte on ',' || t.col || ',' like '%,' || cte.val || ',%'
group by t.id, t.col
having count(cte.val) = max(cte.cnt);
ID COL
-- -------------------
f 1,489,2,45,3,489,45
d 489,45,789
e 1,489,2,45,3
您可以将LIKE
与一些串联技巧一起使用:
SELECT id
FROM yourTable
WHERE ',' || id || ',' LIKE '%,489,%';
但是您当前的设计严重次优,因为您将逗号分隔的值存储在id
列中。 这极大地限制了Oracle筛选该列的能力。 相反,您应该尝试将每个id
值放在单独的行上。
注意:此答案仅适用于根据您的id
CSV搜索单个值。 如果需要同时搜索多个值,则必须复制WHERE
子句中的逻辑。
不用将值存储为字符串,而是将其存储为嵌套表,可以使用SUBMULTISET
运算符:
Oracle安装程序 :
CREATE TYPE intlist IS TABLE OF NUMBER(3,0)
/
CREATE TABLE table_name (
name CHAR(1) PRIMARY KEY,
ids intlist
) NESTED TABLE ids STORE AS table_name__ids;
INSERT INTO table_name ( name, ids )
SELECT 'a', intlist( 12 ) FROM DUAL UNION ALL
SELECT 'b', intlist( 489 ) FROM DUAL UNION ALL
SELECT 'c', intlist( 456, 486 ) FROM DUAL UNION ALL
SELECT 'd', intlist( 489, 45, 789 ) FROM DUAL;
查询1 :
SELECT name
FROM table_name
WHERE intlist( 489 ) SUBMULTISET OF ids;
输出 :
NAME
----
b
d
查询2 :
SELECT name
FROM table_name
WHERE intlist( 45, 489 ) SUBMULTISET OF ids;
输出 :
NAME
----
d
如果必须将值存储为字符串,则可以创建一个函数以将字符串转换为集合并使用相同的技术:
Oracle安装程序 :
CREATE TYPE intlist IS TABLE OF NUMBER(3,0)
/
CREATE OR REPLACE FUNCTION splitNumberList(
i_str IN VARCHAR2,
i_delim IN VARCHAR2 DEFAULT ','
) RETURN INTLIST DETERMINISTIC
AS
p_result INTLIST := INTLIST();
p_start NUMBER(5) := 1;
p_end NUMBER(5);
c_len CONSTANT NUMBER(5) := LENGTH( i_str );
c_ld CONSTANT NUMBER(5) := LENGTH( i_delim );
BEGIN
IF c_len > 0 THEN
p_end := INSTR( i_str, i_delim, p_start );
WHILE p_end > 0 LOOP
p_result.EXTEND;
p_result( p_result.COUNT ) := TO_NUMBER( SUBSTR( i_str, p_start, p_end - p_start ) );
p_start := p_end + c_ld;
p_end := INSTR( i_str, i_delim, p_start );
END LOOP;
IF p_start <= c_len + 1 THEN
p_result.EXTEND;
p_result( p_result.COUNT ) := TO_NUMBER( SUBSTR( i_str, p_start, c_len - p_start + 1 ) );
END IF;
END IF;
RETURN p_result;
END;
/
CREATE TABLE table_name (
name CHAR(1) PRIMARY KEY,
ids VARCHAR2(4000)
);
INSERT INTO table_name ( name, ids )
SELECT 'a', '12' FROM DUAL UNION ALL
SELECT 'b', '489' FROM DUAL UNION ALL
SELECT 'c', '456,486' FROM DUAL UNION ALL
SELECT 'd', '489,45,789' FROM DUAL;
查询3 :
SELECT name
FROM table_name
WHERE splitNumberList( '489' ) SUBMULTISET OF splitNumberList( ids );
输出 :
NAME
----
b
d
查询4 :
SELECT name
FROM table_name
WHERE splitNumberList( '45,489' ) SUBMULTISET OF splitNumberList( ids );
输出 :
NAME
----
d
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.