【问题标题】:SQL Server 2008 R2: Pivot table query performanceSQL Server 2008 R2:数据透视表查询性能
【发布时间】:2016-01-07 10:18:56
【问题描述】:

表格:产品

create table Product
(
    productID int,
    productName varchar(20),
    productsalesdate DATETIME,
    producttype varchar(20)
);

插入

insert into product values(1,'PenDrive','2010-01-01','Electronic');
insert into product values(1,'Computer','2016-01-01','Electronic');
insert into product values(1,'Laptop','2011-02-02','Electronic');
insert into product values(2,'textbook','2014-02-02','books');
insert into product values(2,'notebook','2016-01-01','books');
insert into product values(3,'Car','2016-01-01','Vehicle');
insert into product values(3,'Bike','2016-01-07','Vehicle');

第一次尝试:在这里我弄错了 productType 的总和

SELECT productID, FirstSale,LastSale, [Electronic],[books],[Vehicle]
FROM
(
    SELECT 
        productID, 
        MIN(ProductSalesdate) as FirstSale,
        MAX(ProductSalesdate) as LastSale,
        productType
    FROM
        Product
    Group by productID,productType
) a 
PIVOT
(
    COUNT(productType) 
    FOR productType IN ( [Electronic],[books],[Vehicle] )
) AS pvt;   

第二次尝试:在这次尝试中,我解决了求和问题,但查询需要更多时间来执行大量记录。

SELECT productID,FirstSale,LastSale ,[Electronic],[books],[Vehicle]
FROM
(
    SELECT a.ProductID, a.FirstSale, a.LastSale, b.ProductType
    FROM Product b
    inner join
    (
        SELECT 
            productID, 
            MIN(ProductSalesdate) as FirstSale,
            MAX(ProductSalesdate) as LastSale
        FROM
            Product
        Group by productID
    ) as a 
    ON a.ProductID = b.ProductID
) ab 
PIVOT
(
    COUNT(productType) 
    FOR productType IN ( [Electronic],[books],[Vehicle] )
) AS pvt;   

注意:第二个查询工作正常,但问题在于性能,因为 我要加入两个相同的表,因为要在数据透视查询中获取 productType 的计数。

问题:如何优化我第二次尝试的第二次查询?

【问题讨论】:

  • 某个productType的产品可以有相同的productId吗?
  • 此外,您在第二次尝试中有一个派生表 a,它在 productType, productId 上分组,但您仅在 productId 上与 b 一起加入它......看起来很可疑
  • 您声称您的性能有问题。你能描述一下吗?需要几秒钟、几分钟、几小时?您预计执行时间是多少?
  • 另外,你在Product桌子上有合适的INDEXes吗?
  • @TT,对于 15k 条记录,这似乎需要 10 分钟,是的,我在 productID 上有 index

标签: sql sql-server sql-server-2008-r2 pivot


【解决方案1】:

下面使用临时表来存储派生表ab。我的猜测是它会改进第二个查询的执行计划。

SELECT a.ProductID, a.FirstSale, a.LastSale, b.ProductType
INTO #ab
FROM Product b
inner join
(
    SELECT 
        productID, 
        MIN(ProductSalesdate) as FirstSale,
        MAX(ProductSalesdate) as LastSale
    FROM
        Product
    Group by productID
) as a 
ON a.ProductID = b.ProductID;

SELECT productID,FirstSale,LastSale ,[Electronic],[books],[Vehicle]
FROM #ab AS ab 
PIVOT
(
    COUNT(productType) 
    FOR productType IN ( [Electronic],[books],[Vehicle] )
) AS pvt;

DROP TABLE #ab;

编辑:只是为了运动,我编写了以下脚本,该脚本在#product 中有 15k 行。整个脚本在约 1 秒内执行。我仍然不明白您的查询需要 5.5 分钟。如下:

SET NOCOUNT ON;

CREATE TABLE #product (
    product_id INT,
    product_name VARCHAR(20),
    product_sales_date DATE,
    product_type VARCHAR(20)
);

DECLARE @cnt INT=0;
WHILE @cnt<15000
BEGIN
    INSERT INTO #product(
        product_id,
        product_name,
        product_sales_date,
        product_type
    )
    SELECT 
        product_id=ROUND(20*RAND(),0),
        product_name=LEFT(NEWID(),20),
        product_sales_date=DATEADD(DAY,ROUND((-10+20*RAND()), 0),GETDATE()),
        product_type=
            CASE ROUND(2*RAND(),0)
                WHEN 0 THEN 'Electronic'
                WHEN 1 THEN 'books'
                ELSE 'Vehicle'
            END;

    SET @cnt=@cnt+1;
END

SELECT a.product_id, a.first_sale, a.last_sale, b.product_type
INTO #ab
FROM #product b
inner join
(
    SELECT 
        product_id, 
        MIN(product_sales_date) as first_sale,
        MAX(product_sales_date) as last_sale
    FROM
        #product
    GROUP BY
        product_id
) as a 
ON a.product_id= b.product_id;

SELECT product_id,first_sale,last_sale,[Electronic],[books],[Vehicle]
FROM #ab AS ab 
PIVOT
(
    COUNT(product_type) 
    FOR product_type IN ( [Electronic],[books],[Vehicle] )
) AS pvt;

DROP TABLE #ab;
DROP TABLE #product;

【讨论】:

  • 现在是 5.5 分钟。这对我来说意义重大。
  • @MAK 哎呀,还是很多。也许您可以在临时表上放置一个适当的 KEY/INDEX?这要求您首先创建临时表并在其上添加索引(就像您在常规表上创建和添加索引一样)。那么第一条语句将变为INSERT INTO #ab(...cols...) SELECT ...(没有INTO #ab)。
  • @MAK 例如,您可以分析实际执行计划(在 SSMS 中,单击工具栏中的“包含实际执行计划”按钮)。看看它是否提出了任何索引。
  • @MAK 在我的答案中添加了一个测试脚本,它有 15k 行,相同的数据透视查询在 1 秒内运行。
  • 嘿,非常感谢!明天将测试它并通知您。
【解决方案2】:

似乎你正在尝试做这样的事情。不知道为什么你需要额外的连接或临时表。

SELECT * FROM
(
    SELECT  productID, 
            productType,
            MIN(ProductSalesdate) as FirstSale,
            MAX(ProductSalesdate) as LastSale,
            COUNT(productType) AS ProductCount
    FROM Product
    GROUP BY productID,productType
) t
PIVOT 
(
    SUM(ProductCount)
    FOR productType IN ([Electronic],[books],[Vehicle])
) p

如果计数为 0,您会得到 NULLS,但您可以很容易地将这些值合并为 0

【讨论】:

  • OP 的 PIVOT 查询存在性能问题,这就是为什么我建议先将派生表具体化为临时表的原因。很可能是您的版本做得更好。
  • @TT。从我最近在这里注意到的情况来看,pivot 本身存在性能问题,最好将那些丑陋的聚合与案例语句一起使用
  • 非常正确。我可以向您推荐这个thread on SO,其中使用老式 PIVOT(使用 CASE)和 OPTION(HASH GROUP) 的组合对性能不佳的 PIVOT 进行了优化,以加快分组速度。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-29
  • 1970-01-01
相关资源
最近更新 更多