Oracle Analytic Function.. OVER PARTITION BY - sql

Trying to calculate Total Sales like below with the following query, But getting not a group by error.
select country,month,sales,sum(sales) over (partition by country order by month)
from check2
group by country,month,sales
Data
Expected Output

If you're using OVER..PARTITION BY, then you don't need the GROUP BY.
Just this should be sufficient
select country,month,sales,sum(sales) over (partition by country order by month)
from check2
See example here on LiveSL

Don't use GROUP BY and, if you want the total for the entire partition then, don't use ORDER BY in the analytic function:
select country,
month,
sales,
sum(sales) over (partition by country) As total_sales
from check2
Which, for the sample data:
CREATE TABLE check2(country, month, sales) AS
SELECT 'US', 202204, 2000 FROM DUAL UNION ALL
SELECT 'US', 202205, 3000 FROM DUAL UNION ALL
SELECT 'US', 202206, 5000 FROM DUAL UNION ALL
SELECT 'Canada', 202204, 8000 FROM DUAL UNION ALL
SELECT 'Canada', 202205, 2000 FROM DUAL UNION ALL
SELECT 'Canada', 202206, 1000 FROM DUAL;
Outputs:
COUNTRY
MONTH
SALES
TOTAL_SALES
Canada
202205
2000
11000
Canada
202204
8000
11000
Canada
202206
1000
11000
US
202205
3000
10000
US
202204
2000
10000
US
202206
5000
10000
fiddle

Related

I want to get the count of roll number for each month

ID
STUDENT_ID
STATUS_DATE
1002
434120010026
25-FEB-22
1000
434120010026
03-MAY-03
1001
434120010026
25-FEB-22
1020
434120020023
18-MAR-22
1021
434120020025
18-MAR-22
1022
434120020025
16-MAR-22
Tried this
select count(*),
trunc(status_date, 'mm')
from test_studentattendance
group by trunc(status_date, 'mm');
got count of roll number in each month not the roll numbers.
COUNT(*)
TRUNC(STATUS_DATE,'MM')
1
01-MAY-03
2
01-FEB-22
3
01-MAR-22
COUNT(*) is counting the number of rows in each group and is counting students that appear in the same month multiple times.
To get the number on roll each month, you need to COUNT the DISTINCT identifier for each student (which, I assume would be student_id):
SELECT COUNT(DISTINCT student_id) AS number_of_students,
TRUNC(status_date, 'mm') AS month
FROM test_studentattendance
GROUP BY TRUNC(status_date, 'mm');
Which, for your sample data:
CREATE TABLE test_studentattendance (ID, STUDENT_ID, STATUS_DATE) AS
SELECT 1002, 434120010026, DATE '2022-02-25' FROM DUAL UNION ALL
SELECT 1000, 434120010026, DATE '2003-05-03' FROM DUAL UNION ALL
SELECT 1001, 434120010026, DATE '2022-02-25' FROM DUAL UNION ALL
SELECT 1020, 434120020023, DATE '2022-03-18' FROM DUAL UNION ALL
SELECT 1021, 434120020025, DATE '2022-03-18' FROM DUAL UNION ALL
SELECT 1022, 434120020025, DATE '2022-03-16' FROM DUAL;
Outputs:
NUMBER_OF_STUDENTS
MONTH
1
2022-02-01 00:00:00
1
2003-05-01 00:00:00
2
2022-03-01 00:00:00
db<>fiddle here

List the branch that monthly pays the most in salaries

I have this table, the expected output should be B003 since it's pays 54,000
STAFF
SALARY
BRAN
SL21
30000
B005
SG37
12000
B003
SG14
18000
B003
SA9
9000
B007
SG5
24000
B003
SL41
9000
B005
So far I only have this subquery, which isn't working how I expected.
SELECT BRANCHNO
FROM STAFF
WHERE (SALARY) IN (SELECT MAX(SUM(SALARY))
FROM STAFF
GROUP BY BRANCHNO);
This works but I want a subquery that returns the branchno
SELECT MAX(SUM(SALARY))
FROM STAFF
GROUP BY BRANCHNO;
select BRANCHNO max(sum_sal)
from (SELECT BRANCHNO, SUM(SALARY) sum_sal
FROM STAFF
GROUP BY BRANCHNO) q1
group by BRANCHNO ;
The column used to group the rows can be displayed. So, add BRANCHNO to your select clause.
One option is to use rank analytic function which ranks branches by sum of their salaries in descending order; you'd then return the one(s) that rank as the highest (rnk = 1).
Sample data:
SQL> with staff (staff, salary, bran) as
2 (select 'SL21', 30000, 'B005' from dual union all
3 select 'SG37', 12000, 'B003' from dual union all
4 select 'SG14', 18000, 'B003' from dual union all
5 select 'SA9' , 9000, 'B007' from dual union all
6 select 'SG5' , 24000, 'B003' from dual union all
7 select 'SL41', 9000, 'B005' from dual
8 )
Query:
9 select bran
10 from (select bran, rank() over (order by sum(salary) desc) rnk
11 from staff
12 group by bran
13 )
14 where rnk = 1;
BRAN
----
B003
SQL>

Need sql query to get expected output for the below,

Tried the below query, but it works only for the first and second records.
Select
name,
dept,
sal,
(
coalesce(sal, 0) + coalesce(saltodrop)
) as running total (
SELECT
name,
dept,
sal,
LAG(Sal, 1, 0) OVER(
PARTITION BY [dept]
ORDER BY
[name],
[dept] ASC
) AS [saltodrop]
FROM
dataset
) as data_set_extract
Input
Name dept sal
John sales 10000
Tom sales 8000
Tim sales 5000
George finance 6000
Dane finance 4000
Mike hr 5000
Meme hr 6000
Ark it 5000
Output
Name dept sal
John sales 1000
Tom sales 18000
Tim sales 23000
George finance 29000
Dane finance 33000
Mike hr 38000
Meme hr 44000
Ark it 49000
Using the Oracle database, I need to add a consecutive row of the
first two records, later the sum of the first record and second record
with that of the third record and so on.
Assuming you have already ordered the results then use the SUM analytic function and order by ROWNUM to keep the current ordering:
SELECT t.*,
SUM(sal) OVER (ORDER BY ROWNUM) AS cumulative_sal
FROM table_name t;
Which, for the sample data:
CREATE TABLE table_name (Name, dept, sal) AS
SELECT 'John', 'sales', 10000 FROM DUAL UNION ALL
SELECT 'Tom', 'sales', 8000 FROM DUAL UNION ALL
SELECT 'Tim', 'sales', 5000 FROM DUAL UNION ALL
SELECT 'George', 'finance', 6000 FROM DUAL UNION ALL
SELECT 'Dane', 'finance', 4000 FROM DUAL UNION ALL
SELECT 'Mike', 'hr', 5000 FROM DUAL UNION ALL
SELECT 'Meme', 'hr', 6000 FROM DUAL UNION ALL
SELECT 'Ark', 'it', 5000 FROM DUAL;
Outputs:
NAME
DEPT
SAL
CUMULATIVE_SAL
John
sales
10000
10000
Tom
sales
8000
18000
Tim
sales
5000
23000
George
finance
6000
29000
Dane
finance
4000
33000
Mike
hr
5000
38000
Meme
hr
6000
44000
Ark
it
5000
49000
db<>fiddle here

Best 10 of 12 in SQL

Scoring for a running race series. They get points at each monthly race based on their finish. Their total score is their best 10 of 12 monthly races. How do I get that for each member?
tblRacePoints
memnum - Membership number
RaceNo - YYYYMM, e.g., 201910
Points
I want for each their total score of all races, total score of their best 10 of 12, and each of their lowest two scores for the year. Not everyone has done all the races so they may not have 12 entries for the year.
How do I write a query to do this, and then to rank them by their best 10/12 points?
If you are using MSSQL database, you can use ROW_NUMBER as below to achieve your required output. Same logic can be used for some other databases too.
Note: Table structure is just an assumption.
WITH your_table(player_id,dt,points)
AS
(
SELECT 1,'20190101', 100 UNION ALL SELECT 1,'20190201', 200 UNION ALL
SELECT 1,'20190301', 300 UNION ALL SELECT 1,'20190401', 400 UNION ALL
SELECT 1,'20190501', 500 UNION ALL SELECT 1,'20190601', 600 UNION ALL
SELECT 1,'20190701', 700 UNION ALL SELECT 1,'20190801', 800 UNION ALL
SELECT 1,'20190901', 900 UNION ALL SELECT 1,'20191001', 1000 UNION ALL
SELECT 1,'20191101', 1100 UNION ALL SELECT 1,'20191201', 1200 UNION ALL
SELECT 2,'20190101', 400 UNION ALL SELECT 2,'20190201', 200 UNION ALL
SELECT 2,'20190301', 300 UNION ALL SELECT 2,'20190401', 400 UNION ALL
SELECT 2,'20190501', 500 UNION ALL SELECT 2,'20190601', 600 UNION ALL
SELECT 2,'20190701', 700 UNION ALL SELECT 2,'20190801', 800 UNION ALL
SELECT 2,'20190901', 900 UNION ALL SELECT 2,'20191001', 1000 UNION ALL
SELECT 2,'20191101', 1100 UNION ALL SELECT 2,'20191201', 1200
)
SELECT
player_id,
YEAR(dt) Year,
SUM(Points) total_point
FROM
(
SELECT *,
ROW_NUMBER() OVER (PARTITION BY player_id, YEAR(dt) ORDER BY Points DESC) RN
FROM your_table
)A
WHERE RN <= 10
GROUP BY player_id, YEAR(dt)

How to calculate MTD and QTD by YTD value in Oracle

There are some data in my table t1 looks like below:
date dealer YTD_Value
2018-01 A 1100
2018-02 A 2000
2018-03 A 3000
2018-04 A 4200
2018-05 A 5000
2018-06 A 5500
2017-01 B 100
2017-02 B 200
2017-03 B 500
... ... ...
then I want to write a SQL to query this table and get below result:
date dealer YTD_Value MTD_Value QTD_Value
2018-01 A 1100 1100 1100
2018-02 A 2000 900 2000
2018-03 A 3000 1000 3000
2018-04 A 4200 1200 1200
2018-05 A 5000 800 2000
2018-06 A 5500 500 2500
2017-01 B 100 100 100
2017-02 B 200 100 200
2017-03 B 550 350 550
... ... ... ... ...
'YTD' means Year to date
'MTD' means Month to date
'QTD' means Quarter to date
So if I want to calculate MTD and QTD value for dealer 'A' in '2018-01', it should be the same as YTD.
If I want to calculate MTD value for dealer 'A' in '2018-06', MTD value should equal to YTD value in '2018-06' minus YTD value in '2018-05'. And the QTD value in '2018-06' should equal to YTD value in '2018-06' minus YTD value in '2018-03' or equal to sum MTD value in (2018-04,2018-05,2018-06)
The same rule for other dealers such as B.
How can I write the SQL to achieve this purpose?
The QTD calculation is tricky, but you can do this query without subqueries. The basic idea is to do a lag() for the monthly value. Then use a max() analytic function to get the YTD value at the beginning of the quarter.
Of course, the first quarter of the year has no such value, so a coalesce() is needed.
Try this:
with t(dte, dealer, YTD_Value) as (
select '2018-01', 'A', 1100 from dual union all
select '2018-02', 'A', 2000 from dual union all
select '2018-03', 'A', 3000 from dual union all
select '2018-04', 'A', 4200 from dual union all
select '2018-05', 'A', 5000 from dual union all
select '2018-06', 'A', 5500 from dual union all
select '2017-01', 'B', 100 from dual union all
select '2017-02', 'B', 200 from dual union all
select '2017-03', 'B', 550 from dual
)
select t.*,
(YTD_Value - lag(YTD_Value, 1, 0) over (partition by substr(dte, 1, 4) order by dte)) as MTD_Value,
(YTD_Value -
coalesce(max(case when substr(dte, -2) in ('03', '06', '09') then YTD_VALUE end) over
(partition by substr(dte, 1, 4) order by dte rows between unbounded preceding and 1 preceding
), 0
)
) as QTD_Value
from t
order by 1
Here is a db<>fiddle.
The following query should do the job. It uses a CTE that translates the varchar date column to dates, and then a few joins to recover the value to compare.
I tested it in this db fiddle and the output matches your expected results.
WITH cte AS (
SELECT TO_DATE(my_date, 'YYYY-MM') my_date, dealer, ytd_value FROM my_table
)
SELECT
TO_CHAR(ytd.my_date, 'YYYY-MM') my_date,
ytd.ytd_value,
ytd.dealer,
ytd.ytd_value - NVL(mtd.ytd_value, 0) mtd_value,
ytd.ytd_value - NVL(qtd.ytd_value, 0) qtd_value
FROM
cte ytd
LEFT JOIN cte mtd ON mtd.my_date = ADD_MONTHS(ytd.my_date, -1) AND mtd.dealer = ytd.dealer
LEFT JOIN cte qtd ON qtd.my_date = ADD_MONTHS(TRUNC(ytd.my_date, 'Q'), -1) AND mtd.dealer = qtd.dealer
ORDER BY dealer, my_date
PS : date is a reserved word in most RDBMS (including Oracle), I renamed that column to my_date in the query.
You can use lag() windows analytic and sum() over .. aggregation functions as :
select "date",dealer,YTD_Value,MTD_Value,
sum(MTD_Value) over (partition by qt order by "date")
as QTD_Value
from
(
with t("date",dealer,YTD_Value) as
(
select '2018-01','A',1100 from dual union all
select '2018-02','A',2000 from dual union all
select '2018-03','A',3000 from dual union all
select '2018-04','A',4200 from dual union all
select '2018-05','A',5000 from dual union all
select '2018-06','A',5500 from dual union all
select '2017-01','B', 100 from dual union all
select '2017-02','B', 200 from dual union all
select '2017-03','B', 550 from dual
)
select t.*,
t.YTD_Value - nvl(lag(t.YTD_Value)
over (partition by substr("date",1,4) order by substr("date",1,4) desc, "date"),0)
as MTD_Value,
substr("date",1,4)||to_char(to_date("date",'YYYY-MM'),'Q')
as qt,
substr("date",1,4) as year
from t
order by year desc, "date"
)
order by year desc, "date";
Rextester Demo