The following SQL Puzzle is found in the book APRESS: Advanced Transact –SQL for SQL Server 2000 in page no 71 of Chapter 2.
You are provided with these tables: Orders, OrderDetails, and OrderPayments. Each order in the Orders table can have one or more order parts in the OrderDetails table and zero or more payments in the OrderPayments table. Your task is to write a query that produces order information along with the sum of its order parts and order payments.
The Orders table will look like this:
Ordered custid odate
1 1001 2001−01−18 00:00:00.000
2 1002 2001−02−12 00:00:00.000
The OrderDetails table will look like this:
orderid partno qty
1 101 5
1 102 10
2 101 8
2 102 2
The OrderPayments table will look like this:
orderid paymentno value
1 1 75
1 2 75
2 1 50
2 2 50
The expected output is shown in the following table:
orderid custid odate sum_of_qty sum_of_value
1 1001 2001−01−18 00:00:00.000 15 150
2 1002 2001−02−12 00:00:00.000 10 100
How can you solve this?
To solve this problem you need to write a query with two derived table in SQL Server 2000.
Derived tables are Virtual Tables in SQL Server that are similar to views but differs from views by not storing the definition of it in the Metadata. The Derived Table is a result set of a Query that is used in the FROM Clause of an outer Query and it will be no longer available after the execution outer query. Derived Tables can be nested but may not be co-related.
Let’s go to the solution.
Query listing:1
Select OQ.OrderId,OQ.CustId,OQ.ODate , QtySum,Paysum
From (
Select Orders.OrderId, Orders.CustId,Orders.Odate,
Sum(OD.QTY) as QtySum
From Orders Join OrderDetails OD
On Orders.OrderID = OD.OrderId
Group By Orders.OrderId,Orders.CustId, Orders.Odate
) OQ
Join
( Select O1.OrderID, Sum(op.[value]) as PaySum
from Orders O1
Join OrderPayments OP
on O1.OrderId = Op.Orderid
Group By O1.OrderId ) as OP1
On OQ.OrderID = OP1.OrderId
This query is gives you the desired solution (which is also available in that book at page no 494)
As this book is dealing SQL Server 2000 T-SQL, the author used this derived table way.
Using Derived Table for complex queries having some disadvantages. It reduces the readability of the query and as well as the debugging complex queries (using derived tables) is a big job.
SQL Server 2005 offers a novel way to handle these kinds of problems. Using CTE (Common Table Expressions) you can improve readability, and easily maintain a complex query by building such queries in several blocks.
CTE offers you something that derive tables doesn’t, are, Derived Tables are not referred multiple time in a query, and CTE can be used as recursive queries.
First I show you the CTE way solution to that problem
Query listing:2
With QTYSum as
(Select Ord.OrderId,Sum(OD.Qty) as PartQtySum
From Orders Ord
Join OrderDetails OD
on OD.OrderId = Ord.OrderId
Group By Ord.OrderId
), --Block 1
PaySum As
(Select O.OrderId,Sum([Value]) as PaySum
From Orders O
Join OrderPayments OP
On O.OrderId = OP.OrderId
Group By O.OrderId
) -- Block 2
Select Orders.OrderID, Orders.CustId, Orders.Odate, PartQtySum, PaySum
From Orders
Join Paysum on Orders.OrderId = Paysum.Orderid
Join QTYSum on Orders.ORderId = QTYSum.OrderID
Here, we used the same lines of code, but separating its appearance, which gives us better readability. The OQ alias Select Query Statement in the Query listing:1 was replaced by the Block 1 Query of the Query listing:2 and the OP1 alias Select Query Statement in the Query listing:1 was replaced by the Block 2 Query of the Query listing:2
But this CTE is not used multiple references and recursive queries. Let us see some other example for these options.
Suppose you want to compare each year’s Sales of a particular Employee with his or her previous year’s sales, the CTE can be used as follows.(NorthWind Database’s Orders Table is used for illustration)
With YearToDateSales as
( Select EmployeeId,Year(OrderDate) AS OrderYear,
Count(*) as [Sales]
From dbo.Orders
Where EmployeeId=1
Group by EmployeeId,Year(OrderDate)
)
Select Cur.OrderYear,Cur.Sales , Prv.Sales
From YearToDateSales as Cur
Left Outer Join YearToDateSales Prv
on Prv.OrderYear = Cur.OrderYear -1
In this example, a CTE named ‘YearToDateSales‘ was referred two times in the outer query with two different aliases(Cur, Prv). The Employee ID is hard-coded, can be replaced with an argument. Add the following two lines before the With caluse of the CTE.
Declare @empId as int;
set @empID=1;
And replace the ‘Where EmployeeId=1’ with ‘Where EmployeeId=@empID’.
Suppose a CTE is used in batch like the above argumented CTE, the previous statement of With should be terminated by semicolon.
Now for a recursive CTE, consider a query that has to fetch the hierarchy of organisation using its Employee table.
WITH DirectReports(ManagerID, EmployeeID, EmployeeLevel) AS
(
SELECT ManagerID, EmployeeID, 0 AS EmployeeLevel
FROM HumanResources.Employee
WHERE ManagerID IS NULL
UNION ALL
SELECT e.ManagerID, e.EmployeeID, EmployeeLevel + 1
FROM HumanResources.Employee e
INNER JOIN DirectReports d -- This line is calling the CTE
itself recursively
ON e.ManagerID = d.EmployeeID
)
SELECT ManagerID, EmployeeID, EmployeeLevel
FROM DirectReports
WHERE EmployeeLevel <= 2 ;
For further Studies
http://msdn2.microsoft.com/en-us/library/ms175972.aspx
http://www.4guysfromrolla.com/ASPScripts/PrintPage.asp?REF=%2Fwebtech%2F071906-1.shtml
1 comment:
great site, keep it up the good work.
Post a Comment