Низкая производительность при запросе LINQ к серверу SQL - PullRequest
1 голос
/ 01 апреля 2011

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

GN_Name 1 - 0:N GN_AlternateName

(PK) GN_Name.GeoNameId == (FK) GN_AlternateName.GeoNameId

Я хочу сначала найти имя в GN_AlternateName.AlternateNameи если этого не существует, используйте соответствующее GN_Name.Name.

Я написал следующий запрос LINQ:

return (from name in db.GN_Name
        where name.CountryCode == "se"
        join alt in db.GN_AlternateName 
        on name.GeoNameId equals alt.GeoNameId into outer
        from alt in outer.DefaultIfEmpty()
        where ((alt.IsoLanguage == "sv" && 
                alt.AlternateName.StartsWith(query)) || 
                name.Name.StartsWith(query))
        select new GeoNameModel { 
            Language = alt.IsoLanguage, 
            Name = (alt == null ? name.Name : alt.AlternateName),
            FeatureClass = name.FeatureClass, 
            FeatureCode = name.FeatureCode, 
            GeoNameId = name.GeoNameId, 
            UniqueName = name.UniqueName,
            UniqueCount = name.UniqueCount}).Take(HB.AutoCompleteCount);

Это переводит в следующий SQL:

exec sp_executesql N'SELECT 
[Limit1].[GeoNameId] AS [GeoNameId], 
[Limit1].[IsoLanguage] AS [IsoLanguage], 
[Limit1].[C1] AS [C1], 
[Limit1].[FeatureClass] AS [FeatureClass], 
[Limit1].[FeatureCode] AS [FeatureCode], 
[Limit1].[UniqueName] AS [UniqueName], 
[Limit1].[UniqueCount] AS [UniqueCount]
FROM ( SELECT TOP (5) 
    [Extent1].[GeoNameId] AS [GeoNameId], 
    [Extent1].[FeatureClass] AS [FeatureClass], 
    [Extent1].[FeatureCode] AS [FeatureCode], 
    [Extent1].[UniqueName] AS [UniqueName], 
    [Extent1].[UniqueCount] AS [UniqueCount], 
    CASE WHEN ([Extent2].[AlternateNameId] IS NULL) THEN [Extent1].[Name] ELSE [Extent2].[AlternateName] END AS [C1], 
    [Extent2].[IsoLanguage] AS [IsoLanguage]
    FROM  [dbo].[GN_Name] AS [Extent1]
    LEFT OUTER JOIN [dbo].[GN_AlternateName] AS [Extent2] ON [Extent1].[GeoNameId] = [Extent2].[GeoNameId]
    WHERE (''se'' = [Extent1].[CountryCode]) AND (((''sv'' = [Extent2].[IsoLanguage]) AND ([Extent2].[AlternateName] LIKE @p__linq__0 ESCAPE N''~'')) OR ([Extent1].[Name] LIKE @p__linq__1 ESCAPE N''~''))
)  AS [Limit1]',N'@p__linq__0 nvarchar(4000),@p__linq__1 nvarchar(4000)',@p__linq__0=N'ja%',@p__linq__1=N'ja%'

Я не могу понять, что с ним не так, но на это уходит около 5 секунд.

Должен ли я добавить индекс?Может быть, настроить индексированное представление?Мои знания по SQL-серверу ограничены, и я хотел бы вернуться к некоторому реальному кодированию;)

Любые предложения горячо приветствуются!

ОБНОВЛЕНИЕ Я использую SQL Server 2008Следуя инструкциям тайлонра, я получил следующие результаты.Client statistics Execution plan Execution plan details

Есть 3 "части", которые составляют 100% от общего количества.Однако я не знаю, как использовать эту статистику.

ОБНОВЛЕНИЕ 2

План выполнения SSMS рекомендовал следующий индекс:

CREATE NONCLUSTERED INDEX IX_GN_Name_CountryCode
ON [dbo].[GN_Name] ([CountryCode])
INCLUDE ([GeoNameId],[Name],[FeatureClass],[FeatureCode],[UniqueName],[UniqueCount])

Я добавил его, и теперь запрос работает намного лучше!

ОБНОВЛЕНИЕ 3 Тайлон предлагает использовать только одно предложение LIKE.Я не уверен, как это сделать.Кто-нибудь готов принять вызов?

Ответы [ 5 ]

2 голосов
/ 01 апреля 2011

Во-первых, я буду осторожен с тем, чтобы называть SQL не «реальным кодированием», так как похоже, что улучшение может помочь вам;) (Я парень на C #, а не эксперт по SQL, просто скажу ...)

Войдите в вашу SSMS и возьмите сгенерированный запрос.

Скопируйте его в новое окно запроса.

Теперь сделайте 2 вещи перед тем, как запустить его.1. Перейдите в меню «Запрос» и нажмите «Включить статистику клиента». 2. Перейдите в меню «Запрос» и нажмите «Включить план фактического выполнения»

. Теперь выполните запрос.

Когда запросготово, проверьте статистику клиента для элемента, помеченного как «Время ожидания ответов сервера». Это количество времени (в мс), которое сервер выполняет для этого запроса.

«Общее время выполнения» - этоколичество времени, которое потребовалось клиенту и серверу для передачи данных.

Это даст вам представление о том, каково время на сервере.Например, если это 10 мс, а выполнение из вашего кода занимает 5 с, Sql может не быть проблемой.

Далее откройте вкладку плана выполнения.Это покажет вам, как SQL сгенерировал эти данные.Например, если сканирование таблицы заняло 100% времени (в отличие от сканирования индекса), возможно, вы захотите добавить некоторые индексы.

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

Я думаю, что наличие двух отдельных «похожих» операторов, вероятно, не очень помогает.Подобные операторы не так эффективны, как равенство, например,

WHERE name = 'taylonr'

быстрее, чем

WHERE name like 'taylo%'
1 голос
/ 01 апреля 2011

Вы можете выполнить запрос в Помощник по настройке ядра СУБД .

После анализа запроса будут предложены индексы.

0 голосов
/ 01 апреля 2011

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

FROM [dbo].[GN_Name] AS [Extent1] 
LEFT OUTER JOIN [dbo].[GN_AlternateName] AS [Extent2] 
ON [Extent1].[GeoNameId] = [Extent2].[GeoNameId]) AS [Limit1] 
AND ( 'sv' = [Extent2].[IsoLanguage] ) 

Полагаю, это будет означать, что здесь будет linq.

join alt in db.GN_AlternateName 
on name.GeoNameId equals alt.GeoNameId && alt.IsoLanguage == 'sv'

Также я бы рассмотрел индексов в следующих полях. Но Советник по настройке должен сказать вам так или иначе.

GN_Name.GeoNameId
GN_AlternateName.GeoNameId
GN_Name.CountryCode
GN_Name.Name
GN_AlternateName.AlternateName
0 голосов
/ 01 апреля 2011

Мне кажется, проблема в том, что EF превращает ваши параметры @ p_ linq _1 nvarchar (4000) в nvarchar, и я предполагаю, что в базе данных они хранятся как varchar, заставляя sql-сервер их приводить.

Я столкнулся с той же проблемой.Попробуйте запустить sql в анализаторе запросов, измените типы параметров на varchar и посмотрите, работает ли он быстрее.

0 голосов
/ 01 апреля 2011

Можете ли вы попробовать разбить это на 3 или около того отдельных запросов? Тогда посмотрите, не является ли какой-либо из них ненормально длинным. Объединение всей вашей работы в одну функцию return () очень затрудняет диагностику.

...