2014-10-27 3 views
1

Я хотел бы написать простой оператор Oracle SQL 10g для сравнения элементов (строк) в произвольно определенном запятой, поле, содержащее список разделенных запятыми строк. Элементы в определенном списке могут отображаться в поле в любом порядке, и должны быть найдены точные соответствия (а не подстроки).Сравните элементы в списке, разделенном запятыми, на другой набор элементов в разделенном запятой списке, используя ограниченный Oracle SQL

У меня есть рабочее решение, использующее ряд операторов regexp_like(), введенных вручную, но мне нужно передать это клиенту, который будет поддерживать это перемещение вперед, и хотел бы иметь возможность просто обновлять разделенные запятыми строка напрямую.

У меня также есть некоторые ограничения на основе GUI на основе того, что я могу сделать с Oracle SQL, чтобы выполнить это. В частности, я не могу использовать PL/SQL, и это должно быть записано в одном операторе select (без временных таблиц или чего-нибудь интересного/полезного.) Я нашел несколько решений для того, что я пытаюсь выполнить, но почти все зависело от возможности писать пользовательские функции.

Итак, теперь, когда предыстория/ограничения в стороне, давайте перейдем к ничтожному gritty.

Пример произвольный список (клиент дополнительно): Itema, ItemB, ItemC

Таблица ПОЗИЦИИ:
Колонка товары (VARCHAR2 некоторой произвольной, но достаточной длины)

  • Itema, ItemB, ItemC
  • ItemC, ItemB, Itema
  • ItemX, ItemC, ItemY, Itema, ItemB, ItemB
  • ItemX, ItemY, ItemC

Мне нужен один оператор select, который будет в основном выбирать все строки, в которых элементы содержат «ItemA» и «ItemB» и «ItemC», но без необходимости прерывать эту строку вручную. В этом случае он будет соответствовать первой, второй и третьей строкам, но не четвертой строке.

(EDIT) Я понимаю, что эта структура таблицы плохо разработана. В настоящее время я не знаю, можем ли мы вернуться к клиенту, чтобы исправить это, поскольку данные могут быть использованы, как и в других местах, что делает редизайн дорогостоящим и трудоемким. Я уверен, что многие из вас привыкли к этому сценарию. Первоначальная система была разработана плохо, теперь меня привели, чтобы посоветоваться с трудностями, связанными с плохим дизайном. Предположим, что нормализовать эту таблицу невозможно, и ее необходимо использовать как есть.

Вполне возможно, что то, что я хотел бы сделать, просто невозможно, учитывая ограничения интерфейса, которые мне нужны для использования, но мои знания SQL недостаточно велики, чтобы определить это.

Спасибо всем, что нашли время, чтобы прочитать этот вопрос. Пожалуйста, дайте мне знать, если что-то вводит в заблуждение или нуждается в расширении или разъяснении.

+4

Вы не должны хранить значения, разделенные запятой, в одном столбце в первую очередь. Можете ли вы исправить вашу модель данных? –

+0

«НОРМАЛИЗАЦИЯ», основной принцип «Реляционная модель данных». Сначала прочтите об этом. Ваш дизайн испорчен. Способ отображения данных НЕ является тем, как вы храните его в базе данных. –

+0

Извините, я должен был упомянуть, что это плохо спроектировано. Я только недавно был привлечен, чтобы помочь этому клиенту. Я добавил изменение, явно признающее, что это почти полностью проблема из-за плохого дизайна. В настоящее время я не знаю, можно ли нормализовать эту таблицу из-за времени/стоимости. – Strahn

ответ

2

Хотя я абсолютно согласен с комментаторами в том, что модель данных ошибочна, иногда вам приходится работать с тем, что вам дано. Если действительно невозможно изменить модель данных, тогда вы можете это сделать, но это не совсем красиво и зависит от ваших «ограничений», не исключая использование общих табличных выражений. Я видел, как инструменты с ними справляются. .

with items_cte as (
    select id, regexp_substr(items, '[^,]+', 1, level) as item, level as pos 
    from items 
    connect by level <= regexp_count(items, '[^,]+') 
    and prior id = id 
    and prior sys_guid() is not null 
), 
list_cte as (
    select regexp_substr(:list, '[^,]+', 1, level) as item, 
    count(*) over() as list_length 
    from dual 
    connect by level <= regexp_count(:list, '[^,]+') 
) 
select i.id, listagg(i.item, ',') within group (order by i.pos) as items 
from items_cte i 
join list_cte l 
on l.item = i.item 
group by i.id 
having count(distinct i.item) = max(l.list_length) 
order by i.id; 

     ID ITEMS 
---------- -------------------------------------------- 
     1 ItemA,ItemB,ItemC 
     2 ItemC,ItemB,ItemA 
     3 ItemC,ItemA,ItemB,ItemB 

SQL Fiddle.

Это использование двух общих табличных выражений (CTE, также называемых факторингом подзапросов). Каждый из них разбивает список, разделенный запятыми, на псевдоуровне. Пробой list довольно прост и использует функции регулярных выражений, с которыми вы, похоже, знакомы. items один немного сложнее, потому что предложение connect by обычно не очень хорошо работает с несколькими строками. Это использует трюк, который использует предложение prior с любой недетерминированной функцией - sys_guid() здесь, но вы можете использовать другие - для остановки его циклирования и смешивания значений из разных исходных строк. Я также предположил, что у вас есть уникальный столбец идентификатора таблицы.

Сценарий показывает два отдельных результата разделения, а также конечный результат их объединения.

Идентификатор count(distinct i.item)count(distinct i.item) Наконец, listagg используется для сопоставления значений split в исходных заказах, и только count(distinct i.item) показывает результаты, в которых были сопоставлены все значения из list. distinct необходим для соответствия вашей третьей строке, так как itemB появляется дважды.

+0

Вы были правы, опасаясь ограничений. Наш GUI не разрешил бы оператор with, но я просто отбросил подзапросы туда, где они будут ссылаться, и что, похоже, работает нормально. Пока что начальное тестирование выглядит хорошо! Большое спасибо, Алекс. – Strahn

+0

@Strahn - да, я должен был сказать, что вы можете использовать встроенные представления вместо CTE, если это необходимо - я просто нахожу CTEs более чистым, в общем. Рад, что он работает для вас. –

Смежные вопросы