Performing a COUNT on the MAX number of occurences - sql

My scenario is that person A can sells product A each month of the year. From that information I had to calculate in what month they sold the most of product A for the current year.
Should they sell 10 of product A in January, 6 in August and 10 October, i take the info for the latest month (in this case October).
However, i want to include some sort of tracker that says if the current MAX for the year has been equaled by person A at an earlier point in the year i want to COUNT the number of occurencies. Should person A go on to sell 15 in Novemeber, the counter should restart.
Current data =
EMP PRODUCT MONTH VOLUME
---------------------------------------------------
A A 1 10
A A 8 6
A A 10 10
AIM=
EMP PRODUCT MAX(VOLUME) COUNT
---------------------------------------------------
A A 10 2
Any suggestions as to the most efficient way of resolving this would be great!

CREATE TABLE MY_TABLE (EMP VARCHAR2(10), PRODUCT VARCHAR2(10), MONTH NUMBER, VOLUME NUMBER);
INSERT INTO MY_TABLE VALUES ('A', 'A', 1, 10);
INSERT INTO MY_TABLE VALUES ('A', 'A', 8, 6);
INSERT INTO MY_TABLE VALUES ('A', 'A', 10, 10);
COMMIT;
--EMP PRODUCT MONTH VOLUME
-----------------------------------------------------
--A A 1 10
--A A 8 6
--A A 10 10
SELECT EMP,
PRODUCT,
VOLUME,
MY_COUNT
FROM ( SELECT EMP,
PRODUCT,
VOLUME,
COUNT (MY_RANK) MY_COUNT,
RANK () OVER (PARTITION BY EMP, PRODUCT ORDER BY VOLUME DESC)
MY_SECOND_RANK
FROM (SELECT EMP,
PRODUCT,
volume,
RANK ()
OVER (PARTITION BY EMP, PRODUCT
ORDER BY VOLUME DESC, MONTH DESC)
MY_RANK
FROM MY_TABLE)
GROUP BY EMP, PRODUCT, VOLUME)
WHERE MY_SECOND_RANK = 1;

Related

Cumulative average and count over occurrences increasing in time

I am looking to calculate an average (over number of occurrences) and observation count over increasing dates per instance (take customer as an example instance) in Oracle SQL.
So the count will increase as date goes up, the average could go up or down.
I can do it for an individual case and a fixed time interval, but I would like to see a series for every customer, with every row a separate date where a sale occurred. Right now, I have a single row per customer. Here is the SQL summarizing the average and count for a fixed time interval:
SELECT AVG(bought_usd) as avg_bought
, COUNT(*) as num_of_interactions
, cust_id
FROM salesTable
WHERE obsdate >= DATE('2000-01-01')
AND obsdate <= DATE('2022-01-01')
GROUP BY cust_id
So for an input of:
the output should look like:
Use analytic functions:
SELECT "DATE",
cust,
AVG(bought_usd) OVER (PARTITION BY cust ORDER BY "DATE") AS avg,
COUNT(*) OVER (PARTITION BY cust ORDER BY "DATE") AS cnt
FROM salestable
ORDER BY cust, "DATE"
Note: DATE is a reserved word. You should not use it as an identifier.
Which, for the sample data:
CREATE TABLE salestable ("DATE", cust, bought_usd) AS
SELECT DATE '2010-10-01', 'Cust A', 100 FROM DUAL UNION ALL
SELECT DATE '2010-12-18', 'Cust A', 50 FROM DUAL UNION ALL
SELECT DATE '2010-12-18', 'Cust B', 120 FROM DUAL UNION ALL
SELECT DATE '2011-10-01', 'Cust B', 180 FROM DUAL;
Outputs:
DATE
CUST
AVG
CNT
2010-10-01 00:00:00
Cust A
100
1
2010-12-18 00:00:00
Cust A
75
2
2010-12-18 00:00:00
Cust B
120
1
2011-10-01 00:00:00
Cust B
150
2
db<>fiddle here

Table with daily historical stock prices. How to pull stocks where the price reached a certain number for the first time

I have a table with historical stocks prices for hundreds of stocks. I need to extract only those stocks that reached $10 or greater for the first time.
Stock
Price
Date
AAA
9
2021-10-01
AAA
10
2021-10-02
AAA
8
2021-10-03
AAA
10
2021-10-04
BBB
9
2021-10-01
BBB
11
2021-10-02
BBB
12
2021-10-03
Is there a way to count how many times each stock hit >= 10 in order to pull only those where count = 1 (in this case it would be stock BBB considering it never reached 10 in the past)?
Since I couldn't figure how to create count I've tried the below manipulations with min/max dates but this looks like a bit awkward approach. Any idea of a simpler solution?
with query1 as (
select Stock, min(date) as min_greater10_dt
from t
where Price >= 10
group by Stock
), query2 as (
select Stock, max(date) as max_greater10_dt
from t
where Price >= 10
group by Stock
)
select Stock
from t a
join query1 b on b.Stock = a.Stock
join query2 c on c.Stock = a.Stock
where not(a.Price < 10 and a.Date between b.min_greater10_dt and c.max_greater10_dt)
This is a type of gaps-and-islands problem which can be solved as follows:
detect the change from < 10 to >= 10 using a lagged price
count the number of such changes
filter in only stock where this has happened exactly once
and take the first row since you only want the stock (you could group by here but a row number allows you to select the entire row should you wish to).
declare #Table table (Stock varchar(3), Price money, [Date] date);
insert into #Table (Stock, Price, [Date])
values
('AAA', 9, '2021-10-01'),
('AAA', 10, '2021-10-02'),
('AAA', 8, '2021-10-03'),
('AAA', 10, '2021-10-04'),
('BBB', 9, '2021-10-01'),
('BBB', 11, '2021-10-02'),
('BBB', 12, '2021-10-03');
with cte1 as (
select Stock, Price, [Date]
, row_number() over (partition by Stock, case when Price >= 10 then 1 else 0 end order by [Date] asc) rn
, lag(Price,1,0) over (partition by Stock order by [Date] asc) LaggedStock
from #Table
), cte2 as (
select Stock, Price, [Date], rn, LaggedStock
, sum(case when Price >= 10 and LaggedStock < 10 then 1 else 0 end) over (partition by Stock) StockOver10
from cte1
)
select Stock
--, Price, [Date], rn, LaggedStock, StockOver10 -- debug
from cte2
where Price >= 10
and StockOver10 = 1 and rn = 1;
Returns:
Stock
BBB
Note: providing DDL+DML as show above makes it much easier of people to assist.

SQL turn rows into columns

Hello is it possible to turn row values into columns.
I am using ORACLE SQL and I want to take the month and turn it into columns with the kpi value as shown below.
I tried partitions and merge statements but nothing seems to work.
I would really appreciate some help.
Thank you in advance.
Input data:
department
year
month
kpi
value
A
2000
1
sales
5000
A
2000
1
revenue per client
120
A
2000
2
sales
6000
A
2000
2
revenue per client
140
Desired Output:
department
year
kpi
1
2
A
2000
sales
5000
6000
A
2000
revenue per client
120
140
You can use pivot to do so:
Schema and insert statements:
create table mytable (department varchar(20),year int,month int,kpi varchar(50),value int);
insert into mytable values('A', 2000, 1, 'sales' ,5000);
insert into mytable values('A', 2000, 1, 'revenue per client', 120);
insert into mytable values('A', 2000, 2, 'sales' ,6000);
insert into mytable values('A', 2000, 2, 'revenue per client', 140);
Query:
select * from (
select department,year,month,kpi,value
from mytable
)
pivot
(
max(value)
for month in (1,2)
)
Output:
DEPARTMENT
YEAR
KPI
1
2
A
2000
revenue per client
120
140
A
2000
sales
5000
6000
db<fiddle here
You can use conditional aggrwegation:
select department, year, kpi,
max(case when month = 1 then value end) as month_1,
max(case when month = 2 then value end) as month_2
from t
group by department, year, kpi;

Oracle SQL - Sum of distinct value belong year, for the first time

sorry for the title but is a little difficult to explain the topic in a sigle row..
I have a table like this and i want to know (for each month in the year) the number of employee who have received bonus for the first time.
EMPLOYEE_NAME MONTH BONUS_RECEIVED
AAA 1 1
BBB 1 1
CCC 2 1
AAA 2 1
DDD 2 1
AAA 3 1
BBB 3 1
XXX 3 1
So, the result should be
MONTH TOTAL_BONUS
1 2
2 2
3 1
Month 1, employee AAA and BBB receive the bonus (so the result is 2)
Month 2, employee CCC and DD receive bonus (AAA already received across the year), so the result is 2
Month 3, only employee XXX has received bonus, because AAA and BBB has already received it across the year
A double aggregation solves your problem:
select month, count(1) as total_bonus
from (
select employee_name, min(month) as month
from table_like_this
where bonus_received = 1
group by employee_name
)
group by month;
First, for each employee you find the first month he/she received a bonus. Then, you count the number of employees per all "first bonus-received-month found".
U CAN ALSO USE RANK()
SELECT MONTH
,COUNT(BONUS) AS BONUS
FROM (
SELECT EMPLOYEE
,MONTH
,BONUS
,RANK() OVER (
PARTITION BY EMPLOYEE ORDER BY MONTH
) AS RN
FROM TBTEST
)
WHERE RN = 1
GROUP BY MONTH
You could use ROW_NUMBER() analytic function.
For example,
Setup
CREATE TABLE t
(EMPLOYEE_NAME varchar2(3), MONTH number, BONUS_RECEIVED number);
INSERT ALL
INTO t (EMPLOYEE_NAME, MONTH, BONUS_RECEIVED)
VALUES ('AAA', 1, 1)
INTO t (EMPLOYEE_NAME, MONTH, BONUS_RECEIVED)
VALUES ('BBB', 1, 1)
INTO t (EMPLOYEE_NAME, MONTH, BONUS_RECEIVED)
VALUES ('CCC', 2, 1)
INTO t (EMPLOYEE_NAME, MONTH, BONUS_RECEIVED)
VALUES ('AAA', 2, 1)
INTO t (EMPLOYEE_NAME, MONTH, BONUS_RECEIVED)
VALUES ('DDD', 2, 1)
INTO t (EMPLOYEE_NAME, MONTH, BONUS_RECEIVED)
VALUES ('AAA', 3, 1)
INTO t (EMPLOYEE_NAME, MONTH, BONUS_RECEIVED)
VALUES ('BBB', 3, 1)
INTO t (EMPLOYEE_NAME, MONTH, BONUS_RECEIVED)
VALUES ('XXX', 3, 1)
SELECT * FROM dual;
Query
SQL> SELECT MONTH,
2 COUNT(rn) total_bonus
3 FROM
4 (SELECT t.*,
5 row_number() OVER(PARTITION BY employee_name ORDER BY MONTH) rn
6 FROM t
7 WHERE BONUS_RECEIVED = 1
8 )
9 WHERE rn = 1
10 GROUP BY MONTH;
MONTH TOTAL_BONUS
---------- -----------
1 2
2 2
3 1

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));