嚴格來說,Having並不需要一個子表,但沒有子表的Having並沒有實際意義。如果你只需要一個表,那麼你可以用Where子句達到一切目的。為進行實踐,Having預先假定至少兩個表和一個基於第二個表的合計函數。
下面是一個簡單的例子:你想要訂單總數超過25000美元的客戶清單。你需要適當串連的三個表:Customer、SalesOrderHeader和SalesOrderDetail。然後,你求Detail的和,並將總數與25000美元進行比較。查看列表A。
查看列表A中的代碼時,有一件事情並不明顯,即LineTotal列被計算。你可以像求實際的列的和一樣求出已計算資料行的和。但是,你不能在同一個操作中執行兩個不同層次的合計函數。
SELECT Sales.Customer.CustomerID, Sales.SalesOrderHeader.SalesOrderID, SUM(Sales.SalesOrderDetail.LineTotal) AS SubTotalFROM Sales.Customer INNER JOIN Sales.SalesOrderHeader ON Sales.SalesOrderHeader.CustomerID = Sales.Customer.CustomerID INNER JOIN Sales.SalesOrderDetail ON Sales.SalesOrderDetail.SalesOrderID = Sales.SalesOrderHeader.SalesOrderIDGROUP BY Sales.Customer.CustomerID, Sales.SalesOrderHeader.SalesOrderIDHAVING SUM(LineTotal) > 25000.00ORDER BY Sales.Customer.CustomerID, SalesOrderID ;
列表A:
假設你希望知道所有客戶的平均銷售額。你可以使用列表B中的代碼,它返回如下錯誤資訊:
SELECT Sales.SalesOrderHeader.SalesOrderID, AVG(SUM(Sales.SalesOrderDetail.LineTotal)) AS AverageFROM Sales.SalesOrderHeader INNER JOIN Sales.SalesOrderDetail ON Sales.SalesOrderDetail.SalesOrderID = Sales.SalesOrderHeader.SalesOrderIDGROUP BY Sales.SalesOrderHeader.SalesOrderID
列表B:
Msg 130, Level 15, State 1, Line 1Cannot perform an aggregate function on an expression containing an aggregate or a subquery.
錯誤資訊:
你可以將平均值的計算過程進行分解來解決這個問題。你可以將第一部分(SUM)編寫成一個表值UDF,如列表C所示。你可以根據列表D中的Function Compute平均值。列表E說明你如何能夠進行組合。
USE [AdventureWorks]GO/****** Object: UserDefinedFunction [dbo].[SalesTotals_fnt] Script Date: 12/09/2006 11:32:54 ******/SET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE FUNCTION [dbo].[SalesTotals_fnt]()RETURNS TABLEAS RETURN (SELECT SalesOrderID, SUM(LineTotal)AS TotalSale FROM Sales.SalesOrderDetail GROUP BY Sales.SalesOrderDetail.SalesOrderID)
列表C:
CREATE FUNCTION [dbo].[AverageSale_fns] ( -- Add the parameters for the function here -- )RETURNS moneyASBEGIN -- Declare the return variable here DECLARE @Result money-- Add the T-SQL statements to compute the return value here SET @Result =(SELECTAvg(TotalSale)AS AverageSale FROM dbo.SalesTotals_fnt())-- Return the result of the function RETURN @ResultEND
列表D:
DECLARE @Avg moneySELECT @Avg = dbo.AverageSale_fns()SELECT *, @Avg as Average, TotalSale / @Avg as Ratio, CASE WHEN TotalSale / @Avg > 1 THEN 'Above Average' WHEN TotalSale / @Avg < 1 THEN 'Below Average' ELSE 'Average' ENDFROM dbo.SalesTotals_fnt()
列表E:
現在你知道了如何根據一個子表使用Having子句來測試合計值。當你需要在一個查詢中使用兩個不同的合計函數時,最好是把它們分解成單獨的函數,然後再進行組合(如前一個例子所說明的)。