我想创建一个基于过滤器的varchar列,并且该值可以具有一个或多个逗号分隔的字符串,例如
col a 12 b 489 c 456,486 d 489,45,789
如果条目为489
,则返回值应为b and d
如果条目为45,489
,则返回值应为d
无论值顺序如何,此列都是外键列。
如何在oracle中做到这一点?
答案 0 :(得分:2)
撇开数据模型的问题,如果您必须比较两个CSV字符串的内容,而忽略元素出现的顺序,则需要至少将其中的一个拆分为各个组成部分。
此方法基于@Tim的like
比较,方法是将用户提供的值拆分为CTE中的单独值,使用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
答案 1 :(得分:1)
您可以使用LIKE
以及一些串联技巧:
SELECT id
FROM yourTable
WHERE ',' || id || ',' LIKE '%,489,%';
但是您当前的设计严重次优,因为您将逗号分隔的值存储在id
列中。这极大地限制了Oracle筛选该列的能力。相反,您应该尝试将每个id
值放到单独的行中。
注意:此答案仅适用于针对id
CSV搜索单个值。如果需要同时搜索多个值,则必须复制WHERE
子句中的逻辑。
答案 2 :(得分:1)
将值存储为嵌套表,而不是将值存储为字符串,您可以使用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