Почему SUBSTRING или LEFT делают запрос намного медленнее? - PullRequest
3 голосов
/ 23 июля 2010

У меня есть черный список людей, с которыми никогда не следует связываться. Когда я хочу увидеть, есть ли человек в этом списке, я делаю следующее:

-- Query 1
SELECT * 
FROM bldb.dbo.blacklist l
WHERE l.matchcode
    = dbo.fn_matchcode('12345','Sesame Street','1','Eddie','Bert')

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

Думайте о коде поиска как о сжатой форме адреса и названия, которая помогает мне не подвергаться влиянию опечаток в названиях улиц и т. Д. Он состоит из 22 символов: 13 для адреса, 9 для названия. Когда я хочу посмотреть, есть ли кто-нибудь на 1 Улица Сезам, 12345 в черном списке, я делаю следующее:

-- Query 2
SELECT * 
FROM bldb.dbo.blacklist l
WHERE LEFT(l.matchcode,13)
    = LEFT(dbo.fn_matchcode('12345','Sesame Street','1','Eddie','Bert'),13)

Это работает очень долго ...

Наоборот, это работает намного быстрее:

-- Query 3
SELECT * 
FROM bldb.dbo.blacklist l
WHERE LEFT(l.matchcode,13)
    = (SELECT LEFT(dbo.fn_matchcode('12345','Sesame Street','1','Eddie','Bert'),13))

Это означает, что правая часть условия where вычисляется для каждой строки! Но почему? UDF является детерминированным. Это LEFT(), которое не является детерминированным?

EDIT:

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

Когда я пишу запрос так:

-- Query 4
SELECT * 
FROM bldb.dbo.blacklist
WHERE matchcode LIKE LEFT(dbo.fn_matchcode('12345','Sesame Street','1','Eddie','Bert'),13) + '%'

это все еще занимает несколько минут, чтобы закончить. Обратите внимание, что fn_matchcode просто выполняет некоторые операции со строками и мгновенно возвращает.

Когда я жестко закодировал результат fn_matchcode в запросе:

-- Query 5
SELECT * 
FROM bldb.dbo.blacklist
WHERE matchcode LIKE '12345SSMSTRT1%'

это займет пару миллисекунд! Как бы вы это объяснили?

Ответы [ 5 ]

3 голосов
/ 23 июля 2010

После обновления вашего вопроса вы можете посмотреть на два плана выполнения для ваших запросов № 4 и № 5 и посмотреть, выполняет ли поиск кластеризованного индекса для одного, а поиск некластеризованного индекса для другого? Интересно, потому что он знает статистику для литерала во время компиляции, но не для вызова функции. Поскольку он не подозревает, что будет возвращено только несколько записей, он ошибается из-за осторожности, чтобы избежать целой загрузки поиска закладок.

Если это так, то поможет ли следующее?

SELECT * 
FROM bldb.dbo.blacklist WITH (FORCESEEK)
WHERE matchcode LIKE 
  LEFT(dbo.fn_matchcode('12345','Sesame Street','1','Eddie','Bert'),13) + '%'
1 голос
/ 23 июля 2010

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

1 голос
/ 23 июля 2010

Это означает, что правая часть условия where вычисляется для каждой строки!

Нет, вы думаете о коррелированных подзапросах - но это не так.

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

Хотя использование функции для столбца сделает индекс этого столбца бесполезным, сокращение количества символов в столбце VARCHAR (скажем, первые 10 из VARCHAR (150)) может быть быстрее из-за меньшего количества сравниваемых объектов. INT являются 4 байтами независимо, но это не относится к строковым типам данных ...

1 голос
/ 23 июля 2010

Я бы использовал

SELECT * 
FROM bldb.dbo.blacklist l
WHERE l.matchcode LIKE 
  LEFT(dbo.fn_matchcode('12345','Sesame Street','1','Eddie','Bert'),13) + '%'

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

0 голосов
/ 23 июля 2010

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

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