我正在使用MS SQL Server Management Studio 2008.我遇到编写子查询的问题。这是整个查询,如下所示:
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
当我添加第二个子查询时,问题首先出现了。第一个子查询没有返回任何错误,但它显示了每个ShipDate记录中“Dist Freight”的全部平均总和,而不是仅仅该ShipDate的平均值。我编写了第二个子查询来尝试修复它,但现在我收到了这个错误:
Msg 512,Level 16,State 1,Line 1
子查询返回的值超过1。当子查询遵循=,!=,<,< =,>,> =或子查询用作表达式时,不允许这样做。
如果我要澄清任何事情,请告诉我。
答案 0 :(得分: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
答案 1 :(得分:4)
问题是您有一个子查询返回多行,并且您尝试将其存储为单行/列组合中的值。
要理解为什么会出现这种情况,让我们看一下外部查询的结果,即“大西洋运费”:
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
让我们看一下内部子查询可能返回的内容:
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
最后,表2的不同Non-Atlantic Freight Charge
行是什么样的?
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 | |
| | | |-----------------------------| |
|---------------------------------------------------------------------------|
呃哦。 我们的桌子里有一张桌子 。
那就是问题,我们在另一张桌子里面有一张桌子!
因此,您的问题有两个解决方案。你应该评估每个人的表现。
第一种方法是使用名为Common Table Expressions or CTEs的功能运行两个单独的查询并加入结果。
该查询将如下所示:
; 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
我做了一些改变,我需要指出:
ORDER BY
在公用表表达式中实际上是被禁止的,因此我必须将该子句移到最后。FULL OUTER JOIN
连接它们,因此在一个中缺少的任何行仍将显示,但它将显示为零。确保这是你想要的。COALESCE
来确保如果有一天没有Atlantic Freight Charge
s,因此Atlantic
CTE中没有与当天相对应的ShipDate,那么它将会使用NonAtlantic
CTE中的日期。 这种方式的工作方式是它连接两个查询:
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
因此COALESCE
和ISNULL
允许我将其转换为一组数据,如下所示:
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 Clause的语义,以便了解有关如何仅在单个查询中运行报表的更多信息。我自己实现了类似的查询,但通常使用的是SUM
,而不是AVG
。我会提供一个使用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是否计算了空行。
无论如何,我希望我已经回答了你的问题,并帮助你理解为什么你的查询有问题。
答案 2 :(得分: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