how to use multiple sum and group by in subquery? - sql

ER DIAGRAM SNAP have to Find the Order Amount Total for 2 type of orders for each year.
table is SalesOrderHeader
Subtotal + TaxAmt gives the total order amount
OnlineOrderFlag(0/1 is direct/online respectively)
1>Display the results in the below formats
OrderYear Direct Online
2001 75423 344344
2002
2003
2004
SQL:
select year(a.OrderDate),
( select SUM(SubTotal+TaxAmt) FROM SalesOrderHeader b WHERE OnlineOrderFlag = 0 group by year(b.OrderDate) ) as tot ,
( select SUM(SubTotal+TaxAmt) FROM SalesOrderHeader c WHERE OnlineOrderFlag = 1 group by year(c.OrderDate) ) as tt
FROM SalesOrderHeader a inner join SalesOrderHeader b
on b.SalesOrderID = a.SalesOrderID
inner join
SalesOrderHeader c on c.SalesOrderID = a.SalesOrderID
can someone please tel how to proceed further? i'm stuck at this
2> and also how to find it in the below format ?
OnlineFlag Year TotalAmt
Direct 2001
Direct 2002
Direct 2003
Direct 2004
Online 2001
Online 2002
Online 2003
Online 2004

This should work. Added the inner subquery to make it clear where the group by needs to go and to add the subtotal and tax for each record before summarizing.
select a.year
,SUM(Case when a.OnlineOrderFlag = 1 THEN a.total else null end) as Direct
,SUM(Case when a.OnlineOrderFlag = 0 THEN a.total else null end) as Online
FROM (select year(OrderDate) as year, (SubTotal+TaxAmt) as total, OnlineOrderFlag
from SalesOrderHeader) a
Group by a.year

SELECT YEAR(ORDERDATE), ONLINEORDERFLAG,
SUM(SUBTOTAL) SUBTOTAL,SUM(TAXAMT) TAXAMT,
SUM(SUBTOTAL + TAXAMT) TOTALDUE
FROM SALESORDERHEADER
WHERE ONLINEORDERFLAG = 1
GROUP BY YEAR(ORDERDATE) WITH ROLLUP
UNION
SELECT YEAR(ORDERDATE), ONLINEORDERFLAG,
SUM(SUBTOTAL) ,SUM(TAXAMT),
SUM(SUBTOTAL + TAXAMT)
FROM SALESORDERHEADER
WHERE ONLINEORDERFLAG = 0
GROUP BY YEAR(ORDERDATE) WITH ROLLUP
;

Related

How to get specific SQL counts for each year?

I am trying to get specific counts of bike sales per year, but now I'm only getting a total count of sales over the 5 year period and not broken up by specific years.
--CURRENT CODE:
Select datepart(year, C.TransactionDate) AS SalesYear,
SUM(COUNT(B.SerialNumber)) OVER() AS CountOfSerialNumber
FROM BIKE..Bicycle AS B
INNER JOIN BIKE..CustomerTransaction AS C on B.CustomerID = C.CustomerID
WHERE ModelType = 'Mountain' AND
TransactionDate BETWEEN '20000101' AND '20041231'
GROUP BY datepart(year, C.TransactionDate)
ORDER BY datepart(year, C.TransactionDate) ASC
--And my resulting output reads:
SalesYear | CountOfSerialNumber
2000 9431
2001 9431
2002 9431
2003 9431
2004 9431
You should be able to get the count without using SUM or OVER. OVER can be used to get a percentage of the total, if that's something you wanted to do.
SELECT DATEPART(YEAR, C.TransactionDate) AS SalesYear, COUNT(B.SerialNumber) AS Quantity,
COUNT(B.SerialNumber) * 100.0 / SUM(COUNT(B.SerialNumber)) OVER() AS Percentage
FROM BIKE..Bicycle AS B
INNER JOIN BIKE..CustomerTransaction AS C ON C.CustomerID = B.CustomerID
WHERE B.ModelType = 'Mountain' AND C.TransactionDate BETWEEN '20000101' AND '20041231'
GROUP BY DATEPART(YEAR, C.TransactionDate)
ORDER BY DATEPART(YEAR, C.TransactionDate) ASC

window function count aggregation

I have two quite complex queries going on here. Both should return per given country (Netherlands in this case) the monthly use over the total of 12 months and the percentage that makes up that month of the total of those 12 months.
But the one where i use count as a windowing function returns one more row than the one where i don't use the count as a windowing function.
The query for the left side picture is:
;WITH MonthUsage AS
(
SELECT customer_id, country_name, [Month], [Year], SUM(ItemsPerMonth)
AS ItemsPerMonth
FROM (
SELECT cs.country_name, c.customer_id, YEAR(mol.date_watched) AS Year, MONTH(mol.date_watched) AS [Month], COUNT(*) AS ItemsPerMonth
FROM Customer c
JOIN Customer_Subscription CS ON C.customer_id = CS.customer_id
JOIN Movie_Order_Line mol ON mol.customer_id = c.customer_id
WHERE (mol.date_watched BETWEEN '2017-07-01' AND '2018-07-01') AND cs.country_name = 'Nederland'
GROUP BY c.customer_id,cs.country_name, YEAR(mol.date_watched),
MONTH(mol.date_watched)
UNION ALL
SELECT cs.country_name, c.customer_id, YEAR(sol.date_watched) AS Year,
MONTH(sol.date_watched), COUNT(*) AS ItemsPerMonth
FROM Customer c
JOIN Customer_Subscription CS ON C.customer_id = CS.customer_id
JOIN Show_Order_Line sol ON sol.customer_id = c.customer_id
WHERE sol.date_watched BETWEEN '2017-07-01' AND '2018-07-01'
GROUP BY c.customer_id, cs.country_name, YEAR(sol.date_watched),
MONTH(sol.date_watched)
) AS MonthItems
WHERE country_name = 'Nederland'
GROUP BY customer_id, country_name, [Month], [Year]
),
Months(MonthNumber) AS
(
SELECT 1
UNION ALL
SELECT MonthNumber + 1
FROM months
WHERE MonthNumber < 12
)
SELECT cmb.[Year], ISNULL(cmb.[Month], m.MonthNumber) AS [Month],
ISNULL(ItemsPerMonth, 0) AS ItemsPerMonth,
ISNULL(FORMAT(((CAST(ItemsPerMonth AS decimal) / CAST((
SELECT SUM(ItemsPerMonth)
FROM MonthUsage) AS decimal
))),'P0'), '0%') AS [PercentageOfTotal]
FROM MonthUsage cmb
JOIN Customer c ON c.customer_id = cmb.customer_id
JOIN Months m ON m.MonthNumber = cmb.[Month]
ORDER BY cmb.[Year] ASC, [Month] ASC
And the query for the right picture is:
;WITH MonthUsage AS (
SELECT *
FROM (
SELECT
YEAR(date_watched) AS [Year],
MONTH(date_watched) AS [Month],
COUNT(order_id) OVER(PARTITION BY CONCAT(YEAR(date_watched), MONTH(date_watched))) AS ItemsPerMonth
FROM Movie_Order_Line mov
JOIN Customer c ON c.customer_id = mov.customer_id
JOIN Customer_Subscription cs ON C.customer_id = cs.customer_id
WHERE date_watched BETWEEN '2017-07-01' AND '2018-07-01' AND cs.country_name = 'Nederland'
UNION ALL
SELECT YEAR(date_watched) AS [Year], MONTH(date_watched) AS [Month], COUNT(order_id) OVER(PARTITION BY CONCAT(YEAR(date_watched), MONTH(date_watched))) AS ItemsPerMonth
FROM Show_Order_Line sol
JOIN Customer c ON c.customer_id = sol.customer_id
JOIN Customer_Subscription cs ON C.customer_id = cs.customer_id
WHERE date_watched BETWEEN '2017-07-01' AND '2018-07-01' AND cs.country_name = 'Nederland'
) AS Combined
GROUP BY [YEAR], [Month], ItemsPerMonth
)
SELECT *, ISNULL(FORMAT(((CAST(ItemsPerMonth AS decimal) / CAST((SELECT
SUM(ItemsPerMonth) FROM MonthUsage) AS decimal))),'P0'), '0%') AS
[PercentageOfTotal]
FROM MonthUsage
ORDER BY [Year] ASC, [Month] ASC
I can't seem to figure out why i'm getting different results. Any help is much appreciated. Thank you in advance for your time.

SQL query of sales by customer over multiple years for top 50 customers in one year

I have a query in SQL server that successfully returns the top 50 customers for a given year by sales. I want to expand it to return their sales for the additional years when they may or may not be in the top 50.
SELECT TOP 50 CU.CustomerName, SUM(ART.SalesAnalysis) AS '2011'
FROM ARTransaction AS ART, Customer AS CU
WHERE ART.CustomerN = CU.CustomerN AND ART.PostingDate BETWEEN '2010-12-31' AND '2012-01-01'
GROUP By CU.CustomerName
ORDER BY SUM(ART.SalesAnalysis) DESC
I tried adding nested SELECT statements but they return strange results and I'm not sure why (might not ever work, but the results have me flabbergasted anyway). When included the values of every row is changed and customers are duplicated.
(SELECT SUM(ART.SalesAnalysis)
WHERE ART.PostingDate BETWEEN '2011-12-31' AND '2013-01-01') AS '2012'
I tried to put a TOP statement in a nested SELECT in HAVING but that tells me
"Msg 8114, Level 16, State 5, Line 1
Error converting data type varchar to numeric."
SELECT CU.CustomerName, SUM(ART.SalesAnalysis) AS '2011'
FROM ARTransaction AS ART
JOIN Customer AS CU ON ART.CustomerN = CU.CustomerN
GROUP BY CU.CustomerNAme
HAVING CU.CustomerNAme IN
(SELECT TOP 50 CU.CustomerName
FROM ARTransaction
JOIN Customer ON ARTransaction.CustomerN = Customer.CustomerN
WHERE ARTransaction.SalesAnalysis BETWEEN '2010-12-31' AND '2012-01-01'
GROUP BY Customer.CustomerN
ORDER BY SUM(ART.SalesAnalysis) DESC)
If I understand correctly, you are looking for the top 50 sales for customers based on 2011 data - and want to see all years data for those top 50 from 2011, regardless of those customers being in the top 50 for other years.
Try this, it might need to be tweaked a bit as I don't know the schema, but if I understand the question correctly, this should do the trick.
WITH Top50 AS (
SELECT TOP 50
CU.CustomerN
,SUM(ART.SalesAnalysis) AS SalesTotal
FROM
ARTransaction art
INNER JOIN
Customer cu
ON cu.CustomerN = art.CustomerN
WHERE
ART.PostingDate BETWEEN CAST('2011-01-01' AS DATETIME)
AND CAST('2011-12-31' AS DATETIME)
GROUP BY
CU.CustomerN
ORDER BY
SUM(ART.SalesAnalysis) DESC)
SELECT
c.CustomerName
,SUM(a.SalesAnalysis) AS TotalSales
,YEAR(a.PostingDate) AS PostingDateYear
FROM
ARTransaction a
INNER JOIN
Customer c
ON c.CustomerN = a.CustomerN
INNER JOIN
Top50 t
ON t.CustomerN = a.CustomerN
GROUP BY
c.CustomerName
,YEAR(a.PostingDate)
ORDER BY
PostingDateYear
you could use something like below... you will be looking at the all the data for all the years you want to look at and then just getting the top 50 for the 2011 year
SELECT TOP 50
CU.CustomerName,
SUM(case when year(ART.PostingDate) = 2011 -- or you could use case when ART.PostingDate BETWEEN '2011-01-01' AND '2011-12-31'
then ART.SalesAnalysis
else 0 end) AS 2011,
SUM(case when year(ART.PostingDate) = 2012
then ART.SalesAnalysis
else 0 end) AS 2012
FROM
ARTransaction ART,
inner join Customer CU
on ART.CustomerN = CU.CustomerN
WHERE ART.PostingDate BETWEEN '2011-01-01 AND '2012-12-31'
GROUP By CU.CustomerName
ORDER BY
SUM(case when year(ART.PostingDate) = 2011
then ART.SalesAnalysis
else 0 end) DESC

SQL - Join multiple table

I have four tables Customer, Sales, Invoice, and Receipt.
Customer
ID Name
1 A
Sales
ID Name
1 Ben
Invoice
ID Amt Date CustomerID SalesID
1 12 1/9/2014 1 1
2 10 1/10/2014 1 1
3 20 2/10/2014 1 1
4 30 3/10/2014 1 1
Receipt
ID Amt Date CustomerID SalesID
1 10 4/10/2014 1 1
I wish to join those 4 table as below with sum up the Ammount(s), but I am stuck as to how I can achieve my desired
RESULT
CustomerID SalesID Inv_Amt Rep_Amt Month
1 1 12 0 9
1 1 60 10 10
I've been stuck for days. But, I have no idea how to proceed.
You can get month wise total receipt and invoice amount by grouping and sub query like below :
SELECT Invoice.CustomerID [CustomerID],
Invoice.SalesID [SalesID],
SUM(Invoice.Amt) [Invoice_Amt],
ISNULL((SELECT SUM(Amt)
FROM Receipt
WHERE CustomerID = Invoice.CustomerID
AND SalesID = Invoice.SalesID
AND Month(Date) = Month(Invoice.Date)),0) [Receipt_Amt],
MONTH(Invoice.Date) Month
FROM Invoice
GROUP BY Invoice.CustomerID, Invoice.SalesID, MONTH(Invoice.Date)
SQL Fiddle Demo1
Warning : Here data will come for all months which are in Invoice table. If for any month, there is no any data in invoice table then no result will come for that month even for receipt also.
UPDATE:
To get result from all months of invoice and receipt table, you need to get it using CTE as like below :
;with CTE as
(
SELECT Invoice.CustomerID, Invoice.SalesID, MONTH(Invoice.Date) MonthNo FROM Invoice
UNION
SELECT Receipt.CustomerID, Receipt.SalesID, MONTH(Receipt.Date) MonthNo FROM Receipt
)
SELECT CTE.CustomerID [CustomerID],
CTE.SalesID [SalesID],
ISNULL((SELECT SUM(Amt)
FROM Invoice
WHERE CustomerID = CTE.CustomerID
AND SalesID = CTE.SalesID
AND Month(Date) = CTE.MonthNo),0) [Invoice_Amt],
ISNULL((SELECT SUM(Amt)
FROM Receipt
WHERE CustomerID = CTE.CustomerID
AND SalesID = CTE.SalesID
AND Month(Date) = CTE.MonthNo),0) [Receipt_Amt],
MonthNo
FROM CTE
SQL Fiddle Demo2
Frankly, since you're just selecting customer and sales IDs (as opposed to names), you don't even need to joint all four tables:
SELECT i.CustomerID,
i.SalesID,
SUM(i.Amt) AS InvAmt,
SUM(r.Amt) AS RepAmt,
MONTH(i.`Date`) AS `Month`
FROM Invoice i
JOIN Receipt r ON i.CustomerID = r.CustomerID AND
i.SalesID = r.SalesID AND
MONTH(i.`Date`) = MONTH(r.`Date`)
GROUP BY i.CustomerID, i.SalesID, MONTH(i.`Date`) AS `Month`
Looks like a homework, but ...
SELECT
Customer.ID AS CustomerID,
Sales.ID AS SalesID,
Invoice.Amt AS Inv_Amt,
Receipt.Amt AS Rep_Amt,
MONTH(Invoice.Date) AS Month
FROM
Customer
INNER JOIN Receipt ON Customer.ID = Receipt.CustomerID
INNER JOIN Invoice ON Customer.ID = Invoice.CustomerID
INNER JOIN Sales ON Sales.ID = Receipt.SalesID
I didn't bother checking the result is what you expect, but the query should be something like that. You can play with the join conditions in order to get the result.

Repeat Customers Each Year (Retention)

I've been working on this and I don't think I'm doing it right. |D
Our database doesn't keep track of how many customers we retain so we looked for an alternate method. It's outlined in this article. It suggests you have this table to fill in:
Year Number of Customers Number of customers Retained in 2009 Percent (%) Retained in 2009 Number of customers Retained in 2010 Percent (%) Retained in 2010 ....
2008
2009
2010
2011
2012
Total
The table would go out to 2012 in the headers. I'm just saving space.
It tells you to find the total number of customers you had in your starting year. To do this, I used this query since our starting year is 2008:
select YEAR(OrderDate) as 'Year', COUNT(distinct(billemail)) as Customers
from dbo.tblOrder
where OrderDate >= '2008-01-01' and OrderDate <= '2008-12-31'
group by YEAR(OrderDate)
At the moment we just differentiate our customers by email address.
Then you have to search for the same names of customers who purchased again in later years (ours are 2009, 10, 11, and 12).
I came up with this. It should find people who purchased in both 2008 and 2009.
SELECT YEAR(OrderDate) as 'Year',COUNT(distinct(billemail)) as Customers
FROM dbo.tblOrder o with (nolock)
WHERE o.BillEmail IN (SELECT DISTINCT o1.BillEmail
FROM dbo.tblOrder o1 with (nolock)
WHERE o1.OrderDate BETWEEN '2008-1-1' AND '2009-1-1')
AND o.BillEmail IN (SELECT DISTINCT o2.BillEmail
FROM dbo.tblOrder o2 with (nolock)
WHERE o2.OrderDate BETWEEN '2009-1-1' AND '2010-1-1')
--AND o.OrderDate BETWEEN '2008-1-1' AND '2013-1-1'
AND o.BillEmail NOT LIKE '%#halloweencostumes.com'
AND o.BillEmail NOT LIKE ''
GROUP BY YEAR(OrderDate)
So I'm just finding the customers who purchased in both those years. And then I'm doing an independent query to find those who purchased in 2008 and 2010, then 08 and 11, and then 08 and 12. This one finds 2008 and 2010 purchasers:
SELECT YEAR(OrderDate) as 'Year',COUNT(distinct(billemail)) as Customers
FROM dbo.tblOrder o with (nolock)
WHERE o.BillEmail IN (SELECT DISTINCT o1.BillEmail
FROM dbo.tblOrder o1 with (nolock)
WHERE o1.OrderDate BETWEEN '2008-1-1' AND '2009-1-1')
AND o.BillEmail IN (SELECT DISTINCT o2.BillEmail
FROM dbo.tblOrder o2 with (nolock)
WHERE o2.OrderDate BETWEEN '2010-1-1' AND '2011-1-1')
--AND o.OrderDate BETWEEN '2008-1-1' AND '2013-1-1'
AND o.BillEmail NOT LIKE '%#halloweencostumes.com'
AND o.BillEmail NOT LIKE ''
GROUP BY YEAR(OrderDate)
So you see I have a different query for each year comparison. They're all unrelated. So in the end I'm just finding people who bought in 2008 and 2009, and then a potentially different group that bought in 2008 and 2010, and so on. For this to be accurate, do I have to use the same grouping of 2008 buyers each time? So they bought in 2009 and 2010 and 2011, and 2012?
This is where I'm worried and not sure how to proceed or even find such data.
Any advice would be appreciated! Thanks!
How about a cross-tab on a per customer basis to help you out...
From this, you can start to analyze a bit more in bulk by comparing a customer's
current year to the previous and have a total customers count for each respective year.
From that you can run whatever percentages you want in your final output
This should get you a whole set of all years in question, and you can just keep adding years as need be for comparison. It should be very quick, especially if you have an index on ( BillEMail, OrderDate ).
The premise is that the inner query just blows through all the records, and on a customer basis sets a flag of 1 if there are ANY orders within the given year (via MAX()). It does it via case/when so each year is detected for a customer. Once that has been determined, the outer query then rolls those up comparing each customer with if they had a sale in one year vs the prior, if so, SUM() 1 vs 0 and you have your counts of retention.
SELECT
SUM( case when PreQry.C2011 = 1 and PreQry.C2012 = 1 then 1 else 0 end ) as Retain2011_2012,
SUM( case when PreQry.C2010 = 1 and PreQry.C2011 = 1 then 1 else 0 end ) as Retain2010_2011,
SUM( case when PreQry.C2009 = 1 and PreQry.C2010 = 1 then 1 else 0 end ) as Retain2009_2010,
SUM( case when PreQry.C2008 = 1 and PreQry.C2009 = 1 then 1 else 0 end ) as Retain2008_2009,
SUM( PreQry.C2012 ) CustCount2012,
SUM( PreQry.C2011 ) CustCount2011,
SUM( PreQry.C2010 ) CustCount2010,
SUM( PreQry.C2009 ) CustCount2009,
SUM( PreQry.C2008 ) CustCount2008
from
( select
O.BillEMail as customer,
MAX( CASE when YEAR( O.OrderDate ) = 2012 then 1 else 0 end ) as C2012,
MAX( CASE when YEAR( O.OrderDate ) = 2011 then 1 else 0 end ) as C2011,
MAX( CASE when YEAR( O.OrderDate ) = 2010 then 1 else 0 end ) as C2010,
MAX( CASE when YEAR( O.OrderDate ) = 2009 then 1 else 0 end ) as C2009,
MAX( CASE when YEAR( O.OrderDate ) = 2008 then 1 else 0 end ) as C2008
from
dbo.tblOrder O
where
O.OrderDate >= '2008-01-01'
AND O.BillEmail NOT LIKE '%#halloweencostumes.com'
AND O.BillEmail NOT LIKE ''
group by
O.BillEMail ) as PreQry
Now, if you wanted to detect how many were "NEW" for a given year, you could just add additional columns such as testing the previous year sale flag = 0 vs current year = 1 such as
SUM( case when PreQry.C2011 = 0 and PreQry.C2012 = 1 then 1 else 0 end ) as NewIn2012,
SUM( case when PreQry.C2010 = 0 and PreQry.C2011 = 1 then 1 else 0 end ) as NewIn2011,
SUM( case when PreQry.C2009 = 0 and PreQry.C2010 = 1 then 1 else 0 end ) as NewIn2010,
SUM( case when PreQry.C2008 = 0 and PreQry.C2009 = 1 then 1 else 0 end ) as NewIn2009
If I understand your problem right, then you sounds like you've gotten mixed up in the details. It depends on what you want your definition of 'retain' to be. How about 'also bought in some previous year'? Then, for year X, a customer is retained if they also bought from you in a previous year.
For 2012, for example:
SELECT YEAR(OrderDate) as 'Year',COUNT(distinct(billemail)) as Customers
FROM dbo.tblOrder o with (nolock)
WHERE o.BillEmail IN (SELECT DISTINCT o1.BillEmail
FROM dbo.tblOrder o1 with (nolock)
WHERE o1.OrderDate BETWEEN '2012-1-1' AND '2013-1-1')
AND o.BillEmail IN (SELECT DISTINCT o2.BillEmail
FROM dbo.tblOrder o2 with (nolock)
WHERE o2.OrderDate < '2012-1-1')
AND o.BillEmail NOT LIKE '%#halloweencostumes.com'
AND o.BillEmail NOT LIKE ''
GROUP BY YEAR(OrderDate)
Does this work?
Edit
You can take this a step farther and abstract out the year so 1 query will suffice:
SELECT YEAR(O.OrderDate) as 'Year',COUNT(distinct(billemail)) as Customers
FROM dbo.tblOrder o with (nolock)
WHERE o.BillEmail IN (SELECT DISTINCT o1.BillEmail
FROM dbo.tblOrder o1 with (nolock)
WHERE year(o1.OrderDate)=YEAR(O.OrderDate)
AND o.BillEmail IN (SELECT DISTINCT o2.BillEmail
FROM dbo.tblOrder o2 with (nolock)
WHERE year(o2.OrderDate) < year(o.orderdate)
AND o.BillEmail NOT LIKE '%#halloweencostumes.com'
AND o.BillEmail NOT LIKE ''
GROUP BY YEAR(OrderDate)
This should give you, for each year in which you had orders, the count of distinct customers, and the count of customers who also purchased in a previous year. However, it's not in the same format as the table you want to populate.