Hot to get a average value on analytic function - sql

I have to get the average value on this BAL column for each account
with cte as (
select distinct t.DATE_ID,
ad.ACCOUNT_ID
from TIMEDATE t
, ACCOUNT_DLY ad)
select cte.date_id,
cte.Account_ID,
NVL(current_bal,lag (ad.current_bal) ignore nulls over (PARTITION by cte.account_id order by cte.date_id )) as bal
from cte left join ACCOUNT_DLY ad
on cte.date_id = ad.SRC_EXTRACT_DT
and cte.ACCOUNT_ID = ad.ACCOUNT_ID
order by 2,1;
I guess I need to use analytic function SUM or AVG again with partition on the NVL
on the top of the picture is my table on the bottom is how it suppose to look like
https://i.stack.imgur.com/H2Dql.png

Analytic vs Aggregate Function
You probably should use aggregate function AVG() ... GROUP BY instead of analytic function AVG() OVER(). The difference is that aggregate function returns one and only one row per columns it is grouped by. Analytic function puts the result in every resulting row and, in the sample as yours, that means use of DISTINCT keyword to eliminate duplicate rows which could be performance costly with bigger datasets. You can have the same result using aggregate or analytic function, though.
This is just to compare one and another - if your data is like this:
WITH
tbl AS
(
Select 1 "ID", To_Date('01-JAN-2023', 'dd-MON-yyyy') "A_DATE", 6 "A_VALUE" From Dual Union All
Select 1 "ID", To_Date('02-JAN-2023', 'dd-MON-yyyy') "A_DATE", 7 "A_VALUE" From Dual Union All
Select 1 "ID", To_Date('03-JAN-2023', 'dd-MON-yyyy') "A_DATE", 8 "A_VALUE" From Dual Union All
Select 2 "ID", To_Date('01-JAN-2023', 'dd-MON-yyyy') "A_DATE", 9 "A_VALUE" From Dual Union All
Select 2 "ID", To_Date('02-JAN-2023', 'dd-MON-yyyy') "A_DATE", 9 "A_VALUE" From Dual Union All
Select 2 "ID", To_Date('03-JAN-2023', 'dd-MON-yyyy') "A_DATE", 9 "A_VALUE" From Dual Union All
Select 3 "ID", To_Date('01-JAN-2023', 'dd-MON-yyyy') "A_DATE", 6 "A_VALUE" From Dual Union All
Select 3 "ID", To_Date('02-JAN-2023', 'dd-MON-yyyy') "A_DATE", 3 "A_VALUE" From Dual Union All
Select 3 "ID", To_Date('03-JAN-2023', 'dd-MON-yyyy') "A_DATE", 6 "A_VALUE" From Dual Union All
--
Select 1 "ID", To_Date('01-FEB-2023', 'dd-MON-yyyy') "A_DATE", 4 "A_VALUE" From Dual Union All
Select 1 "ID", To_Date('02-FEB-2023', 'dd-MON-yyyy') "A_DATE", 3 "A_VALUE" From Dual Union All
Select 1 "ID", To_Date('03-FEB-2023', 'dd-MON-yyyy') "A_DATE", 2 "A_VALUE" From Dual Union All
Select 2 "ID", To_Date('01-FEB-2023', 'dd-MON-yyyy') "A_DATE", 6 "A_VALUE" From Dual Union All
Select 2 "ID", To_Date('02-FEB-2023', 'dd-MON-yyyy') "A_DATE", 6 "A_VALUE" From Dual Union All
Select 2 "ID", To_Date('03-FEB-2023', 'dd-MON-yyyy') "A_DATE", 6 "A_VALUE" From Dual Union All
Select 3 "ID", To_Date('01-FEB-2023', 'dd-MON-yyyy') "A_DATE", 6 "A_VALUE" From Dual Union All
Select 3 "ID", To_Date('02-FEB-2023', 'dd-MON-yyyy') "A_DATE", 9 "A_VALUE" From Dual Union All
Select 3 "ID", To_Date('03-FEB-2023', 'dd-MON-yyyy') "A_DATE", 6 "A_VALUE" From Dual
)
... then use of aggregate function gives you the result you wanted...
-- Aggregate Function AVG() ... GROUP BY
Select DISTINCT
ID, To_Char(A_DATE, 'yyyymm') "MTH", AVG(A_VALUE) "AVG_VALUE"
From
tbl
Group By
ID, To_Char(A_DATE, 'yyyymm')
Order By
To_Char(A_DATE, 'yyyymm'), ID
/*
ID MTH AVG_VALUE
---------- ------ ----------
1 202301 7
2 202301 9
3 202301 5
1 202302 3
2 202302 6
3 202302 7
*/
... on the other hand, using analytic function (without DISTINCT keyword) will result with the same AVG value in all rows with same ID and MONTH...
-- Analytic Function AVG() OVER()
Select --DISTINCT
ID, To_Char(A_DATE, 'yyyymm') "MTH", AVG(A_VALUE) OVER(Partition By ID, To_Char(A_DATE, 'yyyymm') ) "AVG_VALUE"
From
tbl
Order By
To_Char(A_DATE, 'yyyymm'), ID
/*
ID MTH AVG_VALUE
---------- ------ ----------
1 202301 7
1 202301 7
1 202301 7
2 202301 9
2 202301 9
2 202301 9
3 202301 5
3 202301 5
3 202301 5
1 202302 3
1 202302 3
1 202302 3
2 202302 6
2 202302 6
2 202302 6
3 202302 7
3 202302 7
3 202302 7
... and if you put the DISTINCT keyword in your Select statement you will get the same result as with aggregate function above
-- using DISTINCT
/*
ID MTH AVG_VALUE
---------- ------ ----------
1 202301 7
2 202301 9
3 202301 5
1 202302 3
2 202302 6
3 202302 7
*/

Related

I do need help in writing SQL Query in ORACLE for the below sample data [duplicate]

This question already has answers here:
Second highest grade for each student
(3 answers)
Closed 3 months ago.
My data looks like below:
table
REF_NUM ID DATE
SIM1 1 12-Oct-22
SIM1 2 10-Oct-22
SIM2 3 15-Oct-22
SIM2 4 14-Oct-22
SIM3 5 08-Oct-22
SIM3 6 02-Oct-22
SIM4 7 08-Oct-22
SIM4 8 10-Oct-22
Output should be as below:
Output:
REF_NUM ID DATE
SIM1 2 10-Oct-22
SIM2 4 14-Oct-22
SIM3 6 02-Oct-22
SIM4 7 08-Oct-22
basically I need data with distinct ref_num , respective ID and with SECOND HIGHEST DATE. Here I have just given two dates in main table, But each ref_num can have more than two dates.
I can sure that whatever I have tried is wrong
Rank rows per each ref_num by the date datatype value in descending order; then fetch these that rank as the second highest.
Sample data:
SQL> with test (ref_num, id, datum) as
2 (select 'sim1', 1, date '2022-10-12' from dual union all
3 select 'sim1', 2, date '2022-10-10' from dual union all
4 select 'sim2', 3, date '2022-10-15' from dual union all
5 select 'sim2', 4, date '2022-10-14' from dual union all
6 select 'sim3', 5, date '2022-10-08' from dual union all
7 select 'sim3', 6, date '2022-10-02' from dual union all
8 select 'sim4', 7, date '2022-10-08' from dual union all
9 select 'sim4', 8, date '2022-10-10' from dual
10 ),
Query begins here:
11 temp as
12 (select ref_num, id, datum,
13 rank() over (partition by ref_num order by datum desc) rnk
14 from test
15 )
16 select ref_num, id, datum
17 from temp
18 where rnk = 2
19 order by ref_num;
REF_ ID DATUM
---- ---------- ----------
sim1 2 10.10.2022
sim2 4 14.10.2022
sim3 6 02.10.2022
sim4 7 08.10.2022
SQL>

SQL Check in which months an ID appears and show the results in a single row for each ID

I have 3 month tables for January, February and March.
January table:
ID
Date
1
01/01/2022
2
01/02/2022
3
01/02/2022
1
01/10/2022
4
01/12/2022
February table:
ID
Date
1
02/01/2022
2
02/07/2022
3
02/10/2022
2
02/15/2022
5
02/17/2022
March table:
ID
Date
1
03/19/2022
2
03/11/2022
3
03/14/2022
3
03/25/2022
6
03/13/2022
Lets assume i make a UNION ALL between all three tables. Now taking March as reference point, i need to classify each ID according to the last date it appeared in the table (the same ID can be repeated in the table with the same or different date, the max date is to be used for the excercise). Now i need to check in which months the IDs appear and be able to sort them according to the month where the ID is present.
Months
ID
Last Date
Jan-Feb-Mar
1
03/19/12
Jan-Feb-Mar
2
03/11/12
Jan-Feb-Mar
3
03/14/12
Jan
4
01/12/2022
Feb
5
02/17/2022
Mar
6
03/13/2022
Also i would need to classify according to a range of days since the last date an ID appeared. The range of days are the following: 1-30, 31-60 and 61-90. It should look something like this table below, even better if the field Months from the previous example shown is added too
Range of Days
ID
Last Date
Months
1-30
1
03/19/12
Jan-Feb-Mar
1-30
2
03/11/12
Jan-Feb-Mar
1-30
3
03/14/12
Jan-Feb-Mar
61-90
4
01/12/2022
Jan
31-60
5
02/17/2022
Feb
1-30
6
03/13/2022
Mar
Try this...
WITH
t_jan AS
(
Select 1 "ID", To_Date('01/01/2022', 'mm/dd/yyyy') "DT" From DUAL UNION ALL
Select 2 "ID", To_Date('01/02/2022', 'mm/dd/yyyy') "DT" From DUAL UNION ALL
Select 3 "ID", To_Date('01/02/2022', 'mm/dd/yyyy') "DT" From DUAL UNION ALL
Select 1 "ID", To_Date('01/10/2022', 'mm/dd/yyyy') "DT" From DUAL UNION ALL
Select 4 "ID", To_Date('01/12/2022', 'mm/dd/yyyy') "DT" From DUAL
),
t_feb AS
(
Select 1 "ID", To_Date('02/01/2022', 'mm/dd/yyyy') "DT" From DUAL UNION ALL
Select 2 "ID", To_Date('02/07/2022', 'mm/dd/yyyy') "DT" From DUAL UNION ALL
Select 3 "ID", To_Date('02/10/2022', 'mm/dd/yyyy') "DT" From DUAL UNION ALL
Select 2 "ID", To_Date('02/15/2022', 'mm/dd/yyyy') "DT" From DUAL UNION ALL
Select 5 "ID", To_Date('02/17/2022', 'mm/dd/yyyy') "DT" From DUAL
),
t_mar AS
(
Select 1 "ID", To_Date('03/19/2022', 'mm/dd/yyyy') "DT" From DUAL UNION ALL
Select 2 "ID", To_Date('03/11/2022', 'mm/dd/yyyy') "DT" From DUAL UNION ALL
Select 3 "ID", To_Date('03/14/2022', 'mm/dd/yyyy') "DT" From DUAL UNION ALL
Select 3 "ID", To_Date('03/25/2022', 'mm/dd/yyyy') "DT" From DUAL UNION ALL
Select 6 "ID", To_Date('03/13/2022', 'mm/dd/yyyy') "DT" From DUAL
),
-- -- -- -- -- -- -- -- -- -- -- --
mnths AS
(
Select
LISTAGG(MONTHS, '-') WITHIN GROUP (ORDER BY To_Char(DT, 'mm')) "MONTHS",
ID "ID",
Max(DT) "DT"
From
(
Select To_Char(Max(DT), 'Mon') "MONTHS", ID, Max(DT) "DT" From t_jan Group By ID UNION ALL
Select To_Char(Max(DT), 'Mon') "MONTHS", ID, Max(DT) "DT" From t_feb Group By ID UNION ALL
Select To_Char(Max(DT), 'Mon') "MONTHS", ID, Max(DT) "DT" From t_mar Group By ID
)
Group By
ID
),
days AS
(
Select
m.MONTHS "MONTHS",
m.ID "ID",
m.DT "DT",
Max(LAST_DAY(m.DT)) OVER() "LAST_DT",
Max(LAST_DAY(m.DT)) OVER() - m.DT "DAYS"
From
mnths m
)
SELECT
CASE
WHEN d.DAYS Between 1 And 30 THEN '1-30'
WHEN d.DAYS Between 31 And 60 THEN '31-60'
WHEN d.DAYS Between 61 And 90 THEN '61-90'
ELSE
'???'
END "RANGE_OF_DAYS",
d.ID "ID",
d.DT "LAST_DATE",
d.MONTHS "MONTHS"
FROM
days d
ORDER BY
d.ID
--
-- Result
--
-- RANGE_OF_DAYS ID LAST_DATE MONTHS
-- 1-30 1 19-MAR-22 Jan-Feb-Mar
-- 1-30 2 11-MAR-22 Jan-Feb-Mar
-- 1-30 3 25-MAR-22 Jan-Feb-Mar
-- 61-90 4 12-JAN-22 Jan
-- 31-60 5 17-FEB-22 Feb
-- 1-30 6 13-MAR-22 Mar

How to separate range of year on oracle

I am working on a db oracle and I need to create a query where it return a range of date. For example:
Supose that I had a field of like this:
I need to get this dates and apply a range of years to return someting like:
|'0-5'|'6-10'|'11-15'|...
| 10 | 35 | 20 |...
where each range contains a number of people in this range of years old.
I tried to use SELECT CASE...
SELECT CASE
WHEN DATE_BORN <= DATE_BORN + 5 THEN '0 - 5
WHEN DATE_BORN >= DATE_BORN + 6 AND DATE_BORN <= 10 THEN '6 - 10'
END AS AGE_RANGE,
COUNT(*)
FROM MY_TABLE
GROUP BY 1
So I saw that this way change only days not year.
How can I write this query?
That's conditional aggregation:
SQL> with test (date_born) as
2 (select date '2000-05-12' from dual union all
3 select date '2001-05-12' from dual union all
4 select date '2012-05-12' from dual union all
5 select date '2013-05-12' from dual union all
6 select date '2004-05-12' from dual union all
7 select date '2008-05-12' from dual union all
8 select date '2009-05-12' from dual union all
9 select date '2001-05-12' from dual union all
10 select date '2012-05-12' from dual union all
11 select date '2001-05-12' from dual union all
12 select date '2004-05-12' from dual union all
13 select date '2005-05-12' from dual
14 )
15 select
16 sum(case when extract (year from date_born) between 2000 and 2005 then 1 else 0 end) as "2000 - 2005",
17 sum(case when extract (year from date_born) between 2006 and 2010 then 1 else 0 end) as "2006 - 2010",
18 sum(case when extract (year from date_born) between 2011 and 2015 then 1 else 0 end) as "2011 - 2015"
19 from test;
2000 - 2005 2006 - 2010 2011 - 2015
----------- ----------- -----------
7 2 3
SQL>
Here is a dynamic way to do this (using the sample table above)
First I think it's easier to have your ranges in rows rather than columns, easier for having a variety of dates that may change.
Second your first grouping is 6 years, so I changed it to just be series of 5 years:
with test (date_born) as
(select date '2000-05-12' from dual union all
select date '2001-05-12' from dual union all
select date '2012-05-12' from dual union all
select date '2013-05-12' from dual union all
select date '2004-05-12' from dual union all
select date '2008-05-12' from dual union all
select date '2009-05-12' from dual union all
select date '2001-05-12' from dual union all
select date '2012-05-12' from dual union all
select date '2001-05-12' from dual union all
select date '2004-05-12' from dual union all
select date '2005-05-12' from dual
)
,mydata AS (
SELECT
(SELECT min(extract(YEAR FROM date_born)) FROM test)+((LEVEL-1)*5)dt1
,(SELECT min(extract(YEAR FROM date_born)) FROM test)+((LEVEL-1)*5)+4 dt2
FROM dual CONNECT BY LEVEL*5 <=
(SELECT max(extract(YEAR FROM date_born))-min(extract(YEAR FROM date_born)) FROM test)+5)
SELECT d.*, count(t.date_born) cnt FROM mydata d
LEFT JOIN test t ON extract(YEAR FROM date_born) BETWEEN d.dt1 AND d.dt2
GROUP BY dt1, dt2
ORDER BY dt1;
You get this for your solution
DT1 DT2 CNT
2000 2004 6
2005 2009 3
2010 2014 3
Solution is basically extracting years from dates, finding min/max of this data set, using connect to get all years in between, and then joining to count your matching records

RESET SUM(AMT_FIELD) OVER(PARTITION BY UNIQUE FIELD ORDER BY ROWNUM)

I just wanted to know, how can I reset summation of number field in below SQL query.
Attached screenshot is the sample of result that I need to get.
Query used:
SUM(UNPAID_MONTHLY) OVER(PARTITION BY SAMPLE_ACCT ORDER BY MONTH_NO DESC) TOTAL_UNPAID_AMT
You need to reset the sum every time the value is zero. You can use cumulative sum to define the group and then another cumulative sum:
select t.*,
sum(unpaid_monthly) over (partition by sample_acct, grp order by month_no desc)
from (select t.*,
sum(case when unpaid_monthly = 0 then 1 else 0 end) over (partition by sample_acct order by month_no) as grp
from t
) t;
You can also use the MATCH_RECOGNIZE clause (if you run Oracle 12 or higher):
WITH t (unpaid_monthly, sample_acct, month_no) AS
(SELECT 1335.67, 22900005, 1 FROM dual UNION ALL
SELECT 1289.36, 22900005, 2 FROM dual UNION ALL
SELECT 1241.95, 22900005, 3 FROM dual UNION ALL
SELECT 1211.32, 22900005, 4 FROM dual UNION ALL
SELECT 1179.33, 22900005, 5 FROM dual UNION ALL
SELECT 0, 22900005, 6 FROM dual UNION ALL
SELECT 5509.8, 22900005, 7 FROM dual UNION ALL
SELECT 3388.59, 22900005, 8 FROM dual UNION ALL
SELECT 1398.41, 22900005, 9 FROM dual UNION ALL
SELECT 0, 22900005, 10 FROM dual UNION ALL
SELECT 1717.97, 22900005, 11 FROM dual UNION ALL
SELECT 0, 22900005, 12 FROM dual UNION ALL
SELECT 5016.4, 22900005, 13 FROM dual)
SELECT unpaid_monthly, sample_acct, month_no,
sum_unpaid + unpaid_monthly AS TOTAL_UNPAID_AMT
FROM t
MATCH_RECOGNIZE (
PARTITION BY sample_acct
ORDER BY month_no
MEASURES
FINAL SUM(unpaid_monthly) - SUM(unpaid_monthly) AS sum_unpaid
ALL ROWS PER MATCH
PATTERN (a+ b?)
DEFINE
a AS unpaid_monthly > 0);
UNPAID_MONTHLY SAMPLE_ACCT MONTH_NO TOTAL_UNPAID_AMT
=============================================================
1335.67 22900005 1 6257.63
1289.36 22900005 2 4921.96
1241.95 22900005 3 3632.6
1211.32 22900005 4 2390.65
1179.33 22900005 5 1179.33
0 22900005 6 0
5509.8 22900005 7 10296.8
3388.59 22900005 8 4787
1398.41 22900005 9 1398.41
0 22900005 10 0
1717.97 22900005 11 1717.97
0 22900005 12 0
5016.4 22900005 13 5016.4

SQL Oracle Query self query

I am trying to figure out how to populate the below NULL values with 1.245 for dates from 07-OCT-14 to 29-SEP-14 then from 26-SEP-14 to 28-JUL-14 it will be 1.447.
This means if the date is less than or equal to the given date then use the value of max effective date which is less than the given date
We could select the last available index_ratio value for given security_alias and effective date <=p.effective_date , so in other words we will need to modify the sql to return from the subquery the index ratio value identified for the maximum available effective date assuming that this effective date is less or equal position effective date
How to populate the value ?
select ab.security_alias,
ab.index_ratio,
ab.effective_date
from securitydbo.security_analytics_fi ab
where ab.security_alias = 123627
order by ab.effective_date desc
Below should be the output
Assuming I understand your requirements correctly, I think the analytic function LAST_VALUE() is what you're after. E.g.:
with sample_data as (select 1 id, 10 val, to_date('01/08/2015', 'dd/mm/yyyy') dt from dual union all
select 1 id, null val, to_date('02/08/2015', 'dd/mm/yyyy') dt from dual union all
select 1 id, null val, to_date('03/08/2015', 'dd/mm/yyyy') dt from dual union all
select 1 id, null val, to_date('04/08/2015', 'dd/mm/yyyy') dt from dual union all
select 1 id, 20 val, to_date('05/08/2015', 'dd/mm/yyyy') dt from dual union all
select 1 id, 21 val, to_date('06/08/2015', 'dd/mm/yyyy') dt from dual union all
select 1 id, null val, to_date('07/08/2015', 'dd/mm/yyyy') dt from dual union all
select 1 id, null val, to_date('08/08/2015', 'dd/mm/yyyy') dt from dual union all
select 1 id, 31 val, to_date('09/08/2015', 'dd/mm/yyyy') dt from dual union all
select 1 id, null val, to_date('10/08/2015', 'dd/mm/yyyy') dt from dual union all
select 1 id, 42 val, to_date('11/08/2015', 'dd/mm/yyyy') dt from dual)
select id,
last_value(val ignore nulls) over (partition by id order by dt) val,
dt
from sample_data
order by id, dt desc;
ID VAL DT
---------- ---------- ----------
1 42 11/08/2015
1 31 10/08/2015
1 31 09/08/2015
1 21 08/08/2015
1 21 07/08/2015
1 21 06/08/2015
1 20 05/08/2015
1 10 04/08/2015
1 10 03/08/2015
1 10 02/08/2015
1 10 01/08/2015