SELECT, если строка содержит значение столбца - PullRequest
0 голосов
/ 11 апреля 2019
Manufacturer
==========================
id            name      
--------------------------
1             Company Inc.
2             Google Test.
3             3M (UNITY) USA. INC.
4             CE EE

Скажите, у меня есть строка 'Google Test. 1257 SCS RANDOM 31233DD ' и я хочу найти все строки в таблице manufacturer, где ht name является частью данной строки:

SELECT * FROM manufacturer
WHERE 'Google Test. 1257 SCS RANDOM 31233DD' ILIKE '%' || name || '%' 

Правильно возвращает:

id            name      
--------------------------
2             Google Test.

Но когда я это сделаю:

SELECT * FROM manufacturer
WHERE '3dad QTICE EEN ' ILIKE  '%' || name || '%'

возвращает:

id            name      
--------------------------
4             CE EE

Я не хочу частичных совпадений, подобных этому. name не должно совпадать в середине слова. Я пытался substring():

SELECT * from manufacturer
WHERE  SUBSTRING('Google Test. 1257 SCS RANDOM 31233DD' from name) != '';

Но я получаю:

ERROR: invalid regular expression: quantifier operand invalid

К сожалению, у меня нет точной спецификации, чтобы сработать, так как я запрашиваю это у внешнего БД. Но из того, что я видел, столбец varchar(256). Все значения в верхнем регистре и используют простые пробелы. Все начинаются с символа или цифры и заканчиваются цифрой, символом или специальным символом. Пример: 'КЛЕВЛАНДСКИЙ ДРЕЛЬ (ЗЕЛЕНЫЙ)' . В значении есть специальные символы, такие как ,.()&/

На самом деле я не ищу эффективности, если для выполнения одного запроса не требуется более 50 мс.

На данный момент существует около 10000+ записей, но со временем оно может возрасти.

Ответы [ 3 ]

2 голосов
/ 11 апреля 2019

Чтобы решить эту проблему, вам действительно нужно использовать регулярное выражение, так как добавление пробела по обе стороны строки не будет совпадать в начале или конце строки. Используя регулярные выражения, мы можем проверить и эту ситуацию. Например:

SELECT *
FROM manufacturer
WHERE 'Google Test. 1257 36700 SCS RANDOM WORD 31233DD' ~ ('(^| )' || name || '( |$)');

Выход:

id  name
2   Google Test.

Запрос:

SELECT *
FROM manufacturer
WHERE '3dad QTICE EEN ' ~ ('(^| )' || name || '( |$)');

Выход:

There are no results to be displayed.

Запрос:

SELECT *
FROM manufacturer
WHERE 'CE EE ' ~ ('(^| )' || name || '( |$)');

Выход:

id  name
4   CE EE

Демонстрация на dbfiddle

Update

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

REGEXP_REPLACE(name, '([\\.+*?[^\]$(){}=!<>|:\-#])', '\\\1', 'g')

Итак, ваш запрос должен быть:

SELECT *
FROM manufacturer
WHERE 'Google Test. 1257 36700 SCS RANDOM WORD 31233DD' ~ ('(^| )' || REGEXP_REPLACE(name, '([\\.+*?[^\]$(){}=!<>|:\-#])', '\\\1', 'g') || '( |$)');

Обновленная демоверсия

2 голосов
/ 11 апреля 2019

Все значения начинаются с символа или числа и заканчиваются либо цифрой, символом или специальным символом.... В значении есть специальные символы, такие как ,.()&/.

Я предлагаю оператор совпадения с регулярным выражением ~ .Тщательно определите границы и экранирующие специальные символы в name:

Создать один раз :

CREATE OR REPLACE FUNCTION f_regexp_escape(text)
  RETURNS text AS
$func$
SELECT regexp_replace($1, '([!$()*+.:<=>?[\\\]^{|}-])', '\\\1', 'g')
$func$  LANGUAGE sql IMMUTABLE;

Тогда:

SELECT * FROM manufacturer
WHERE  '3dad QTICE EEN ' ~ ('\m' || f_regexp_escape(name) || '( |$)')

Как?Почему?

\m .. начало слова. Работает, так как: значения начинаются с символа или цифры
( |$) .. aпробел или конец строки.Нам это нужно, поскольку значения: оканчиваются цифрой, символом или специальным символом

Содержимое manufacturer.name является ядром шаблона .Вы хотите буквальное значение всех его символов, поэтому уберите любое специальное значение, избегая правильно.Это верно для LIKE (несколько специальных символов), а также для оператора совпадения с регулярным выражением ~ (больше специальных символов).Часто упускается из виду и довольно ловушка.Это дало вам (и хитрое определение границ).Прочитайте это!

А затем используйте функцию f_regexp_escape(), как показано.A name like:

3M (UNITY) USA. INC.

становится:

3M \(UNITY\) USA\. INC\.

Может быть удобно хранить легко экранированные шаблоны в таблице manufacturer, возможно, в качестве дополнительного столбца.И, возможно, с дополнительным заполнением, как это:

\m3M \(UNITY\) USA\. INC\.( |$)

Или создайте шаблон на лету, как показано.

Таким образом name может быть одним словом или целой фразой, и конецс любыми персонажами.Но начало и конец никогда не совпадают в середине «слова» на другой стороне.

В Postgres есть арсенал других инструментов сопоставления с образцом:

Если ваша таблица большая, рассмотрим полнотекстовый поиск инфраструктура с оптимизированными индексами и поиск по фразе возможность:

2 голосов
/ 11 апреля 2019

Один метод с LIKE заключается в добавлении пробелов в начало и конец:

SELECT *
FROM db
WHERE ' ' || '3dad QTICE EEN ' || ' ' ILIKE  '% ' || manufacturer || ' %'

Если вам нужно более сложное сопоставление, вам может потребоваться использовать регулярные выражения с границами слов.

...