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
Related
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
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
account name
year
revenue
abc
2006
1000
abc
2007
2000
abc
2008
5000
Hello everyone,
So I am trying to find a way to subtract the revenue for the latest year for a given account name to the earliest year found in a dataset.
For example in the above table
the latest year for abc -> 2008
the earliest year for abc -> 2006,
I can't hardcode the years in the code, I don't know what the years would be.
So, I want to get something like this
account name
subtracted revenue
abc
4000
I wish I could share some code but I have no idea how to proceed. I was thinking of using windowing function, but don't know how to apply it in this scenario.
Here is something that I tried, just check if this is any helpful
insert into revenue
select * from
(select 'abc' as accountname, 2006 as year, 1000 as revenue union
select 'abc' as accountname, 2007 as year, 2000 as revenue union
select 'abc' as accountname, 2008 as year, 5000 as revenue union
select 'def' as accountname, 2004 as year, 1000 as revenue union
select 'def' as accountname, 2006 as year, 3000 as revenue union
select 'xyz' as accountname, 2005 as year, 5000 as revenue
) as a
select accountname
, sum(case when yearname ='maxyear' then revenue else 0 end) - sum(case when yearname ='minyear' then revenue else 0 end) subtractedrevenue
from
(select *
, case when min(year)over(partition by accountname order by accountname) = max(year)over(partition by accountname order by accountname) then 'maxyear'
when year = min(year)over(partition by accountname order by accountname) then 'minyear'
when year= max(year)over(partition by accountname order by accountname) then 'maxyear'
else '' end yearname
from revenue
) as a
where yearname<>''
group by accountname```
You can use window functions:
select account_name,
sum(case when seqnum_desc = 1 then revenue else - revenue end)
from (select t.*,
row_number() over (partition by account_name order by year) as seqnum_asc,
row_number() over (partition by account_name order by year desc) as seqnum_desc
from t
) t
where 1 in (seqnum_asc, seqnum_desc)
group by account_name;
Here is a db<>fiddle.
YEAR MONTH BALANCE SSN
2016 1 3175 34/1043/03T
2016 1 2984 93/1194/07T
2016 1 2269 39/3149/00T
2015 12 3172 36/1011/03T
2015 12 2984 22/1224/07T
2015 12 2169 12/3143/00T
For example I have this table, but I have rows for each month of each year, and I have to choose the best ssn and balance of each month of each year. For example, here, I would like obtain this on my query:
YEAR MONTH BALANCE SSN
2016 1 3175 34/1043/03T
2015 12 3172 36/1011/03T
What can I do?
You can do this in several ways. A very Oracle'ish way is to use keep:
select year, month,
max(balance) as balance,
max(SSN) keep (dense_rank first order by balance desc) as ssn
from t
group by year, month;
Like most DBMSes Oracle supports ROW_NUMBER/RANK:
select *
from
(
select year, month, balance, SSN,
row_number()
over (partition by year, month
order by balance desc) as rn
from tab
) dt
where rn = 1
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));