0

I'm trying to optimize or change the SQL to work with inner joins rather than independent calls

Database: one invoice can have many payment records and order (products) records

Original:

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

I'm sure we can do this another way by grouping and joining tables together. When I tried the SQL statement below, I wasn't getting the same amount (count) of records...I'm thinking in respect to the type of join or where it joins !! but still couldn't get it working after 3 hrs of looking on the screen..

So far I got to...

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

Sorry for the bad english and I'm not sure what to search for to find if it's already been answered here :D

Thanks in advance for all the help

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())

两条评论:关键领域是orders真的Invoice还是也是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 英镑的付款,由于订单表中有两条记录,这一数字翻了一番,达到 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

Your "currentYear" reference could be parameterized or you can use from getting the current date from sql function such as

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 回答