Можно ли запросить столбец через запятую для определенного значения? - PullRequest
2 голосов
/ 27 августа 2011

У меня есть (и не принадлежит, поэтому я не могу изменить) таблица с макетом, похожим на этот.

ID | CATEGORIES
---------------
1  | c1
2  | c2,c3
3  | c3,c2
4  | c3
5  | c4,c8,c5,c100

Мне нужно вернуть строки, которые содержат определенный идентификатор категории.Я начал с написания запросов с помощью операторов LIKE, потому что значения могут быть где угодно в строке

SELECT id FROM table WHERE categories LIKE '%c2%'; Возвращает строки 2 и 3

SELECT id FROM table WHERE categories LIKE '%c3%' and categories LIKE '%c2%'; Снова получит строки 2 и3, но не строка 4

SELECT id FROM table WHERE categories LIKE '%c3%' or categories LIKE '%c2%'; Снова получите строки 2, 3 и 4

Я не люблю все операторы LIKE.Я нашел FIND_IN_SET() в документации Oracle, но, похоже, он не работает в 10g.Я получаю следующую ошибку:

ORA-00904: "FIND_IN_SET": invalid identifier
00904. 00000 -  "%s: invalid identifier"

при выполнении этого запроса: SELECT id FROM table WHERE FIND_IN_SET('c2', categories); (пример из документов) или этот запрос: SELECT id FROM table WHERE FIND_IN_SET('c2', categories) <> 0; (пример из Google)

Я ожидаючтобы вернуть строки 2 и 3.

Есть ли лучший способ написать эти запросы вместо использования тонны операторов LIKE?

Ответы [ 5 ]

10 голосов
/ 27 августа 2011

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

select 
  * 
from
  YourTable 
where 
  ',' || CommaSeparatedValueColumn || ',' LIKE '%,SearchValue,%'

Но этот запрос будет медленным, как и все запросы, использующие LIKE, особеннос лидирующим подстановочным знаком.

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

Лучшим решением было бы добавить дочернюю таблицу для этих категорий.Вернее даже отдельная таблица для категорий и таблица, которая связывает их с YourTable.

2 голосов
/ 05 июня 2014

Вы можете написать табличную функцию PIPELINED, которая возвращает таблицу из 1 столбца. Каждая строка является значением из строки, разделенной запятой. Используйте что-то вроде этого, чтобы pop строку из списка и put как строку в таблице:

PIPE ROW(ltrim(rtrim(substr(l_list, 1, l_idx - 1),' '),' '));

Использование:

SELECT * FROM MyTable 
WHERE 'c2' IN TABLE(Util_Pkg.split_string(categories));

Подробнее здесь: Документы Oracle

1 голос
/ 03 апреля 2015

Пока список с разделителями-запятыми составляет 512 символов или менее, в этом случае вы также можете использовать регулярное выражение (функции регулярного выражения Oracle, например, REGEXP_LIKE(), ограничены 512 символами):

SELECT id, categories
  FROM mytable
 WHERE REGEXP_LIKE('c2', '^(' || REPLACE(categories, ',', '|') || ')$', 'i');

Выше я заменяю запятые оператором чередования регулярных выражений |.Если ваш список значений с разделителями уже ограничен |, тем лучше.

1 голос
/ 25 октября 2014

Ради будущих поисковиков не забывайте способ регулярного выражения:

with tbl as (
select 1 ID, 'c1' CATEGORIES from dual
union
select 2 ID, 'c2,c3' CATEGORIES from dual
union
select 3 ID, 'c3,c2' CATEGORIES from dual
union
select 4 ID, 'c3' CATEGORIES from dual
union
select 5 ID, 'c4,c8,c5,c100' CATEGORIES from dual
)
select * 
from tbl
where regexp_like(CATEGORIES, '(^|\W)c3(\W|$)');

        ID CATEGORIES
---------- -------------
         2 c2,c3
         3 c3,c2
         4 c3

Это соответствует границе слова, поэтому даже если за запятой стоит пробел, она все равно будет работать.Если вы хотите быть более строгим и соответствовать только там, где запятая разделяет значения, замените '\ W' запятой.В любом случае, читайте регулярное выражение следующим образом: соответствует группе либо начала строки, либо границы слова, за которой следует целевое значение поиска, за которым следует группа либо границы слова, либо конца строки.

1 голос
/ 27 августа 2011

Да и нет ...

"Да":

Нормализация данных (настоятельно рекомендуется) - т.е. разбить столбец категории так, чтобы каждая категория была в отдельном ... тогда вы можете просто запросить ее в обычном режиме ...

"Нет":
Пока вы сохраняете эту «псевдоструктуру», будет несколько проблем (производительность и другие), и вам придется делать что-то похожее на:

SELECT * FROM MyTable WHERE categories LIKE 'c2,%' OR categories = 'c2' OR categories LIKE '%,c2,%' OR categories LIKE '%,c2'

ЕСЛИ вам абсолютно необходимо, чтобы вы могли определить функцию с именем FIND_IN_SET, например:

CREATE OR REPLACE Function FIND_IN_SET
   ( vSET IN varchar2, vToFind IN VARCHAR2 )
   RETURN number
IS
    rRESULT number;
BEGIN

rRESULT := -1;
SELECT COUNT(*) INTO rRESULT FROM DUAL WHERE vSET LIKE ( vToFine || ',%' ) OR vSET = vToFind OR vSET LIKE ('%,' || vToFind || ',%') OR vSET LIKE ('%,' || vToFind);

RETURN rRESULT;

END;

Затем вы можете использовать эту функцию как:

SELECT * FROM MyTable WHERE FIND_IN_SET (categories, 'c2' ) > 0;
...