Запрос записей, которые начинаются / заканчиваются строкой, в границах слова, используя REGEXP (MySql) - PullRequest
0 голосов
/ 21 января 2019

В приведенном ниже запросе я хотел бы найти записи, начинающиеся с engineer . например Я хотел бы отозвать записи с описанием инженер ing

SELECT * FROM app.desc_test t
WHERE lower(t.desc) REGEXP '[[:<:]]engineer[[:>:]]';

Границы слова правильно обрабатывают все специальные символы (т. Е. Запятые, пробелы, специальные символы и т. Д. До и после), но я не уверен, как написать регулярное выражение, чтобы оно начиналось с инженер.

Кроме того, как бы я сказал, что начинается с ИЛИ и заканчивается инженером.

CREATE TABLE desc_test (
  id int(11) NOT NULL AUTO_INCREMENT,
  desc varchar(1000) COLLATE utf8mb4_unicode_ci NOT NULL,
  PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

Редактировать
Значение будет неизвестным / динамическим, поэтому жесткое кодирование любого «ing» выражения не является решением.

Ответы [ 3 ]

0 голосов
/ 21 января 2019

Если вы хотите сопоставить только начало слова, вы можете просто удалить [[:>:]] из регулярного выражения.

SELECT * FROM app.desc_test t
WHERE lower(t.desc) REGEXP '[[:<:]]engineer';
0 голосов
/ 22 января 2019

Для "desc начинается с":

"Начинается с:

REGEXP:  '^engineer...'
LIKE:    'engineer%...'

Складывание кейса:

If the collation of the column is `..._ci`, then do _not_ waste time with `LOWER()`.

Итак, этооптимально для нахождения desc, который начинается с"инженер" или "инженер" или "инженер", и т. д .:

   WHERE t.desc LIKE 'engineer%'

Если вы действительно имели в виду "где содержится описание 'инженер' или ... ", затем

   WHERE t.desc REGEXP '[[:<:]]engineer'

Но лучшим способом было бы использовать FULLTEXT(desc) и использовать это; оно позволяет слову находиться где угодно в desc и desc может быть TEXT.

   WHERE MATCH(desc) AGAINST('+engineer*' IN BOOLEAN MODE)

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

  • LOWER(desc) ... - плохо, независимо от остальной части пункта
  • LIKE 'engineer%' - отлично, если у вас есть INDEX(desc)
  • LIKE 'engineer%' - плохо без индекса или с префиксом:INDEX(desc(100))
  • MATCH... - отлично из-за индекса FULLTEXT.
  • REGEXP ... - плохо, проверяет каждую запись

Для "есть слово, которое начинается или заканчивается на":

Вам нужно перечислить положительные и отрицательные тестовые случаи:

engineering blah
The engineer.
MechanicalEngineering  -- neither starts nor ends at word boundary??
engineer

Если все они верны, то это единственный жизнеспособный ответ:

    WHERE t.desc LIKE '%engineer%'

эквивалент REGEXP 'engineer' медленнее (но имеет тот же эффект).

В других ситуациях я хотел бы взглянуть на что-то близкое к

   WHERE t.desc REGEXP '[[:<:]]engineer|engineer[[:>:]]'

, которое ищет "слово", которое начинается илизаканчивается «инженером».Обратите внимание, что сюда не входит «Механический инжиниринг».

0 голосов
/ 21 января 2019

Примечание. Полнотекстовый поиск, на который ссылается Билл Карвин является предпочтительным

потому что использование REGEXP в тысячи раз медленнее, чем индексированное решение

Но ...

Чтобы использовать текущую реализацию REGEXP, ваш MySQL должен выглядеть следующим образом:

SELECT * FROM app.desc_test t WHERE lower(t.desc) 
REGEXP '[[:<:]]engineer[a-z]*[[:>:]]';

Regex выглядит так:

[[:<:]]engineer[a-z]*[[:>:]]

Значение:

[[:<:]] - начало границы слова
engineer - строка, заданная поиском (динамическая)
[a-z] - любой символ между a-z от нуля до любого количества раз.
* - указанная выше «группа» находится между нулем и любым количеством раз.
[[:>:]] - Конец границы слова

Вышесказанное должно делать то, что вам нужно. Вы также можете настроить его, например, чтобы включить цифры ((a-z0-9)) или что угодно.


Изменения к этому ответу:

Один:

Пересмотрено, улучшено: используйте [[:alpha:]] так:

[[:<:]]engineer[[:alpha:]]*[[:>:]]

Два:

Как правильно указал Barmar , на самом деле нет необходимости в избыточном REGEXP. Ваши границы слов или их отсутствие делают вашу работу за вас.

Поэтому, чтобы выбрать любое слово, начинающееся с engineer или заканчивающееся engineer, вы просто делаете оператор REGEXP OR :

SELECT * FROM app.desc_test t WHERE lower(t.desc) 
REGEXP '([[:<:]]engineer)|(engineer)[[:>:]])' 

Это означает:

Вернуть true, если:

  • Термин инженер встречается в начале слова, независимо от того, что следует за ним.
  • ИЛИ термин инженер стоит в конце слова, независимо от того, что стоит перед ним.

Это должно соответствовать именно тому, что вы ищете. Это было проверено на MySQL 5.7.


Источники:

Примеры случаев:

Engineer

Match

Engineering

Match

Engineers

Match

Engineer!

Match


Кроме того, как бы я сказал, что начинается с ИЛИ и заканчивается инженером.

Просто переверните REGEXP и установите его как ИЛИ оператор:

SELECT * FROM app.desc_test t WHERE lower(t.desc) 
REGEXP '[[:<:]](engineer[[:alpha:]]*)|([[:alpha:]]*engineer)[[:>:]]';

, который сообщает REGEXP:
"искать инженера в начале слова, за которым следуют любые значения z или искать любые значения аз, после чего инженер в конце слово ".

...