2015-08-24 5 views
2

У меня большие проблемы с оптимизацией определенного SQL-запроса. Он содержит несколько внутренних соединений и добавление индексов в каждую таблицу не улучшает производительность вообще.SQL Server - Создать индекс для запроса по нескольким таблицам?

Мой запрос:

declare @categoryid int = 2; 

SELECT [Scanresultebay].Id 
     , [Scanresultebay].Productebayid 
     , [Scanresultebay].Price 
     , [Scanresultebay].Stockamount 
     , [Scanresultebay].Timestamp 
     , [Category].Categoryname 
     , (
      SELECT TOP 1 [Scanresultebay].price 
      FROM [Scanresultebay] 
        INNER JOIN [Productebay] ON [Productebay].id = .[Scanresultebay].productebayid 
        INNER JOIN [EbaySeller] on [EbaySeller].id = [ProductEbay].ebaysellerid 
      WHERE [dbo].[EbaySeller].id = 28 
        and [ProductEbay].categoryid = @categoryid 
      ORDER BY [Scanresultebay].Id DESC 
     ) AS 'OurPrice' 
FROM [Scanresultebay] 
     INNER JOIN [Productebay] ON [Productebay].Id = [Scanresultebay].productebayid 
     INNER JOIN [Category] ON [Category].Id = [Productebay].categoryid 
WHERE [Scanresultebay].productebayid in ( 
      SELECT [Scanresultebay].productebayid 
      FROM [Scanresultebay] 
        INNER JOIN [ProductEbay] ON [ProductEbay].id = [ScanResultEbay].ProductEbayId 
        INNER JOIN [Category] ON [Category].Id = ProductEbay.CategoryID 
      WHERE [ProductEbay].categoryid = @categoryid and [ProductEbay].expired is null 
      GROUP BY [Scanresultebay].ProductEbayId 
     ) 
     and [Scanresultebay].Id in (
      SELECT max(Id) 
      FROM [Scanresultebay] 
      WHERE productebayid = [Scanresultebay].ProductEbayId 
        and [Scanresultebay].Price <> 0 
      GROUP BY [Scanresultebay].[ProductEbayId] 
     ); 

Мой индекс (как SQL Server предложил мне):

CREATE NONCLUSTERED INDEX [ind_GetPrice] 
ON [dbo].[ScanResultEbay] ([Id],[ProductEbayId]) include ([Stockamount], [Timestamp], [Price]) 

мне нужен этот запрос для отображения Infos на приборной панели на моем сайте. Поэтому я должен пройти через каждую категорию (всего 100) с этим запросом. Это длится до 30-40 секунд, что слишком много.

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

Так мои вопросы:

  1. Могу ли я сделать определенный индекс только для этого запроса, поэтому он получает намного быстрее (в настоящее время она занимает 0,5sec).
  2. Является ли мой запрос слишком длинным (или недействительным) из-за этих подзапросов?
  3. Что я могу сделать еще, чтобы улучшить свою производительность?
+1

Первое, что нужно сделать, это посмотреть план выполнения. Один совет, который я могу вам дать, это попытаться изменить операторы 'in' на' exist'. –

+0

просто хочу исключить параметр sniffing: , пожалуйста, добавьте опцию (recomple) в конце запроса и сообщите нам –

+0

для меня, похоже, CROINALITY ISSUE .. вы можете материализовать свое условие 2 IN в TEMP TABLE, если данные не огромный? –

ответ

0

Вы должны создать индексы для каждого внешнего ключа.

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

редактировать:

Выработать:

  1. Индексы являются глобальными. Не конкретный запрос.
  2. 100 категорий чрезвычайно малы. У вас много подключений (все необходимо)
  3. Создайте индексы, чтобы повысить производительность!
2

Это не полный ответ, но ответ на вопрос в комментарии о существует синтаксис.
вместо:

WHERE [Scanresultebay].productebayid in ( 
     SELECT [Scanresultebay].productebayid 
     FROM [Scanresultebay] 
       INNER JOIN [ProductEbay] ON [ProductEbay].id = [ScanResultEbay].ProductEbayId 
       INNER JOIN [Category] ON [Category].Id = ProductEbay.CategoryID 
     WHERE [ProductEbay].categoryid = @categoryid and [ProductEbay].expired is null 
     GROUP BY [Scanresultebay].ProductEbayId 
    ) 

использовать это:

WHERE EXISTS ( 
     SELECT 1 
     FROM [Scanresultebay] t1 
       INNER JOIN [ProductEbay] ON [ProductEbay].id = t1.ProductEbayId 
       INNER JOIN [Category] ON [Category].Id = ProductEbay.CategoryID 
     WHERE [ProductEbay].categoryid = @categoryid 
     AND [ProductEbay].expired is null 
     AND [Scanresultebay].productebayid = t1.productebayid 
    ) 

Обратите внимание, что я переехал [Scanresultebay].productebayid из ИНЕКЕ основного запроса в ИНЕКЕ внутреннего запроса. Exists будет вычисляться до true запроса внутри скобок, возвращает результаты, в противном случае - false.

Кроме того, последний in оператор должен быть заменен на =, так как select SELECT max(Id) будет возвращать только одно значение.

2

Я хотел бы попробовать переписать этот запрос что-то вроде этого (это лишь доказательство концепции с использованием CTE):

declare @categoryid int = 2; 

with products_a as (
    select [scanresultebay].productebayid 
       from [scanresultebay] 
         inner join [productebay] on [productebay].id = [scanresultebay].productebayid 
         inner join [category] on [category].id = productebay.categoryid 
       where [productebay].categoryid = @categoryid and [productebay].expired is null 
       group by [scanresultebay].productebayid 
    ), -- 1st from where clause 
    products_b as (
    select max(id) id 
       from [scanresultebay] 
       where productebayid = [scanresultebay].productebayid 
         and [scanresultebay].price <> 0 
       group by [scanresultebay].[productebayid] 
    ), -- 2nd from where clause 
    items as (
    select [scanresultebay].id 
      , [scanresultebay].productebayid 
      , [scanresultebay].price 
      , [scanresultebay].stockamount 
      , [scanresultebay].timestamp 
      , [category].categoryname 
    from [scanresultebay] 
      inner join [productebay] on [productebay].id = [scanresultebay].productebayid 
      inner join [category] on [category].id = [productebay].categoryid 
    ) 
    select 
     items.* 
    from  
     items 
     join 
      products_a 
     on 
      products_a.productebayid = items.productebayid 
     join 
      products_b 
     on 
      products_b.id = items.id 
    ; 

Это устраняет IN и, разделив запрос, он делает проще проверить/оптимизировать частичные запросы.

Я не мог запустить его, поэтому могут быть небольшие ошибки.

Он не содержит часть о цене - я не расслышал стыки использовать, , но я хотел бы использовать что-то вроде этого (как часть запроса выше):

select id, 
[scanresultebay].productebayid, 
price, 
row_number() over (partition by [scanresultebay].productebayid order by [scanresultebay].id desc) nr -- top price is nr = 1 per each [scanresultebay].productebayid 
from [scanresultebay] 
     inner join [productebay] on [productebay].id = .[scanresultebay].productebayid 
     inner join [ebayseller] on [ebayseller].id = [productebay].ebaysellerid 
where [dbo].[ebayseller].id = 28 
     and [productebay].categoryid = @categoryid 

Я предположил, что [scanresultebay].productebayid является ключом для соединения. Запрос выше (после настройки) можно добавить к первому запросу выше и в конечном запросе добавить .

У меня нет хорошего инструмента для проверки формата и синтаксиса, поэтому, пожалуйста, простите любые ошибки выше.