使用WITH ROLLUP + HAVING的意外结果

时间:2012-11-27 16:52:05

标签: sql sql-server-2008 tsql

此查询来自“Microsoft SQL Server 2008 - 数据库开发”(如果您有本书,请参见第26页)。并且,由于在本书中找到了查询,因此我正在运行AdventureWorks2008数据库。问题出现在第一个查询中,但下面有第二个查询,其中包含一个解决方法。

问题:为什么我们在使用WITH ROLLUP和HAVING子句时缺少ProductCategoryID = 2的小计行,以及下一个查询的总行?

所有ProductCategoryID都有一个小计行,删除HAVING子句时结果集中包含一个总行。

查询:

SELECT
    Production.ProductCategory.ProductCategoryID
    , Production.Product.ProductSubcategoryID
    , AVG(ListPrice) AS 'Average'
    , MIN(ListPrice) AS 'Minimum'
    , MAX(ListPrice) AS 'Maximum'
FROM Production.Product
JOIN Production.ProductSubcategory ON Production.ProductSubcategory.ProductSubcategoryID = Production.Product.ProductSubcategoryID
JOIN Production.ProductCategory ON Production.ProductSubcategory.ProductCategoryID = Production.ProductCategory.ProductCategoryID
WHERE
    ListPrice <> 0
GROUP BY
    Production.ProductCategory.ProductCategoryID
    , Production.Product.ProductSubcategoryID
WITH ROLLUP
HAVING
    MIN(ListPrice) > 200

结果:

ProductCategoryId   ProductSubcategoryId    Average     Minimum Maximum
1                   1                       1683.365    539.99  3399.99
1                   2                       1597.45     539.99  3578.27
1                   3                       1425.2481   742.35  2384.07
1                   NULL                    1586.737    539.99  3578.27
2                   12                      678.2535    249.79  1364.50
2                   14                      780.0436    337.22  1431.50
2                   16                      631.4155    333.42  1003.91

查询结果我希望在原始查询中看到的结果(通过用子查询替换我们的HAVING子句来解决问题):

SELECT
    Production.ProductCategory.ProductCategoryID
    , Production.Product.ProductSubcategoryID
    , AVG(ListPrice) AS 'Average'
    , MIN(ListPrice) AS 'Minimum'
    , MAX(ListPrice) AS 'Maximum'
FROM Production.Product
JOIN Production.ProductSubcategory ON Production.ProductSubcategory.ProductSubcategoryID = Production.Product.ProductSubcategoryID
JOIN Production.ProductCategory ON Production.ProductSubcategory.ProductCategoryID = Production.ProductCategory.ProductCategoryID
WHERE
    ListPrice <> 0
    AND Production.Product.ProductSubcategoryID IN 
    (
        SELECT
            Production.Product.ProductSubcategoryID
        FROM Production.Product
        GROUP BY
            Production.Product.ProductSubcategoryID
        HAVING
            MIN(ListPrice) > 200
    )
GROUP BY
    Production.ProductCategory.ProductCategoryID
    , Production.Product.ProductSubcategoryID
WITH ROLLUP

结果:

ProductCategoryId   ProductSubcategoryId    Average     Minimum Maximum
1                   1                       1683.365    539.99  3399.99
1                   2                       1597.45     539.99  3578.27
1                   3                       1425.2481   742.35  2384.07
1                   NULL                    1586.737    539.99  3578.27
2                   12                      678.2535    249.79  1364.50
2                   14                      780.0436    337.22  1431.50
2                   16                      631.4155    333.42  1003.91
2                   NULL                    710.1015    249.79  1431.50
NULL                NULL                    1193.2472   249.79  3578.27

2 个答案:

答案 0 :(得分:1)

我同意“由于在ROLLUP子句之后应用了HAVING子句,因此缺少那些行”。 获取行的一种方法是从以下位置更改HAVING子句:

HAVING
     MIN(ListPrice) > 200

为:

HAVING
     (MIN(ListPrice) > 200)
  or (Grouping (Production.ProductCategory.ProductCategoryID) = 1)
  or (Grouping (Production.Product.ProductSubcategoryID) =  1)

此外,应该有一个订单条款。类似的东西:

Order by Case
            When (Grouping (Production.ProductCategory.ProductCategoryID) = 1) Then 1
            Else 0
            End,
         Production.ProductCategory.ProductCategoryID,
         Case
            When (Grouping (Production.Product.ProductSubcategoryID) =  1) Then 1
            Else 0
            End,
         Production.Product.ProductSubcategoryID

答案 1 :(得分:0)

缺少那些行,因为在ROLLUP子句之后应用了HAVING子句,而对于那些行,MIN(ListPrice)小于200(对于 ProductSubcategoryId ProductCategoryId 2的总数为20.24,对于2.29为总计)。

要获得与第二个查询相同的结果,您还可以使用:

SELECT
    c.ProductCategoryID
    , s.ProductSubcategoryID
    , AVG(ListPrice) AS 'Average'
    , MIN(ListPrice) AS 'Minimum'
    , MAX(ListPrice) AS 'Maximum'
FROM (
    SELECT *, MIN(ListPrice) OVER (PARTITION BY ProductSubcategoryID) AS MinimumListPriceForSubcategory
    FROM Production.Product
) p
JOIN Production.ProductSubcategory s ON s.ProductSubcategoryID = p.ProductSubcategoryID
JOIN Production.ProductCategory c ON s.ProductCategoryID = c.ProductCategoryID
WHERE ListPrice <> 0
AND MinimumListPriceForSubcategory>200
GROUP BY c.ProductCategoryID, s.ProductSubcategoryID
WITH ROLLUP