2

I'm using MS SQL Server Management Studio 2008. I'm having an issue with writing a subquery. This is the entire query as follows:

SELECT DISTINCT 
    FH.ShipDate, AVG(FH.[Dist Freight]) AS [Atlantic Freight Charge], 
    (SELECT DISTINCT [Non-Atlantic Freight Charge]
     FROM 
         (SELECT DISTINCT 
              FH.ShipDate, AVG(FH.[Dist Freight]) AS [Non-Atlantic Freight Charge]
          FROM dbo.vw_FreightHistory AS FH
          WHERE VendorName != 'Atlantic Trucking'
          GROUP BY ShipDate, VendorName) AS [Non-Atlantic Freight Charge])
FROM dbo.vw_FreightHistory as FH
WHERE VendorName = 'Atlantic Trucking'
GROUP BY ShipDate, VendorName
ORDER BY ShipDate

The issue first came up when I added that second subquery. The first subquery did not return any errors but it showed the entire average sum of "Dist Freight" in each ShipDate record rather than the average for only that ShipDate. I wrote the second subquery to try and fix that, but now I'm getting this error:

Msg 512, Level 16, State 1, Line 1
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.

Please let me know if I should clarify anything.

4

3 回答 3

4

问题是您有一个返回多行的子查询,并且您试图将其作为值存储在单行/列组合中。

为了理解为什么会这样,让我们​​看一下外部查询“大西洋运费”的结果:

表 1 - 大西洋运费

ShipDate        Atlantic Freight Charge
01/01/2012      1.00
01/02/2012      1.00
01/03/2012      1.00
01/04/2012      1.00
01/05/2012      1.00

让我们看看内部子查询可能返回什么:

表 2 - 非大西洋运费

ShipDate        Non-Atlantic Freight Charge
01/01/2012      2.00
01/02/2012      3.00
01/03/2012      4.00
01/04/2012      5.00
01/05/2012      6.00

最后,Non-Atlantic Freight Charge表 2 的不同行是什么样的?

表 3 - 不同的非大西洋运费

Non-Atlantic Freight Charge
2.00
3.00
4.00
5.00
6.00

现在,在 SQL 中,您根据您的 SELECT 子句指定您想要一个包含三列的报告。这是如何布置的:

SELECT DISTINCT 

  FH.ShipDate

, AVG(FH.[Dist Freight]) AS [Atlantic Freight Charge]

, (SELECT DISTINCT [Non-Atlantic Freight Charge]
    FROM 
      (SELECT DISTINCT FH.ShipDate, AVG(FH.[Dist Freight]) AS [Non-Atlantic Freight Charge]
          FROM dbo.vw_FreightHistory AS FH
          WHERE VendorName != 'Atlantic Trucking'
          GROUP BY ShipDate, VendorName) AS [Non-Atlantic Freight Charge])

您会看到第一列是ShipDate,第二列是Atlantic Freight Charge,第三列是每个与内部子查询不同Non-Atlantic Freight Charge的查询。

为了让 SQL Server 正确地表示这一点,想象一下尝试将该查询的结果放在第一个表中。

所以对于表 1 的第一行:

ShipDate        Atlantic Freight Charge
01/01/2012      1.00

我们需要添加一个列Non-Atlantic Freight Charge,我们需要在其中存储表 3 中的查询结果:

| ShipDate      | Atlantic Freight Charge | Non-Atlantic Freight Charge     |
|---------------|-------------------------|---------------------------------|
| 01/01/2012    | 1.00                    | | Non-Atlantic Freight Charge | |
|               |                         | |-----------------------------| |
|               |                         | | 2.00                        | |
|               |                         | | 3.00                        | |
|               |                         | | 4.00                        | |
|               |                         | | 5.00                        | |
|               |                         | | 6.00                        | |
|               |                         | |-----------------------------| |
|---------------------------------------------------------------------------|

哦哦。我们有一张桌子,我们的桌子里面。

这就是问题所在,我们有一张桌子放在另一张桌子里面!

因此,您的问题有两种解决方案。您应该评估每个的性能。

第一种是使用称为公用表表达式或 CTE的功能来运行两个单独的查询并连接结果。

该查询将如下所示:

CTE 解决方案

;  WITH Atlantic AS (
 SELECT FH.ShipDate, AVG(FH.[Dist Freight]) AS [Atlantic Freight Charge]
   FROM dbo.vw_FreightHistory as FH
  WHERE VendorName = 'Atlantic Trucking'
  GROUP BY ShipDate
      )
      , NonAtlantic AS (
 SELECT FH.ShipDate, AVG(FH.[Dist Freight]) AS [Non-Atlantic Freight Charge]
   FROM dbo.vw_FreightHistory as FH
  WHERE VendorName != 'Atlantic Trucking'
  GROUP BY ShipDate
      )
 SELECT COALESCE(Atlantic.ShipDate, NonAtlantic.ShipDate)
      , ISNULL([Atlantic Freight Charge], 0) AS [Atlantic Freight Charge]
      , ISNULL([Non-Atlantic Freight Charge], 0) AS [Non-Atlantic Freight Charge]
   FROM Atlantic
        FULL OUTER JOIN NonAtlantic
          ON Atlantic.ShipDate = NonAtlantic.ShipDate

我做了一些更改,我需要指出:

  1. 我删除了“Order By”,一般来说,排序应该由消耗 SQL Server 数据的任何东西来完成,当您的客户端应用程序也可以这样做时,不要通过要求它订购某些东西来不必要地对服务器征税。
  2. ORDER BY无论如何,在公用表表达式中实际上是被禁止的,所以我必须将该子句移到最后。
  3. 我已将您的查询分成两部分,Atlantic 和 NonAtlantic,并使用 aFULL OUTER JOIN将它们连接起来,因此其中缺少的任何行仍会出现,但会显示为零。确保这是您想要的。
  4. 我使用 aCOALESCE来确保如果有一天没有Atlantic Freight Charges 并且因此在AtlanticCTE 中没有与该天对应的 ShipDate,那么它将使用NonAtlanticCTE 中的日期。

它的工作方式是连接两个查询,如下所示:

ShipDate        Atlantic Freight Charge | FULL OUTER JOIN |  ShipDate        Non-Atlantic Freight Charge
01/01/2012      1.00                    |                 |  NULL            NULL
01/02/2012      1.00                    |                 |  NULL            NULL
01/03/2012      1.00                    |                 |  NULL            NULL
01/04/2012      1.00                    |                 |  01/03/2012      2.00
01/05/2012      1.00                    |                 |  01/04/2012      3.00
NULL            NULL                    |                 |  01/05/2012      4.00
NULL            NULL                    |                 |  01/06/2012      5.00
NULL            NULL                    |                 |  01/07/2012      6.00

因此COALESCEISNULL允许我将其转换为一组数据,如下所示:

ShipDate        Atlantic Freight Charge  Non-Atlantic Freight Charge
01/01/2012      1.00                     0.00
01/02/2012      1.00                     0.00
01/03/2012      1.00                     0.00
01/04/2012      1.00                     2.00
01/05/2012      1.00                     3.00
01/05/2012      0.00                     4.00
01/06/2012      0.00                     5.00
01/07/2012      0.00                     6.00

然而,这可能不是最好的解决方案

这是最容易实现的,获取两个查询,运行它们,然后加入结果。但 SQL Server 支持聚合函数,可让您对结果进行分区。您可能有兴趣研究OVER 子句的语义,以了解有关如何仅在单个查询中运行报告的更多信息。我自己也实现了这样的查询,但通常使用SUMs,而不是AVGs。我会使用 OVER 子句提供解决方案的可能实现,但它可能有点过于复杂,我担心我会弄乱正确地平均结果。实际上,现在我考虑一下,这样的事情可能会很好:

SELECT FH.ShipDate
     , AVG(CASE WHEN VendorName = 'Atlantic Trucking'  THEN FH.[Dist Freight] ELSE NULL END) AS [Atlantic Freight Charge]
     , AVG(CASE WHEN VendorName != 'Atlantic Trucking' THEN FH.[Dist Freight] ELSE NULL END) AS [Non-Atlantic Freight Charge]
  FROM dbo.vw_FreightHistory as FH
 GROUP BY ShipDate
 ORDER BY ShipDate

但是我忘记了 AVG 是否计算空行。

无论如何,我希望我已经回答了您的问题,并帮助您理解了您的查询为何有问题。

于 2013-07-19T16:42:12.737 回答
4

我认为你想要的是这样的:

 SELECT 
    FH.ShipDate, 
    AVG(CASE 
           WHEN VendorName = 'Atlantic Trucking' 
           THEN FH.[Dist Freight] 
           ELSE NULL 
        END) AS [Atlantic Freight Charge],  
    AVG(CASE 
           WHEN VendorName != 'Atlantic Trucking' 
           THEN FH.[Dist Freight] 
           ELSE NULL 
         END) AS [Non-Atlantic Freight Charge]
FROM 
    dbo.vw_FreightHistory as FH
GROUP BY 
    ShipDate
ORDER BY 
    ShipDate
于 2013-07-19T16:23:28.520 回答
0

如果返回多行,这取决于您希望在单列中出现的内容?

如果您想要费用,请使用SUM()您的多行返回子查询:

SELECT DISTINCT FH.ShipDate, AVG(FH.[Dist Freight]) AS [Atlantic Freight Charge], 
SUM(SELECT DISTINCT [Non-Atlantic Freight Charge]
  FROM 
    (SELECT DISTINCT FH.ShipDate, AVG(FH.[Dist Freight]) AS [Non-Atlantic Freight Charge]
        FROM dbo.vw_FreightHistory AS FH
        WHERE VendorName != 'Atlantic Trucking'
        GROUP BY ShipDate, VendorName) AS [Non-Atlantic Freight Charge]))
FROM dbo.vw_FreightHistory as FH
WHERE VendorName = 'Atlantic Trucking'
GROUP BY ShipDate, VendorName
ORDER BY ShipDate
于 2013-07-19T16:21:08.917 回答