Оптимальный поиск LIKE в SQL - PullRequest
7 голосов
/ 05 января 2009

У меня есть база данных запчастей, которую я буду постоянно запрашивать для системы квотирования. База данных деталей содержит более 1 400 000 записей. Пользователи только начнут вводить номера деталей, которые, как они ожидают, система сможет найти после нескольких символов, поэтому мне нужно иметь возможность поиска по шаблону, что-то вроде:

SELECT NeededFields FROM Parts WHERE PartNumber LIKE 'ML%'

Есть ли какая-либо оптимизация, которую я могу выполнить, чтобы попытаться получить максимальную производительность от этого типа запроса? У меня есть индексированное поле PartNumber, но я не уверен, что это лучшее, что я могу получить. Я хотел бы рассмотреть альтернативные структуры индексации, встроенные в базу данных, отдельно от индексов SQL. Первичный ключ - это Guid, но он мне нужен для репликации и из-за конкретных структур данных, которые я использую.

Ответы [ 10 ]

4 голосов
/ 05 января 2009

Я предполагаю, что ваш первичный ключ (GUID), вероятно, имеет кластерный индекс. Вы можете рассмотреть возможность создания первичного ключа НЕ кластеризованным. Вместо этого вы можете кластеризовать индекс, который вы создали для PartNumber. (в таблице может быть только один кластерный индекс)

Вам также следует подумать о добавлении предиката TOP в запрос, чтобы возвращались только первые 100 (или около того) строк. Я думаю ... если пользователь сначала введет букву M, может быть пара сотен тысяч совпадений, которые будут загружаться медленно. Ограничив количество строк, вы получите лучшую производительность.

4 голосов
/ 05 января 2009

Большинство (хороших) оптимизаторов попытаются использовать индекс для предложения LIKE, где подстановочный знак не появляется первым. Если шаблон начинается с подстановочного знака, он может сделать гораздо меньше.

Если индекс является индексом B-дерева, а не хеш-индексом (системы ISAM обычно используют B-деревья), то для ограничения поиска по индексу можно использовать начальные символы предложения. Если система использует хеш-индексы, вы не сможете легко работать с частичными строками, если не создадите отдельные индексы для первого символа, затем для первых двух символов, а затем для первых трех символов ... столбца. Система ISAM может предоставить вам такую ​​гибкость; большинство систем SQL этого не делают, и вам придется создавать столбцы из 1, 2, 3, ... символов, содержащие первые 1, 2, 3 ... символы поля номера детали.

Добавлено : в комментариях спрашивается "какая СУБД?", Что справедливо. Я могу поручиться за IBM Informix Dynamic Server (IDS) и Standard Engine (SE) в любой версии, на которую вы можете положиться. Я ожидаю, что IBM DB2 (LUW или z / OS) сделает это; Я ожидаю, что Oracle сделает это. Комментарии указывают на то, что PostgreSQL 8.0 и выше делает это - с оговорками. Я не могу ответить на мои собственные знания для Sybase, Ingres, MS SQL Server, Firebird или MySQL. С каждой СУБД могут быть связаны предупреждения о том, когда можно использовать индекс.

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

1 голос
/ 05 января 2009

PostgreSQL не может оптимизировать запросы LIKE, когда подстановочный знак находится в конце.

Здесь таблица Zones индексируется по имени столбца:

CREATE INDEX index_zones_name ON Zones(name);

И индекс используется для = запросов:

EXPLAIN SELECT id FROM Zones WHERE name = 'toto.fr';
                              QUERY PLAN                                   
-------------------------------------------------------------------------------
 Index Scan using index_zones_name on zones  (cost=0.00..21.06 rows=4 width=4)
   Index Cond: (name = 'toto.fr'::text)

Запрос выполняется мгновенно, несмотря на три миллиона строк.

Но, для LIKE запросов:

EXPLAIN SELECT id FROM Zones WHERE name LIKE 'toto%';
                   QUERY PLAN                        
---------------------------------------------------------
 Seq Scan on zones  (cost=0.00..75991.43 rows=1 width=4)
   Filter: (name ~~ 'toto%'::text)

И запрос занимает много дольше.

1 голос
/ 05 января 2009

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

1 голос
/ 05 января 2009

Мне любопытно,

Можете ли вы расширить свой вопрос, чтобы включить продолжительность для следующих 4 запросов:

SELECT top 100 NeededFields FROM Parts WHERE PartNumber LIKE '%'
SELECT top 100 NeededFields FROM Parts WHERE PartNumber LIKE 'M%'
SELECT top 100 NeededFields FROM Parts WHERE PartNumber LIKE 'ML%'
SELECT top 100 NeededFields FROM Parts WHERE PartNumber LIKE 'ML0833%'

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

Кроме того, я думаю, что только что заметил что-то, полностью покрыт ли твой индекс? Если это не так, вы, вероятно, получаете сканирование таблицы, когда счетчик результатов достигает порогового значения.

1 голос
/ 05 января 2009

Экспериментируйте, разбивая вашу таблицу, используя первые 2 или 3 символа номера детали. Поэкспериментируйте с локальными индексами разделов против глобальных индексов.

1 голос
/ 05 января 2009

Этот запрос выглядит хорошо! Если поле проиндексировано и вы выполняете запрос LIKE 'term%', где подстановочный знак находится в конце, вы должны получить оптимизированные планы выполнения.

В зависимости от вашей СУБД вы можете проверить, что действительно делает оптимизатор, с помощью ключевого слова EXPLAIN.

1 голос
/ 05 января 2009

Как насчет того, чтобы разделить таблицу на поле partnumber. Вы можете разделить таблицу на разные тома.

Том А содержит а-м
Том B содержит n-z

РЕДАКТИРОВАТЬ Никогда не делал этого, кстати.

См. Это для теории http://msdn.microsoft.com/en-us/library/ms345146.aspx

0 голосов
/ 05 января 2009

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

mysql> CREATE INDEX part_of_name ON customer (name(10));

Индекс будет соответствовать только 10 первым символам

0 голосов
/ 05 января 2009

Не используйте SQL для этого.

Создайте несколько дампов данных, необходимых для вашего пользовательского интерфейса. Миллион записей не очень много - рассмотрите создание 26 файлов - по одному на каждое «начальное письмо» и сохраняйте информацию. Включите в него самые популярные записи.

Если вы хотите больше подробностей (или пользователь начинает прокручивать), вы можете сделать 26*26 больше файлов (AB, AC, AD и т. Д.) Для каждой из оставшихся записей.

Если вы настаиваете на использовании SQL для этого, профилируйте его. Попробуйте создать индекс только для ведущего символа, например

CREATE INDEX partno_idx ON parts (SUBSTRING(partnumber,0,1))

(или каков ваш местный диалект SQL), затем используйте запрос типа WHERE SUBSTRING(partnumber,0,1)='M'. Результат позволит избежать поиска по префиксу, который редко выполняется быстрее, чем целочисленные (или целые числа) индексы (в которых может использоваться хеш-таблица или список пропусков).

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...