0

独立した呼び出しではなく、内部結合で動作するように SQL を最適化または変更しようとしています。

データベース: 1 つの請求書に多数の支払記録と注文 (製品) 記録を含めることができます

オリジナル:

SELECT 
    InvoiceNum, 
    (SELECT SUM(Orders.Cost) FROM Orders WHERE Orders.Invoice = InvoiceNum  and Orders.Returned <> 1 GROUP BY Orders.Invoice) as vat_only, 
    (SELECT SUM(Orders.Vat) FROM Orders WHERE Orders.Invoice = InvoiceNum and Orders.Returned <> 1 GROUP BY Orders.Invoice) as sales_prevat, 
    (SELECT SUM(pay.Amount) FROM Payments as pay WHERE Invoices.InvoiceNum = pay.InvoiceNum ) as income
FROM 
    Invoices 
WHERE 
    InvoiceYear = currentyear

テーブルをグループ化して結合することで、これを別の方法で行うことができると確信しています。以下の SQL ステートメントを試したところ、同じ量 (カウント) のレコードが得られませんでした...結合の種類または結合場所に関して考えています!! しかし、画面を3時間見た後でも、まだ機能しませんでした..

これまでのところ...

SELECT 
    Invoices.InvoiceNum, 
    Sum(Orders.Cost) AS SumOfCost, 
    Sum(Orders.VAT) AS SumOfVAT, 
    SUM(distinct Payments.Amount) as money
FROM 
    Invoices
LEFT JOIN 
    Orders ON Orders.Invoice = Invoices.InvoiceNum 
LEFT JOIN 
    Payments ON Invoices.InvoiceNum = Payments.InvoiceNum
WHERE 
    Invoices.InvoiceYear = 11 
    AND Orders.Returned <> 1
GROUP BY 
    Invoices.InvoiceNum

悪い英語で申し訳ありません。ここで既に回答されている場合、何を検索すればよいかわかりません:D

すべての助けを前もって感謝します

4

4 に答える 4

3

あなたの問題は、注文に請求書の複数の行があり、請求書に複数の支払いがあることです (場合によっては)。これにより、特定の注文に対してクロス積効果が発生します。これを修正するには、テーブルを事前に要約します。

関連する問題として、join支払いがない場合は が失敗するため、 が必要left outer joinです。

select i.InvoiceNum, osum.cost, osum.vat, p.income
from Invoice i left outer join
     (select o.Invoice, sum(o.Cost) as cost, sum(o.vat) as vat
      from orders o
      where Returned <> 1
      group by o.Invoice
     ) osum
     on osum.Invoice = i.InvoiceNum left outer join
     (select p.InvoiceNum, sum(pay.Amount) as income
      from Payments p
      group by p.InvoiceNum
     ) psum
     on psum.InvoiceNum = i.InvoiceNum
where i.InvoiceYear = year(getdate())

2 つのコメント: Is the key field for ordersreallyInvoiceまたは is also InvoiceNum? また、フィールドはありますInvoice.InvoiceYearか?それとも節で欲しいyear(i.InvoiceDate)ですか?where

于 2013-06-14T15:35:14.297 に答える
2

支払いと注文の両方に請求書ごとに複数のレコードを含めることができると仮定すると、相互結合を避けるためにサブクエリで集計を行う必要があります。

SELECT  Invoices.InvoiceNum, o.Cost, o.VAT, p.Amount
FROM    Invoices
        LEFT JOIN
        (   SELECT  Invoice, Cost = SUM(Cost), VAT = SUM(VAT)
            FROM    Orders
            WHERE   Orders.Returned <> 1
            GROUP BY Invoice
        ) o
            ON o.Invoice = Invoices.InvoiceNum
        LEFT JOIN
        (   SELECT  InvoiceNum, Amount = SUM(Amount)
            FROM    Payments
            GROUP BY InvoiceNum
        ) P
            ON P.InvoiceNum = Invoices.InvoiceNum
WHERE   Invoices.InvoiceYear = 11;

補遺

コメントを拡張するにはCROSS JOIN、請求書のこのデータを想像してください (1)

注文

Invoice     Cost        VAT
1           15.00       3.00
1           10.00       2.00

支払い

InvoiceNum      Amount
1               15.00
1               10.00

これらのテーブルを結合すると、次のようになります。

SELECT  Orders.*, Payments.Amount
FROM    Invoices
        LEFT JOIN Orders 
            ON Orders.Invoice = Invoices.InvoiceNum 
        LEFT JOIN Payments 
            ON Invoices.InvoiceNum = Payments.InvoiceNum;

最終的には次のようになります。

Orders.Invoice  Orders.Cost Orders.Vat  Payments.Amount
1               15.00       3.00        15.00                       
1               10.00       2.00        15.00
1               15.00       3.00        10.00
1               10.00       2.00        10.00

つまり、支払い/注文のすべての組み合わせであるため、請求書ごとに必要以上の行が取得され、合計が歪められます。したがって、元のデータには 25 ポンドの支払いがありましたが、注文テーブルに 2 つのレコードがあるため、これは 2 倍の 50 ポンドになります。これが、各テーブルを個別に集計する必要がある理由です。単一の請求書で同じ金額に対して複数の支払い/注文があった場合、DISTINCT を使用しても機能しません。


最適化に関する最後のポイントとして、おそらくテーブルにインデックスを付ける必要があります。クエリを実行して実際の実行プランを表示すると、SSMS がインデックスを提案しますが、次のようにするとパフォーマンスが向上するはずです。

CREATE NONCLUSTERED INDEX IX_Orders_InvoiceNum ON Orders (Invoice) INCLUDE(Cost, VAT, Returned);
CREATE NONCLUSTERED INDEX IX_Payments_InvoiceNum ON Payments (InvoiceNum) INCLUDE(Amount);

これにより、両方のサブクエリが各テーブルのインデックスのみを使用できるようになり、ブックマークのループアップやクラスター化インデックスのスキャンは不要になります。

于 2013-06-14T15:31:23.117 に答える
0
    select
          PreQuery.InvoiceNum,
          PreQuery.VAT_Only,
          PreQuery.Sales_Prevat,
          SUM( Pay.Amount ) as Income
       from
          ( select
                  I.InvoiceNum,
                  SUM( O.Cost ) as VAT_Only,
                  SUM( O.Vat ) as sales_prevat
               from 
                  Invoice I
                     Join Orders O
                        on I.InvoiceNum = O.Invoice
                       AND O.Returned <> 1
               where
                  I.InvoiceYear = currentYear 
               group by
                  I.InvoiceNum ) PreQuery
          JOIN Payments Pay
             on PreQuery.InvoiceNum = Pay.InvoiceNum
   group by
      PreQuery.InvoiceNum,
      PreQuery.VAT_Only,
      PreQuery.Sales_Prevat

「currentYear」参照をパラメーター化するか、次のようなSQL関数から現在の日付を取得することから使用できます

Year( GetDate() )
于 2013-06-14T15:26:05.190 に答える
0

これを試してみてください。私はテストしていないことに注意してください。メモ帳で一掃しただけです。どのサブテーブルにも存在しない請求書がある場合は、LEFT JOIN

SELECT InvoiceNum, vat_only, sales_prevat, income
FROM Invoices i
INNER JOIN (SELECT Invoice, SUM(Cost) [vat_only], SUM(Vat) [sales_prevat]
            FROM Orders
            WHERE Returned <> 1
            GROUP BY Invoice) o
    ON i.InvoiceNum = o.Invoice
INNER JOIN (SELECT SUM(Amount) [income]
            FROM Payments) p
    ON i.InvoiceNum = p.InvoiceNum 
WHERE i.InvoiceYear = currentyear
于 2013-06-14T15:35:42.460 に答える