【问题标题】:SQL Server - Create Index on query over multiple tables?SQL Server - 在多个表的查询上创建索引?
【发布时间】:2015-11-17 12:41:13
【问题描述】:

我在优化特定 SQL 查询时遇到了很大的问题。它包含多个内部连接,向每个表添加索引根本不会提高性能。

我的查询:

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])

我需要这个查询来在我网站的仪表板上显示信息。所以我必须用这个查询遍历每个类别(总共 100 个)。这最多持续 30-40 秒,这当然是太多了。

创建视图是个问题,因为我还必须为子查询声明 categoryid 的参数,并且参数不能传递给视图。

所以我的问题:

  1. 我可以为这个查询创建一个特定的索引,这样它会变得更快(现在需要 0.5 秒)。
  2. 由于这些子查询,我的查询是否太长(或无效)?
  3. 我还能做些什么来提高我的表现?

【问题讨论】:

  • 首先要看执行计划。我可以给您的一个提示是尝试将 in 运算符更改为 exists 运算符。
  • 只是想排除参数嗅探:请在查询末尾添加选项(重新完成)并让我们知道
  • 对我来说,这似乎是 CARDINALITY ISSUE.. 如果数据不大,您能否在 TEMP TABLE 中实现您的 2 IN 条件?
  • 这里和那里有一个名为productebayid 的字段,没有任何前缀。该字段来自哪个表?还给表起别名:在没有别名的情况下在多个地方使用的查询中使用相同的表可能会由于您的解释与优化器的解释之间的差异而导致意外问题
  • @Paolo 感谢您的编辑,我用正确的表别名更新了它。

标签: sql sql-server indices


【解决方案1】:

我会尝试将此查询重写为类似的内容(这只是使用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 是加入的关键。 上面的查询(调整后)可以添加到上面的第一个查询并加入 在最终查询中。

我没有很好的格式和语法检查工具,所以请原谅上面的任何错误。

【讨论】:

    【解决方案2】:

    这不是一个完整的答案,而是对您在 cmets 中关于存在语法的问题的回答。
    而不是:

    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 从主查询的 where 子句移到内部查询的 where 子句中。 Exists 将评估为true 括号内的查询返回结果,否则为false。

    此外,最后一个 in 运算符应替换为 =,因为 select SELECT max(Id) 只会返回一个值。

    【讨论】:

      【解决方案3】:

      您应该为每个外键创建索引。

      如果您的 where 子句包含单个表的多个字段,那么您也应该在这些组合上创建索引。

      编辑:

      详细说明:

      1. 索引是全局的。不是特定的查询。
      2. 100 个类别非常少。你有很多连接(都是必需的)
      3. 创建可大幅提高性能的索引!

      【讨论】:

        猜你喜欢
        • 2012-07-31
        • 1970-01-01
        • 1970-01-01
        • 2018-11-05
        • 2015-02-15
        • 2023-03-04
        • 1970-01-01
        • 2015-08-16
        • 1970-01-01
        相关资源
        最近更新 更多