Самый быстрый способ найти строку по подстроке в SQL? - PullRequest
22 голосов
/ 11 июля 2011

У меня огромная таблица с 2 столбцами: Id и Title.Идентификатор bigint, и я могу свободно выбирать тип столбца заголовка: varchar, char, text, что угодно.Заголовок столбца содержит случайные текстовые строки, такие как «abcdefg», «q», «allyourbasebelongtous», максимум 255 символов.

Моя задача - получить строки по заданной подстроке.Подстроки также имеют произвольную длину и могут быть началом, серединой или концом строк.Самый очевидный способ сделать это:

SELECT * FROM t LIKE '%abc%'

Меня не волнует INSERT, мне нужно только сделать быстрый выбор.Что я могу сделать, чтобы выполнить поиск как можно быстрее?

Я использую MS SQL Server 2008 R2, насколько я вижу, полнотекстовый поиск будет бесполезен.

Ответы [ 7 ]

13 голосов
/ 11 июля 2011

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

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

7 голосов
/ 11 июля 2011

Если вы хотите использовать меньше места, чем ответ Рэнди, и в ваших данных имеется значительное повторение, вы можете создать структуру данных дерева N-Ary, где каждое ребро является следующим символом, и вешать каждую строку и конечную подстроку в ваших данных наЭто.

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

4 голосов
/ 11 июля 2011

Похоже, вы исключили все хорошие альтернативы.

Вы уже знаете, что ваш запрос

SELECT * FROM t WHERE TITLE LIKE '%abc%'

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

Если вы уверены, что строка находится в начале поля, вы можете сделать

SELECT * FROM t WHERE TITLE LIKE 'abc%'

, который будет использовать индекс для заголовка.

Вы уверены, что полнотекстовый поиск вам здесь не поможет?

В зависимости от ваших бизнес-требований я иногда использовал следующую логику:

  • Сначала выполните запрос «начинается с» (LIKE 'abc%'), который будет использовать индекс.
  • В зависимости от того, возвращены ли какие-либо строки (или сколько), условно перейти к «более сложному» поиску, который будет выполнять полное сканирование (LIKE '%abc%')

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

3 голосов
/ 19 ноября 2011

Вы можете добавить еще один вычисляемый столбец в таблицу: titleLength as len (title) PERSISTED. Это будет хранить длину столбца «заголовок». Создайте индекс по этому.

Кроме того, добавьте еще один вычисляемый столбец с именем: ReverseTitle as Reverse (title) PERSISTED.

Теперь, когда кто-то ищет ключевое слово, проверьте, совпадает ли длина ключевого слова с длиной заголовка. Если это так, выполните поиск "=". Если длина ключевого слова меньше длины titleLength, сделайте LIKE. Но сначала сделайте заголовок LIKE 'abc%', затем сделайте reverseTitle LIKE 'cba%'. Аналогично подходу Брэда - то есть вы выполняете следующий сложный запрос, только если требуется.

Кроме того, если правила 80-20 применяются к вашим ключевым словам / подстрокам (т. Е. Если большинство поисков выполняется по меньшинству ключевых слов), то вы также можете рассмотреть вопрос о том, чтобы выполнить какое-либо кэширование. Например: скажем, вы обнаружили, что многие пользователи ищут ключевое слово «abc», и этот поиск по ключевым словам возвращает записи с идентификаторами 20, 22, 24, 25 - вы можете сохранить это в отдельной таблице и проиндексировать. А теперь, когда кто-то ищет новое ключевое слово, сначала загляните в эту таблицу «кеша», чтобы увидеть, был ли поиск уже выполнен более ранним пользователем. Если это так, не нужно снова заглядывать в основную таблицу. Просто верните результаты из таблицы "cache".

Вы также можете комбинировать вышеперечисленное с SQL Server TextSearch. (при условии, что у вас есть веская причина не использовать его). Но вы могли бы, тем не менее, сначала использовать текстовый поиск, чтобы составить список результатов. а затем запустите SQL-запрос к вашей таблице, чтобы получить точные результаты, используя идентификаторы, возвращаемые поиском TExt в качестве параметра вместе с вашим ключевым словом.

Все это, очевидно, предполагает, что вы должны использовать SQL. Если нет, вы можете исследовать что-то вроде Apache Solr.

0 голосов
/ 26 ноября 2011

Сделайте одно, используйте первичный ключ в определенном столбце и индексируйте его в кластерной форме.

Затем выполните поиск, используя любой метод (подстановочный знак или = или любой), поиск будет оптимальным, поскольку таблица ужекластерная форма, поэтому он знает, где он может найти (потому что столбец уже в отсортированной форме)

0 голосов
/ 23 ноября 2011
  1. Используйте ASCII кодировку с кластерной индексацией столбец с символами. Кодировка влияет на производительность поиска из-за данных размер как на ОЗУ, так и на диске. Узким местом часто является ввод / вывод.
  2. Длина вашего столбца 255 символов, поэтому вы можете использовать обычный индекс для Ваше поле символов, а не полный текст, что быстрее. Не делайте выберите ненужные столбцы в вашем операторе select.
  3. Наконец, добавьте больше оперативной памяти на сервер и увеличьте размер кэша .
0 голосов
/ 22 ноября 2011

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

...