繁体   English   中英

如何匹配Oracle中2个列表中的值?

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM