問題は、複数の行を返すサブクエリがあり、それを単一の行/列の組み合わせで値として格納しようとしていることです。
この理由を理解するために、外側のクエリである「Atlantic Freight Charge」の結果がどのようになるかを見てみましょう。
表 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 句に基づいて、3 つの列を含むレポートが必要であることを指定しています。レイアウトは次のとおりです。
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
、2 番目の列が、3 番目の列が内部サブクエリからのすべての個別Atlantic Freight Charge
のクエリであることがわかります。Non-Atlantic Freight Charge
SQL Server がこれを正しく表すために、そのクエリの結果を最初のテーブルに入れることを想像してみてください。
したがって、表 1 の最初の行は次のようになります。
ShipDate Atlantic Freight Charge
01/01/2012 1.00
column を追加する必要が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 | |
| | | |-----------------------------| |
|---------------------------------------------------------------------------|
ええとああ。テーブルの中にテーブルがあります。
それが問題です。あるテーブルが別のテーブルの中にあるのです!
したがって、問題には2つの解決策があります。それぞれのパフォーマンスを評価する必要があります。
1 つ目は、共通テーブル式または CTEと呼ばれる機能を使用して、2 つの個別のクエリを実行し、結果を結合することです。
そのクエリは次のようになります。
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
私が指摘する必要があるいくつかの変更があります:
- 「Order By」を削除しました。一般に、SQL Server からデータを消費しているものは何でも順序付けを行う必要があります。クライアント アプリケーションが同様にそれを実行できるときに、サーバーに何かを注文するように要求して、サーバーに不必要に負担をかけないでください。
ORDER BY
とにかく、一般的なテーブル式では実際には禁止されているため、その句を最後に移動する必要があります。
- クエリを Atlantic と NonAtlantic の 2 つの部分に分割し、
FULL OUTER JOIN
それらを接続するために a を使用しました。そのため、一方にない行は表示されますが、ゼロが表示されます。これがあなたが望むものであることを確認してください。
- a を使用し
COALESCE
て、Atlantic Freight Charge
s のない日があり、CTE にその日に対応する ShipDate がない場合、 CTEAtlantic
の日付が使用されるようにしますNonAtlantic
。
これが機能する方法は、次のように 2 つのクエリを接続することです。
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
ただし、それはおそらく最高のパフォーマンスのソリューションではありません
実装が最も簡単で、2 つのクエリを取得し、両方を実行して、結果を結合します。ただし、SQL Server では、結果を分割できる集計関数がサポートされています。単一のクエリのみでレポートを実行する方法について詳しく知るために、 OVER 句のセマンティクスを調べることに興味があるかもしれません。私はそのようなクエリを自分で実装しましたが、通常はSUM
s ではなくAVG
s を使用します。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 が null 行をカウントするかどうかは忘れてしまいます。
とにかく、私はあなたの質問に答え、クエリに問題があった理由を理解するのに役立つことを願っています.