SQL Server 2005, широкие индексы, вычисляемые столбцы и sargable запросы - PullRequest
1 голос
/ 02 июня 2010

В моей базе данных предположим, что у нас есть таблица, определенная следующим образом:

CREATE TABLE [Chemical](
    [ChemicalId] int NOT NULL IDENTITY(1,1) PRIMARY KEY,
    [Name] nvarchar(max) NOT NULL,
    [Description] nvarchar(max) NULL
)

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

Итак, мы создаем следующий вычисляемый столбец и связанный с ним индекс:

ALTER TABLE [Chemical]
ADD [Name_Indexable] AS LEFT([Name], 20)

CREATE INDEX [IX_Name] 
ON [Chemical]([Name_Indexable]) 
INCLUDE([Name])

Индекс не будет уникальным, но мы можем обеспечить уникальность с помощью триггера.

Если мы выполним следующий запрос, план выполнения приведет к индексу scan , а это не то, что нам нужно:

SELECT [ChemicalId], [Name], [Description] 
FROM [Chemical] 
WHERE [Name]='[1,1''-Bicyclohexyl]-2-carboxylic acid, 4'',5-dihydroxy-2'',3-dimethyl-5'',6-bis[(1-oxo-2-propen-1-yl)oxy]-, methyl ester'

Однако, если мы изменим запрос, чтобы сделать его «sargable», тогда план выполнения приведет к индексу seek , что и нужно:

SELECT [ChemicalId], [Name], [Description] 
FROM [Chemical] 
WHERE [Indexable_Name]='[1,1''-Bicyclohexyl]-' AND [Name]='[1,1''-Bicyclohexyl]-2-carboxylic acid, 4'',5-dihydroxy-2'',3-dimethyl-5'',6-bis[(1-oxo-2-propen-1-yl)oxy]-, methyl ester'

Является ли это хорошим решением, если мы контролируем формат всех запросов к базе данных через наш средний уровень? Есть ли способ лучше? Это главный клудж? Должны ли мы использовать полнотекстовое индексирование?

Ответы [ 6 ]

2 голосов
/ 03 июня 2010

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

CREATE TABLE dbo.[Chemical]
    ([ChemicalId] int NOT NULL IDENTITY(1,1), 
    [Name] Nvarchar(max) NOT NULL, 
    [Description] Nvarchar(max) NOT NULL,
    [Name_Index] AS (CONVERT(VARCHAR(20), LEFT([Name], 20)) + CONVERT(VARCHAR(20), [ChemicalId])) PERSISTED PRIMARY KEY);
2 голосов
/ 02 июня 2010

Ваш индекс на name_indexable, а не на name. Поскольку name_indexable генерируется из функции, включающей name, а не непосредственно в столбце name, оптимизатор не будет автоматически использовать индекс, если в предложении where содержится ссылка на name. Вы должны искать на name_indexable, чтобы использовать индекс. Поскольку у вас есть средний уровень, лучше всего предложить функцию, которая ищет по name_indexable, если заданное имя <= 200 символов, и в противном случае выполняет поиск по обоим. </p>

1 голос
/ 03 июня 2010

Я нахожу ваше решение по вопросу (последний запрос) очень хорошим, но лично я предпочитаю более точно сказать, что и как я хочу сделать в SQL. Поэтому, если вы работаете с Microsoft SQL Server или с другим SQL Server, который поддерживает CTE (общее табличное выражение), вы можете переписать ваш запрос следующим образом:

DECLARE @data nvarchar(max);

SET @data = '[1,1''-Bicyclohexyl]-2-carboxylic acid, 4'',5-dihydroxy-2'',3-dimethyl-5'',6-bis[(1-oxo-2-propen-1-yl)oxy]-, methyl ester';

WITH ReduceData ([ChemicalId], [Name], [Description]) AS (
    SELECT [ChemicalId], [Name], [Description] 
    FROM [dbo].[Chemical]
    WHERE [Name_Indexable]=LEFT(@data,20)
)
SELECT [ChemicalId], [Name], [Description] 
FROM ReduceData
WHERE [Name]=@data

(В реальной реализации вам, вероятно, не нужно определять @data. Вместо этого вы можете просто использовать параметризованный запрос.). Я предлагаю просто сказать SQL более четко, что вы хотите. Все запросы CTE могут быть очень хорошо оптимизированы.

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

ОБНОВЛЕНО : Кстати линия

ALTER TABLE [Chemical]
    ADD [Name_Indexable] AS LEFT([Name], 20)

следует изменить на

ALTER TABLE [Chemical]
    ADD [Name_Indexable] AS CAST(LEFT([Name], 20) AS varchar(20)) PERSISTED

для создания столбца [Name_Indexable] типа varchar(20) в Microsoft SQL Server 2008 и пометки его как PERSISTED для хранения вычисленных значений в таблице и их обновления при обновлении любых других столбцов, от которых зависит вычисляемый столбец

1 голос
/ 03 июня 2010

ИМХО, да, я думаю, что это плохой подход. Если вы знали, что первые 20 символов будут уникальными, то это должен быть столбец первого класса с уникальным ограничением. Если вы хотите улучшить поиск по столбцу «Имя», то использование полнотекстового поиска - верный путь. Если вы хотите убедиться, что столбец varchar (max) уникален, то создайте вычисляемый столбец, который генерирует хеш из значения, и устанавливайте уникальное ограничение для этого.

Alter Table Add NameHash Hashbytes('SHA1', [Name])

Сложение

Учитывая наше обсуждение, если ваши поиски всегда будут иметь точное совпадение, то вы можете хешировать свой параметр поиска и сравнить его с NameHash выше. Однако , выгода в том, что совпадение должно быть точным (то есть чувствительным к регистру).

Я до сих пор доволен, что FTS будет вашим лучшим выбором. Несмотря на то, что вы разбиваете текст на слова, FTS лучше всего подходит для поиска большого количества текста. Чем длиннее критерии поиска, тем точнее будет поиск.

1 голос
/ 02 июня 2010

Вы пробовали

WHERE [Name_Indexable]='1,2,3-Propanetriol'

После всего этого ваш индекс создается на

0 голосов
/ 03 июня 2010

Исправьте вашу модель данных. У вас есть список с разделителями-запятыми в столбце имени, для меня это означает, что вы бы лучше могли запросить, если у вас есть связанная таблица. Ваше имя - это список ингредиентов, а не имя.

Если это действительно настоящее имя, то у Зарегистрированного пользователя есть хороший план.

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