allendks45
allendks45

Reputation: 341

SQL Subtotal and Grand Totals

Will my code work for solving the following problem? The total cost of all items ordered for all orders placed in July, 1996. Looking at 2 tables (Orders and OrderDetails) this is what I have so far.

SELECT     
   Orders.OrderID, Customers.ContactName, Orders.OrderDate
FROM       
   Customers 
INNER JOIN
   Orders ON Customers.CustomerID = Orders.CustomerID 
INNER JOIN
   [Order Details] ON Orders.OrderID = [Order Details].OrderID
WHERE     
   (Orders.OrderDate BETWEEN CONVERT(DATETIME, '1996-07-01 00:00:00', 102) AND CONVERT(DATETIME, '1996-07-31 00:00:00', 102))
   AND SUM(Quantity * UnitPrice) AS grand_total;

The intent is to find the sum of each row and keep that number then sum the rows to produce a grand total. When I run the query it definitely doesn't produce what should be.

Upvotes: 1

Views: 8751

Answers (3)

Bogdan Sahlean
Bogdan Sahlean

Reputation: 1

Try this:

SELECT     
   o.OrderID, c.ContactName, o.OrderDate, SUM(od.Quantity * od.UnitPrice) AS grand_total
FROM       
   dbo.Customers c 
INNER JOIN
   dbo.Orders o ON c.CustomerID = o.CustomerID 
INNER JOIN
   dbo.[Order Details] od ON o.OrderID = od.OrderID
WHERE     
   o.OrderDate BETWEEN {ts '1996-07-01 00:00:00.000'} AND {ts '1996-07-31 23:59:59.997'}
GROUP BY 
   GROUPING SETS ((o.OrderID, c.ContactName, o.OrderDate), ())

GROUPING SETS ((o.OrderID, c.ContactName, o.OrderDate), ...) will group the source rows by o.OrderID, c.ContactName, o.OrderDate and, also, will compute the SUM for every OrderID, ContactName, OrderDate pair of values.

GROUPING SETS ((...), ()) instructs SQL Server to compute, also, the grand total (total of all order totals):

SELECT OrderID, SUM(OrderDetailValue) AS OrderTotal
FROM (
    SELECT 11, 1, 'A', 1 UNION ALL 
    SELECT 12, 1, 'B', 10 UNION ALL 
    SELECT 13, 2, 'A', 100
) AS Orders(OrderDetailID, OrderID, ProductName, OrderDetailValue)
GROUP BY GROUPING SETS ((OrderID), ());

Results:

OrderID OrderTotal
------- ----------
1       11  <-- Total generated by GROUPING SETS ((OrderID), ...)
2       100 <-- Total generated by GROUPING SETS ((OrderID), ...)
NULL    111 <-- Total generated by GROUPING SETS (..., ())

Upvotes: 3

M.Ali
M.Ali

Reputation: 69514

;WITH CTE
AS(
SELECT     Orders.OrderID
         , Customers.ContactName
         , Orders.OrderDate
         , SUM(Quantity * UnitPrice) AS grand_total
         , rn = ROW_NUMBER() OVER (ORDER BY Orders.OrderID)
FROM       Customers INNER JOIN
           Orders ON Customers.CustomerID = Orders.CustomerID INNER JOIN
           [Order Details] ON Orders.OrderID = [Order Details].OrderID
WHERE MONTH(Orders.OrderDate) = 7 AND  YEAR(Orders.OrderDate) = 1996
GROUP BY  Orders.OrderID
         , Customers.ContactName
         , Orders.OrderDate
 )
SELECT   A.OrderID
         , A.ContactName
         , A.OrderDate
         , A.grand_total
         , (SELECT SUM(grand_total)
            FROM CTE B
            WHERE B.grand_total > A.grand_total) RunningTotal
FROM CTE A
ORDER BY RunningTotal

Result Set

╔═════════╦═════════════════════╦═════════════════════════╦═════════════╦══════════════╗
║ OrderID ║     ContactName     ║        OrderDate        ║ grand_total ║ RunningTotal ║
╠═════════╬═════════════════════╬═════════════════════════╬═════════════╬══════════════╣
║   10267 ║ Peter Franken       ║ 1996-07-29 00:00:00.000 ║ 4031.00     ║ NULL         ║
║   10252 ║ Pascale Cartrain    ║ 1996-07-09 00:00:00.000 ║ 3730.00     ║ 4031.00      ║
║   10255 ║ Michael Holz        ║ 1996-07-12 00:00:00.000 ║ 2490.50     ║ 7761.00      ║
║   10263 ║ Roland Mendel       ║ 1996-07-23 00:00:00.000 ║ 2464.80     ║ 10251.50     ║
║   10258 ║ Roland Mendel       ║ 1996-07-17 00:00:00.000 ║ 2018.60     ║ 12716.30     ║
║   10249 ║ Karin Josephs       ║ 1996-07-05 00:00:00.000 ║ 1863.40     ║ 14734.90     ║
║   10250 ║ Mario Pontes        ║ 1996-07-08 00:00:00.000 ║ 1813.00     ║ 16598.30     ║
║   10260 ║ Henriette Pfalzheim ║ 1996-07-19 00:00:00.000 ║ 1746.20     ║ 18411.30     ║
║   10253 ║ Mario Pontes        ║ 1996-07-10 00:00:00.000 ║ 1444.80     ║ 20157.50     ║
║   10265 ║ Frédérique Citeaux  ║ 1996-07-25 00:00:00.000 ║ 1176.00     ║ 21602.30     ║
║   10257 ║ Carlos Hernández    ║ 1996-07-16 00:00:00.000 ║ 1119.90     ║ 22778.30     ║
║   10268 ║ Manuel Pereira      ║ 1996-07-30 00:00:00.000 ║ 1101.20     ║ 23898.20     ║
║   10264 ║ Maria Larsson       ║ 1996-07-24 00:00:00.000 ║ 724.50      ║ 24999.40     ║
╚═════════╩═════════════════════╩═════════════════════════╩═════════════╩══════════════╝

Upvotes: 0

CRAFTY DBA
CRAFTY DBA

Reputation: 14915

There are many ways to do this.

GROUP BY ROLLUP - http://technet.microsoft.com/en-us/library/ms177673.aspx

GROUPING SETS - http://technet.microsoft.com/en-us/library/bb522495(v=SQL.105).aspx

OVER - http://technet.microsoft.com/en-us/library/ms189461.aspx

Do not use Shiva's solution since COMPUTE is defunct in SQL Server 2012!

Here are some issues that I corrected in my solution.

1 - Use table alias's
2 - Do not use between or convert on date ranges.  
    It will not be SARGABLE.

Since you were not specific, I chose a simple sum by each customers order id with a total month to-date for the customer regardless of the order id.

To solve this, I used a OVER clause. Since I did not have test data or tables from you, It is your homework to check the solution for syntax errors.

-- Customer sum by order id, total sum by customer.
SELECT 
  C.ContactName, 
  O.OrderID, 
  O.OrderDate, 
  SUM(D.Quantity * D.UnitPrice) AS OrderTotal, 
  SUM(D.Quantity * D.UnitPrice) OVER (PARTITION BY C.ContactName) as CustomerTotal
FROM Customers as c INNER JOIN Orders as O
  ON C.CustomerID = O.CustomerID 
INNER JOIN [Order Details] D 
  ON O.OrderID = D.OrderID 
WHERE 
  O.OrderDate >= '1996-07-01 00:00:00' AND 
  O.OrderDate < '1996-08-01 00:00:00'
GROUP BY
  C.ContactName,
  O.OrderID, 
  O.OrderDate 

Upvotes: 1

Related Questions