Как сопоставить значения из 2 списков в Oracle? - PullRequest
0 голосов
/ 12 декабря 2018

Я хочу создать столбец varchar на основе фильтра, и значение может иметь одну или несколько строк, разделенных запятыми, например:

  col
a 12
b 489
c 456,486
d 489,45,789

, если запись была 489 возвращаемым значениемдолжно быть b and d, если запись была 45,489, возвращаемое значение должно быть d независимо от порядка значений, этот столбец является столбцом внешнего ключа.как это сделать в оракуле?

Ответы [ 3 ]

0 голосов
/ 12 декабря 2018

Оставляя в стороне проблемы с моделью данных, если вам нужно сравнить содержимое двух строк CSV, игнорируя при этом порядок отображения элементов, вам нужно разбить хотя бы одну из них на составные части.

Это основано на like сравнении @Tim, разделив предоставленное пользователем значение на отдельные значения в 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       
0 голосов
/ 12 декабря 2018

Вместо сохранения значений в виде строк, сохраните их как вложенную таблицу, и вы можете использовать оператор SUBMULTISET:

Oracle Setup :

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
0 голосов
/ 12 декабря 2018

Вы можете использовать LIKE вместе с некоторыми приемами конкатенации:

SELECT id
FROM yourTable
WHERE ',' || id || ',' LIKE '%,489,%';

Но ваш текущий дизайн не совсем оптимален, поскольку вы храните значения, разделенные запятыми, в столбце id.Это сильно ограничивает способность Oracle фильтровать этот столбец.Вместо этого вы должны попытаться вывести каждое значение id в отдельную строку.

Примечание. Этот ответ работает только для поиска одного значения по вашему id CSV.Если вам нужно искать более одного значения одновременно, вам придется продублировать логику в предложении WHERE.

...