簡體   English   中英

在子查詢中使用查詢列

[英]Use query column within subquery

我有兩個表:

INVOICES
ID | DISCOUNT_PRC
1  |   NULL
2  |   0.10
3  |   0.70
...

INVOICE_ITEMS
ID | INVOICE_ID | PRICE | ALT_PRICE
1  |     1      |  100  |  0
2  |     1      |  200  |  150
3  |     2      |  400  |  300
4  |     2      |  200  |  0
5  |     2      |  100  |  NULL
6  |     3      |  200  |  40
7  |     3      |  100  |  NULL
...

注意:該數據庫正在被另一個應用程序使用,不允許將零更改為NULL值,反之亦然。

我需要輸出每個發票的項目總和,每個項目的PRICE乘以折扣(1 - DISCOUNT_PRC) ,除非ALT_PRICE不為NULL且大於零。 在這種情況下,只需采用ALT_PRICE 因此,所需的輸出將如下所示:

INVOICES
ID | OVERALL_PRICE
1  |    250     (100*1) + (150)
2  |    570       (300) + (200*0.9) + (100*0.9)
3  |     70        (40) + (100*0.3)

到目前為止,我有:

select I.ID,
     case when ISNULL( IT.ALT_PRICE, 0) > 0
          then IT.ALT_PRICE
          else IT.PRICE * (1 - ISNULL( I.DISCOUNT_PRC, 0))
          end AS OVERALL_PRICE
from INVOICES I
join INVOICE_ITEMS IT on IT.INVOICE_ID = I.ID

結果

ID  OVERALL_PRICE
1   100
1   150
2   300
2   180
2   90
3   40
3   30

其結果是有效的每個項目,現在我需要SUM他們在每一行對應一個發票。 我嘗試使用LEFT JOIN然后使用OUTER_APPLY

select I.ID, items.OVERALL_PRICE FROM INVOICES I
OUTER APPLY ( select sum (
     case when ISNULL( IT.ALT_PRICE, 0) > 0
          then IT.ALT_PRICE
          else IT.PRICE * (1 - ISNULL( I.DISCOUNT_PRC, 0))
          end) AS OVERALL_PRICE
from INVOICE_ITEMS IT
where IT.INVOICE_ID = I.ID
group by IT.INVOICE_ID ) as items

但是我得到了同樣的錯誤:

Multiple columns are specified in an aggregated expression containing an outer reference. If an expression being aggregated contains an outer reference, then that outer reference must be the only column referenced in the expression.

編輯:我也想避免在我的主查詢列中包含SUM (或任何其他聚合表達式),因為這將要求將所有其他列分組。 實際表有50多個列和其他一些子查詢,因此我想避免這種情況。

編輯2:好的,我已經使用B3S(在子查詢中聯接),iSR5(OVER)和Gordon Linoff(APPLY)的解決方案進行了一些基准測試。 我已插入50k發票和500k發票項目,並且使用了MSSQL STATISTICS ,根據數據庫的大小,它應顯示足夠好的結果。 結果如下:

Join within subquery:
- CPU time = 1170 ms,  elapsed time = 335 ms.
- CPU time = 1202 ms,  elapsed time = 344 ms.
- CPU time = 1153 ms,  elapsed time = 348 ms.

OVER:
- CPU time = 3089 ms,  elapsed time = 1361 ms.
- CPU time = 3010 ms,  elapsed time = 1075 ms.
- CPU time = 3010 ms,  elapsed time = 1070 ms.

APPLY:
- CPU time = 2496 ms,  elapsed time = 2320 ms.
- CPU time = 2433 ms,  elapsed time = 2171 ms.
- CPU time = 2496 ms,  elapsed time = 2179 ms.

結論:我確實希望通過SQL對子查詢中的聯接進行優化,但是我仍然期望在其他兩個建議下看到更好的結果。 這對我來說是一個很大的驚喜,但是我不得不贊揚B3S(並接受答案)。 我確信內部連接會對性能造成打擊,所以我沒有去嘗試。 無論如何,不​​要猶豫從子查詢中連接外部表-當然,如果需要的話。

您非常接近:

select I.ID,
     sum(case when ISNULL( IT.ALT_PRICE, 0) > 0
          then IT.ALT_PRICE
          else IT.PRICE * (1 - ISNULL( I.DISCOUNT, 0))
          end) AS OVERALL_PRICE
from INVOICES I
join INVOICE_ITEMS IT on IT.INVOICE_ID = I.ID
GROUP BY I.ID

根據您的評論進行編輯:

select DISTINCT
     I.TESTFIELD1,
     I.TESTFIELD2,
     I.ID,
     (SELECT SUM(case when ISNULL( IT2.ALT_PRICE, 0) > 0
          then IT2.ALT_PRICE
          else IT2.PRICE * (1 - ISNULL( I2.DISCOUNT_PRC, 0))
          end)
     FROM INVOICES I2
     LEFT JOIN INVOICE_ITEMS IT2 ON IT2.INVOICE_ID = I2.ID
     WHERE I2.ID = I.ID
     GROUP BY I2.ID) AS OVERALL_PRICE
from INVOICES I
join INVOICE_ITEMS IT on IT.INVOICE_ID = I.ID

我添加了一些測試字段,以向您展示如何使用sum並避免group by每個表字段group by

SQL小提琴這里

我認為,您的apply方法應該在沒有group by情況下工作。

select I.ID, items.OVERALL_PRICE
FROM INVOICES I OUTER APPLY
     (select sum(case when IT.ALT_PRICE > 0
                      then IT.ALT_PRICE
                      else IT.PRICE * (1 - coalesce( I.DISCOUNT_PRC, 0))
                 end) AS OVERALL_PRICE
      from INVOICE_ITEMS IT
      where IT.INVOICE_ID = I.ID
     ) items;

請注意,初始條件不需要NULL檢查, because NULL will fails almost all comparisons. If will fails almost all comparisons. If ALT_PRICE is never negative or zero, you can simply use COALESCE()`:

select I.ID, items.OVERALL_PRICE
FROM INVOICES I OUTER APPLY
     (select sum(coalesce(IT.ALT_PRICE,
                          IT.PRICE * (1 - coalesce( I.DISCOUNT_PRC, 0))
                         )
                ) AS OVERALL_PRICE
      from INVOICE_ITEMS IT
      where IT.INVOICE_ID = I.ID
     ) as items;

但是它不在SQL Server中。 我不確定為什么SQL Server在外部引用上有此限制。 好像很奇怪

在這種情況下,您可以將邏輯重寫為:

select I.ID, items.OVERALL_PRICE
from INVOICES I outer apply
     (select (sum(case when IT.ALT_PRICE > 0 then IT.ALT_PRICE else 0 END) +
              sum(case when IT.ALT_PRICE = 0 OR IT.ALT_PRICE IS NULL
                       then IT.PRICE else 0
                  end) * (1 - coalesce( I.DISCOUNT_PRC, 0))
             ) AS OVERALL_PRICE
      from INVOICE_ITEMS IT
      where IT.INVOICE_ID = I.ID
     ) items;

這並不令人滿意,但是您可以使用apply

另一種方法是利用OVER() ,這將避免您使用GROUP BY

您可以這樣做:

SELECT DISTINCT
    Inv.ID,
    SUM(CASE
            WHEN items.ALT_Price IS NOT NULL AND items.ALT_Price > 0 
            THEN items.ALT_Price
            ELSE items.Price * (1 - ISNULL(DISCOUNT_PRC, 0 ))
    END) OVER(PARTITION BY Inv.ID ORDER BY Inv.ID) AS OVERALL_PRICE
FROM #Invoices Inv
JOIN #Invoices_Items items ON items.Inovice_ID = Inv.ID

然后,您可以添加列而不必將其包含在GROUP BY

您也可以將其用作子查詢:

SELECT *
FROM(
SELECT DISTINCT
    Inv.ID,
    SUM(CASE
            WHEN items.ALT_Price IS NOT NULL AND items.ALT_Price > 0 
            THEN items.ALT_Price
            ELSE items.Price * (1 - ISNULL(DISCOUNT_PRC, 0 ))
    END) OVER(PARTITION BY Inv.ID ORDER BY Inv.ID) AS OVERALL_PRICE
FROM #Invoices Inv
JOIN #Invoices_Items items ON items.Inovice_ID = Inv.ID
) D 
-- extend it with more filters, JOINs ..etc

這是我要選擇的方法(與其他方法進行性能分析):

select i.ID
     , sum(coalesce(alt_price,price*(1-isnull(discount_prc,0)))) OVERALL_PRICE
  from invoices i
  join (select id
             , invoice_id
             , price
             , case alt_price when 0 then null else alt_price end alt_price
          from invoice_items) ii
    on i.id = ii.invoice_id
 group by i.id

在此查詢中,將alt_price s零首先轉換為NULL然后在sum內使用coalesce選擇alt_price或折扣price

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM