MS SQL | How to query a filtered column (WHERE) with non filtered data - sql

I have a problem solving an MS SQL query.
in summary, the query should get the date column as two columns, year and month, the count of other columns, the sum of total of a column, and a filtered sum column.
what I struggled with was adding the filtered sum column.
a sample data, Test:
customerID, 1,2,3,4...
InvoiceID, 1234551, 1234552...
ProductID, A, B, C...
Date, Datetime
Income, int
customerID
InvoiceID
ProductID
Date
Income
1
1234551
A
01/01/2015
300
2
1234552
B
02/01/2016
300
I have a solution, but I am sure there is a more simple solution.
WITH CTE_1 AS
(
SELECT Date,
COUNT(DISTINCT Test.customerID) AS customers,
COUNT(Test.InvoiceID) AS Invoices,
COUNT(Test.ProductID) AS Products,
Sum(Income) AS Total_Income,
ISNULL((SELECT Sum(Income) AS Income_A FROM Test ts WHERE ProductID = 'A' AND ts.Date = Test.Date),0) AS Total_Income_A
FROM Test
GROUP BY Test.Date
)
SELECT YEAR(Date) AS Year,
MONTH(Date) AS Month,
Sum(customers) AS customers,
Sum(Invoices) AS Invoices,
Sum(Products) AS Products,
Sum(Total_Income) AS Total_Income,
Sum(Total_Income_A) AS Total_Income_A
FROM CTE_1
GROUP BY YEAR(Date), MONTH(Date)
ORDER BY YEAR(Date), MONTH(Date)
to produce:
Year, 2015, 2016...
Month, 1, 2, ...
customers, int
Invoices, int
Products, int
Total_Income, int
Total_Income_A, int
Year
Month
customers
Invoices
Products
Total_Income
Total_Income_A
2015
1
3
4
4
1600
600
2015
2
1
1
1
1200
0
Thanks!
Nir

You can directly apply a Conditional Aggregation such as
SELECT YEAR(Date) AS Year,
MONTH(Date) AS Month,
COUNT(DISTINCT customerID) AS customers,
COUNT(DISTINCT InvoiceID) AS Invoices,
COUNT(ProductID) AS Products,
SUM(Income) AS Total_Income,
ISNULL(SUM(CASE WHEN ProductID = 'A' THEN Income END),0) AS Total_Income_A
FROM Test
GROUP BY YEAR(Date), MONTH(Date)
ORDER BY YEAR(Date), MONTH(Date)
Demo

Related

Number of Customer Purchases in Their First Month

I have a list of customer orders. I can easily calculate the month and year of first purchase for each customer (e.g. customer 1 had their first purchase in Sept 2021, customer 2 had their first purchase in Oct 2021, etc.). What I want to add is an additional column that counts the number of purchases a customer made in their first month.
Existing data table (Orders):
OrderId
CustomerId
OrderDate
1
1
9/15/2021
2
1
10/15/2021
3
1
11/1/2021
4
2
10/1/2021
5
2
10/6/2021
6
2
10/7/2021
7
2
11/9/2021
8
3
11/15/2021
Desired output:
CustomerId
FirstOrderMonth
FirstOrderYear
FirstMonthPurchaseCount
1
9
2021
1
2
10
2021
3
3
11
2021
1
I was thinking something like this for the first three columns:
SELECT o.CustomerId,
MONTH(MIN(o.OrderDate)) as FirstOrderMonth,
YEAR(MIN(o.OrderDate)) as FirstOrderYear
FROM Orders o
GROUP BY o.CustomerId
I am not sure how to approach the final column and was hoping for some help.
Aggregate by the customer's id, the year and the month of the order and use window functions to get the year and month of the 1st order and the count of that 1st month:
SELECT DISTINCT CustomerId,
FIRST_VALUE(MONTH(OrderDate)) OVER (PARTITION BY CustomerId ORDER BY YEAR(OrderDate), MONTH(OrderDate)) FirstOrderMonth,
MIN(YEAR(OrderDate)) OVER (PARTITION BY CustomerId) FirstOrderYear,
FIRST_VALUE(COUNT(*)) OVER (PARTITION BY CustomerId ORDER BY YEAR(OrderDate), MONTH(OrderDate)) FirstMonthPurchaseCount
FROM Orders
GROUP BY CustomerId, YEAR(OrderDate), MONTH(OrderDate);
See the demo.
You may use the RANK() function to identify the first month purchases for each user as the following:
Select D.CustomerId, MONTH(OrderDate) FirstOrderMonth,
YEAR(OrderDate) FirstOrderYear, COUNT(*) FirstMonthPurchaseCount
From
(
Select *, RANK() Over (Partition By CustomerId Order By YEAR(OrderDate), MONTH(OrderDate)) rnk
From table_name
) D
Where D.rnk = 1
Group By D.CustomerId, MONTH(OrderDate), YEAR(OrderDate)
See a demo.
If you want to find second, third ... month purchases, you may use the DENSE_RANK() function instead of RANK() and change the value in the where clause to the required month order.
select CustomerId
,min(month(OrderDate)) as FirstOrderMonth
,min(year(OrderDate)) as FirstOrderYear
,count(first_month_flag) as FirstMonthPurchaseCount
from (select *
,case when month(OrderDate) = month(min(OrderDate) over(partition by CustomerId)) then 1 end as first_month_flag
from Orders) Orders
group by CustomerId
CustomerId
FirstOrderMonth
FirstOrderYear
FirstMonthPurchaseCount
1
9
2021
1
2
10
2021
3
3
11
2021
1
Fiddle

SQL to identify customers who placed more than X orders in a given year

Here's the table I am working with:
customer_id order_id order_date
101 1 2016-12-11
102 2 2016-12-13
101 3 2017-12-14
103 4 2017-12-15
... ... ...
I need a SQL to find out how many customers made more than X purchases in 2016 and 2017.
I've gotten the proper answer for it being customer 101, with this code:
select
customer_id
from
(
select
year(order_date) as order_date_year,
customer_id,
count(*) as number_of_orders
from
cust_orders
group by
year(order_date),
customer_id
having
count(*) >= 3
) as t
group by
order_date_year,
customer_id
But this doesn't solve for specific years being more than X.
You need 2 levels of aggregation:
select c.customer_id
from (
select customer_id
from cust_orders
where year(order_date) in (2016, 2017)
group by customer_id, year(order_date)
having count(*) >= 10
) c
group by c.customer_id
having count(*) = 2;
Replace 10 with the number of purchases.
Change 2 to the number of years that you want to search for.
See the demo.
You can use aggregation with a having clause to get the counts by year and customer. Then aggregate again and count the years:
select customer_id
from (select customer_id, year(order_date) as year
from cust_orders co
group by customer_id, year(order_date)
having count(*) >= X
) x
where year in (2016, 2017)
group by customer_id
having count(*) = 1;

Group By Count and Total Count

I am trying to figure out how can I get following result by query but unable to do the same.
Table Name: ProductMaster
ProductCode Quantity EntryDate
A1 10 10/03/2015
A1 10 15/03/2015
A2 10 18/03/2015
A2 10 25/03/2015
A1 10 10/04/2015
A2 10 15/04/2015
I want to get result as
If I select March month, result should be as:
ProductCode MonthCount TotalCount
A1 20 30
A2 20 30
If I select April month, result should be as:
ProductCode MonthCount TotalCount
A1 10 30
A2 10 30
My Query:
SELECT ProductCode, SUM(Quantity)
FROM ProductMaster
WHERE DATEPART(MONTH, EntryDate) = #Month
GROUP BY ProductCode
Where #month = 3 or 4, based on input.
Additionally, How can i get the count of productcode.
For Month = 3
ProductCode MonthCount TotalCount
A1 2 3
A2 2 3
You could use a case expression based on #Month:
SELECT ProductCode,
SUM (CASE WHEN MONTH(EntryDate) = #Month THEN Quantity ELSE 0 END)
AS MonthCount,
SUM (Quantity) AS TotalConount
FROM ProductMaster
GROUP BY ProductCode
EDIT:
To answer the edited question, you can use the same technique with count instead of sum:
SELECT ProductCode,
COUNT (CASE WHEN MONTH(EntryDate) = #Month THEN Quantity ELSE NULL END)
AS MonthCount,
COUNT (*) AS TotalConount
FROM ProductMaster
GROUP BY ProductCode
Here is a solution using a Common Table Expression (CTE). This query will get the results for all months in the same resultset. If you only need one month you can add a WHERE clause and filter on MonthNumber for just the month you desire.
;WITH cteProductMasterByMonth AS
(
SELECT ProductCode, DATEPART(MONTH, EntryDate) as MonthNumber, SUM(Quantity) as MonthCount
FROM #ProductMaster
GROUP BY ProductCode, DATEPART(MONTH, EntryDate)
)
SELECT ProductCode, MonthCount, SUM(MonthCount) OVER(PARTITION BY ProductCode) AS TotalCount
FROM cteProductMasterByMonth
ORDER BY MonthNumber, ProductCode
I tested this on SqlFiddle
Note: If the EntryDate values spans multiple years and you want to the results for each month-year pair, then you will need to add an additional condition to in the CTE query to group by DATEPART(YEAR, EntryDate) as well and maybe a YearNumber field similar to the MonthNumber field in the select clause.

SQL Query: Find highest revenue month/year for a customer

I'm looking to query the database to find highest revenue month for all the customers in the system. I have got the query working to pull customers monthly revenue from all the years for which the data is present. But I'm struggling to figure out how to get highest revenue month-year from this data.
The database is SQL Server 2008 R2.
The columns are: Customer name, Year, Month, and Revenue.
I even tried using Row_Number() and tried partitioning by customer name/year and ordering by revenue. But it didn't work. Maybe I'm making some mistake there.
Here's how I tried to build the base query.
Select Customer, Year(orderdatetime) as Year, Month(orderdatetime) as Month, SUM(Revenue)
From Orders
Group By Customer, Year(orderdatetime), Month(orderdatetime)
This is how I tried to use Row_Number()
WITH Max_Revenue AS
(
Select Customer, Year(orderdatetime) as Year, Month(orderdatetime) as Month, SUM(Revenue), RowNumber = ROW_NUMBER() OVER(PARTITION By Year Order By Revenue DESC)
From Orders
Group By Customer, Year(orderdatetime), Month(orderdatetime)
)
Select Max_Revenue.Customer, Max_Revenue.Year, Max_Revenue.Month, Max_Revenue.Revenue
From Max_Revenue
Where Max_Revenue.RowNumber = 1
Order By Max_Revenue.Customer asc
The data I get back is like:
Customer Month Year Revenue
ABC 2 2012 100
ABC 3 2013 150
ABC 5 2012 200
XYZ 4 2011 500
XYZ 6 2012 650
XYZ 7 2012 800
What I want as the output is
Customer Month Year Revenue
ABC 5 2012 200
XYZ 7 2012 800
So every customer's best month and respective year in terms of revenue.
SELECT Customer,
Year,
Revenue,
Month
FROM (
SELECT Customer,
Year,
ROW_NUMBER() OVER(PARTITION By Customer Order By Revenue DESC) as rank,
Revenue,
Month
FROM (
Select Customer,
Year(orderdatetime) as Year,
Month(orderdatetime) as Month,
SUM(Revenue) as Revenue
From Orders
Group By
Customer,
Year(orderdatetime),
Month(orderdatetime)
) BS
GROUP BY Customer,
Year,
Month) BS2
WHERE BS2.rank = 1
OR change = ROW_NUMBER() OVER(PARTITION By Year Order By Revenue DESC to
= ROW_NUMBER() OVER(PARTITION By Customer Order By Revenue DESC

Oracle SQL Query:Find out which year total sales amount is maximum

my working table, Table name: sales
Here Is MY TABLE, [sl_no is primary key] table structure:
CREATE TABLE SALES
( SL_NO NUMBER PRIMARY KEY, REGION VARCHAR2(10) NOT NULL,
MONTH VARCHAR2(20) NOT NULL, YEAR NUMBER NOT NULL,
SALES_AMOUNT NUMBER NOT NULL )
and here is table data:
SQL> select * from sales;
SL_NO REGION MONTH YEAR SALES_AMOUNT
---------- ---------- -------------------- ---------- ------------
1 east december 2011 750000
2 east august 2011 800000
3 west january 2012 640000
5 east march 2012 1200000
6 west february 2011 580000
4 west april 2011 555000
6 rows selected.
I have tried this query to view total sales amount of those[2011,2012] year;
SELECT year, SUM(sales_amount) FROM sales GROUP BY year;
YEAR SUM(SALES_AMOUNT)
---------- -----------------
2011 2685000
2012 1840000
MY GOAL:> I want to find out the year of maximum sales amount.
I tried this,and work perfectly...but when i want to display that year also, it gives an Error.
SQL> select max(sum(sales_amount)) from sales group by year;
MAX(SUM(SALES_AMOUNT))
----------------------
2685000
SQL> select year, max(sum(sales_amount)) from sales group by year;
select year, max(sum(sales_amount)) from sales group by year
*
ERROR at line 1:
ORA-00937: not a single-group group function
Extra addition: if multiple rows have same value means....when sales amount of both year[2011,2012] remain same, Then....
plZ help me to Solve this problem.
This should work.
with yr_agg as (
select year, sum(sales_amount) as total
from sales
group by year
)
select year, total as max_total
from yr_agg
where total = (select max(total)
from yr_agg);
I think the simplest way is to order the results and take the first row:
select year, sales_amount
from (SELECT year, SUM(sales_amount) as sales_amount
FROM sales
GROUP BY year
order by sum(sales_amount) desc
) t
where rownum = 1;
EDIT:
If you need to display all the matching rows (which isn't mentioned in the question), I would suggest using the dense_rank() analytic function:
select year, sales_amount
from (SELECT year, SUM(sales_amount) as sales_amount,
dense_rank(over order by SUM(sales_amount) desc) as seqnum
FROM sales
GROUP BY year
order by sum(sales_amount) desc
) t
where seqnum = 1;
Or, you might like the max() version instead:
select year, sales_amount
from (SELECT year, SUM(sales_amount) as sales_amount,
max(sum(sales_amount)) over () as maxsa
FROM sales
GROUP BY year
order by sum(sales_amount) desc
) t
where sales_amount = maxsa;
Following select should do what you need (untested, do not have Oracle at home):
select year, total
from (
select year, sum(sales_amount) total
from sales
group by year
)
where total = (select max(total_amount)
from (
select year, sum(sales_amount) total_amount
from sales
group by year
))
Take in account, though, that it might give you different years in each execution if two of them have exactly the same total amount. You might want to include some more conditions to avoid this.
Here is my Query where multiple row can select
SELECT year,MAX(total_sale) as max_total
FROM
(SELECT year,SUM(sales_amount) AS total_sale FROM sales GROUP BY year)
GROUP BY
year HAVING MAX(total_sale) =
(SELECT MAX(total_sale) FROM (SELECT SUM(sales_amount) AS total_sale FROM sales GROUP BY year));