Oracle SQL how many days has passed since previous order - sql

I have a problem how to calculate the days how many days has passed since previous order.
My code:
select
order_id,
order_date
from
oe.orders
where customer_id = 838
order by
order_date desc
The order_id and order_date are like below:
order_id = 1920 & order_date= 25-MAR-19 15.45.38.000000000
order_id = 1618 & order_date= 08-FEB-19 12.51.39.000000000
order_id = 1592 & order_date= 04-FEB-19 07.35.46.000000000
...
I am new user of sql and no idea how to do it. Thank you for your help!

If you want the differences in days (just the date part) then:
WITH
tbl AS
(
Select 1 "ID", To_Date('25-MAR-19 15.45.38', 'dd-MON-yy hh24:mi:ss') "A_DATE" From Dual Union All
Select 2 "ID", To_Date('08-FEB-19 12.51.39', 'dd-MON-yy hh24:mi:ss') "A_DATE" From Dual Union All
Select 3 "ID", To_Date('04-FEB-19 07.35.46', 'dd-MON-yy hh24:mi:ss') "A_DATE" From Dual Union All
Select 4 "ID", To_Date('28-JAN-19 12.13.10', 'dd-MON-yy hh24:mi:ss') "A_DATE" From Dual
)
Select
ID "ID",
TRUNC(A_DATE, 'dd') - TRUNC(Nvl(First_Value(A_DATE) OVER (Order By ID Rows Between 1 Preceding And Current Row), A_DATE), 'dd') "DAYS_DIFF"
From
tbl
ID
DAYS_DIFF
1
0
2
-45
3
-4
4
-7
OR ...
Select
ID "ID",
TRUNC(A_DATE, 'dd') - TRUNC(Nvl(Last_Value(A_DATE) OVER (Order By ID Rows Between Current Row And 1 Following ), A_DATE), 'dd') "DAYS_DIFF"
From
tbl
Order By TRUNC(A_DATE, 'dd')
... result
ID
DAYS_DIFF
4
0
3
7
2
4
1
45
Regards

CREATE TABLE orders
(ORDER_ID, ORDER_DATE) AS
SELECT 3, TIMESTAMP'2022-10-31 09:54:48' FROM DUAL UNION ALL
SELECT 2, TIMESTAMP'2022-10-17 19:04:44' FROM DUAL UNION ALL
SELECT 1, TIMESTAMP'2022-10-08 14:44:23' FROM DUAL
SELECT order_id, order_date,
order_date - LAG(order_date) OVER (ORDER BY order_id) AS diff
FROM orders;
ORDER_ID ORDER_DATE DIFF
1 08-OCT-22 02.44.23.000000 PM -
2 17-OCT-22 07.04.44.000000 PM +000000009 04:20:21.000000000
3 31-OCT-22 09.54.48.000000 AM +000000013 14:50:04.000000000

Related

Find overlapping date in SQL

I need SELECT for finding data with overlapping date in Oracle SQL just from today to exactly one year ago. ID_FORMULAR is not UNIQUE value and I need to include just data with overlapping date where ID_FORMULAR is UNIQUE.
My code:
SELECT T1.*
FROM VISITORS T1, VISITORS T2
WHERE ( T1.ID_FORMULAR != T2.ID_FORMULAR
AND t1.FROM_DATE >= t2.FROM_DATE
AND t1.FROM_DATE <= t2.TO_DATE
AND T1.CREATED_DATE >= ADD_MONTHS (TRUNC (CURRENT_DATE), -12)
AND T1.CREATED_DATE < TRUNC (CURRENT_DATE) + 1)
OR ( T1.ID_FORMULAR != T2.ID_FORMULAR
AND t1.TO_DATE >= t2.FROM_DATE
AND t1.TO_DATE <= t2.TO_DATE
AND T1.CREATED_DATE >= ADD_MONTHS (TRUNC (CURRENT_DATE), -12)
AND T1.CREATED_DATE < TRUNC (CURRENT_DATE) + 1)
OR ( T1.ID_FORMULAR != T2.ID_FORMULAR
AND t1.TO_DATE >= t2.TO_DATE
AND t1.FROM_DATE <= t2.FROM_DATE
AND T1.CREATED_DATE >= ADD_MONTHS (TRUNC (CURRENT_DATE), -12)
AND T1.CREATED_DATE < TRUNC (CURRENT_DATE) + 1)
It is not working correctly. Any help?
From Oracle 12, you can use MATCH_RECOGNIZE to perform row-by-row processing:
SELECT *
FROM (
SELECT *
FROM visitors
WHERE created_date >= ADD_MONTHS(TRUNC(CURRENT_DATE), -12)
AND created_date < TRUNC(CURRENT_DATE) + 1
)
MATCH_RECOGNIZE(
ORDER BY from_date
ALL ROWS PER MATCH
PATTERN (any_row overlap+)
DEFINE
overlap AS PREV(id_formular) != id_formular
AND PREV(to_date) >= from_date
)
Which, for the sample data:
CREATE TABLE visitors (id_formular, created_date, from_date, to_date) AS
SELECT 1, DATE '2022-08-01', DATE '2022-08-01', DATE '2022-08-03' FROM DUAL UNION ALL
SELECT 2, DATE '2022-08-01', DATE '2022-08-02', DATE '2022-08-04' FROM DUAL UNION ALL
SELECT 3, DATE '2022-08-01', DATE '2022-08-03', DATE '2022-08-05' FROM DUAL UNION ALL
SELECT 1, DATE '2022-08-01', DATE '2022-08-06', DATE '2022-08-06' FROM DUAL UNION ALL
SELECT 2, DATE '2022-08-01', DATE '2022-08-07', DATE '2022-08-09' FROM DUAL UNION ALL
SELECT 2, DATE '2022-08-01', DATE '2022-08-08', DATE '2022-08-10' FROM DUAL UNION ALL
SELECT 1, DATE '2022-08-01', DATE '2022-08-09', DATE '2022-08-11' FROM DUAL;
Outputs:
FROM_DATE
ID_FORMULAR
CREATED_DATE
TO_DATE
01-AUG-22
1
01-AUG-22
03-AUG-22
02-AUG-22
2
01-AUG-22
04-AUG-22
03-AUG-22
3
01-AUG-22
05-AUG-22
08-AUG-22
2
01-AUG-22
10-AUG-22
09-AUG-22
1
01-AUG-22
11-AUG-22
db<>fiddle here
I don't quite understand the question. The thing that is confusing me is that you need just rows where ID is unique. If ID is unique than there is no other row to overlap with. Anyway, lets suppose that the sample data is like below:
WITH
tbl AS
(
SELECT 0 "ID", DATE '2021-07-01' "CREATED", DATE '2021-07-01' "DATE_FROM", DATE '2021-07-13' "DATE_TO" FROM DUAL UNION ALL
SELECT 1, DATE '2021-12-01', DATE '2021-12-01', DATE '2021-12-03' FROM DUAL UNION ALL
SELECT 1, DATE '2021-12-04', DATE '2021-12-04', DATE '2021-12-14' FROM DUAL UNION ALL
SELECT 1, DATE '2021-12-12', DATE '2021-12-12', DATE '2021-12-29' FROM DUAL UNION ALL
SELECT 2, DATE '2022-08-04', DATE '2022-08-04', DATE '2022-08-10' FROM DUAL UNION ALL
SELECT 2, DATE '2022-08-11', DATE '2022-08-11', DATE '2022-08-21' FROM DUAL UNION ALL
SELECT 2, DATE '2022-08-21', DATE '2022-08-21', DATE '2022-08-29' FROM DUAL UNION ALL
SELECT 3, DATE '2022-08-11', DATE '2022-08-11', DATE '2022-08-29' FROM DUAL UNION ALL
SELECT 4, DATE '2022-08-14', DATE '2022-08-14', DATE '2022-08-14' FROM DUAL UNION ALL
SELECT 4, DATE '2022-08-29', DATE '2022-08-14', DATE '2022-08-29' FROM DUAL
)
We can add some columns that will tell us if the ID is unique or not, what is the order of appearance of the same ID, what is the end date of the previous row for the same ID and if the rows of a particular ID overlaps or not. Here is the code: (used analytic functions with windowing clause)
SELECT
ID "ID",
CASE WHEN Count(*) OVER (PARTITION BY ID ORDER BY ID) = 1 THEN 'Y' ELSE 'N' END "IS_UNIQUE",
Count(ID) OVER (PARTITION BY ID ORDER BY ID, DATE_FROM, DATE_TO ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) "ID_ORDER_NO",
CREATED "CREATED",
DATE_FROM "DATE_FROM",
DATE_TO "DATE_TO",
CASE
WHEN Count(ID) OVER (PARTITION BY ID ORDER BY ID, DATE_FROM, DATE_TO ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) = 1
THEN Null
ELSE
First_Value(DATE_TO) OVER (PARTITION BY ID ORDER BY ID, DATE_FROM, DATE_TO ROWS BETWEEN 1 PRECEDING AND CURRENT ROW )
END "PREVIOUS_END_DATE",
CASE
WHEN Count(ID) OVER (PARTITION BY ID ORDER BY ID, DATE_FROM, DATE_TO ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) = 1
THEN 'N'
ELSE
CASE
WHEN DATE_FROM <= First_Value(DATE_TO) OVER (PARTITION BY ID ORDER BY ID, DATE_FROM, DATE_TO ROWS BETWEEN 1 PRECEDING AND CURRENT ROW )
THEN 'Y'
ELSE 'N'
END
END "OVERLAPS"
FROM
TBL
WHERE
CREATED BETWEEN ADD_MONTHS(TRUNC(SYSDATE, 'dd'), -12) And TRUNC(SYSDATE, 'dd')
Here is the resulting dataset...
/* R e s u l t
ID IS_UNIQUE ID_ORDER_NO CREATED DATE_FROM DATE_TO PREVIOUS_END_DATE OVERLAPS
---------- --------- ----------- --------- --------- --------- ----------------- --------
1 N 1 01-DEC-21 01-DEC-21 03-DEC-21 N
1 N 2 04-DEC-21 04-DEC-21 14-DEC-21 03-DEC-21 N
1 N 3 12-DEC-21 12-DEC-21 29-DEC-21 14-DEC-21 Y
2 N 1 04-AUG-22 04-AUG-22 10-AUG-22 N
2 N 2 11-AUG-22 11-AUG-22 21-AUG-22 10-AUG-22 N
2 N 3 21-AUG-22 21-AUG-22 29-AUG-22 21-AUG-22 Y
3 Y 1 11-AUG-22 11-AUG-22 29-AUG-22 N
4 N 1 14-AUG-22 14-AUG-22 14-AUG-22 N
4 N 2 29-AUG-22 14-AUG-22 29-AUG-22 14-AUG-22 Y
*/
This dataset could be further used to get you the rows and columns that you are trying to get. You can filter it, do some other calculations (like number of overlaping days), get number of rows per ID and so on....
Regards...

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

Month counts between dates

I have the below table. I need to count how many ids were active in a given month. So thinking I'll need to create a row for each id that was active during that month so that id can be counted each month. A row should be generated for a term_dt during that month.
active_dt term_dt id
1/1/2018 101
1/1/2018 5/15/2018 102
3/1/2018 6/1/2018 103
1/1/2018 4/25/18 104
Apparently this is a "count number of overlapping intervals" problem. The algorithm goes like this:
Create a sorted list of all start and end points
Calculate a running sum over this list, add one when you encounter a start and subtract one when you encounter an end
If two points are same then perform subtractions first
You will end up with list of all points where the sum changed
Here is a rough outline of the query. It is for SQL Server but could be ported to any RDBMS that supports window functions:
WITH cte1(date, val) AS (
SELECT active_dt, 1 FROM #t AS t
UNION ALL
SELECT COALESCE(term_dt, '2099-01-01'), -1 FROM #t AS t
-- if end date is null then assume the row is valid indefinitely
), cte2 AS (
SELECT date, SUM(val) OVER(ORDER BY date, val) AS rs
FROM cte1
)
SELECT YEAR(date) AS YY, MONTH(date) AS MM, MAX(rs) AS MaxActiveThisYearMonth
FROM cte2
GROUP BY YEAR(date), MONTH(date)
DB Fiddle
I was toying with a simpler query, that seemed to do the trick, for Oracle:
with candidates (month_start) as (
select to_date ('2018-' || column_value || '-01','YYYY-MM-DD')
from
table
(sys.odcivarchar2list('01','02','03','04','05',
'06','07','08','09','10','11','12'))
), sample_data (active_dt, term_dt, id) as (
select to_date('01/01/2018', 'MM/DD/YYYY'), null, 101 from dual
union select to_date('01/01/2018', 'MM/DD/YYYY'),
to_date('05/15/2018', 'MM/DD/YYYY'), 102 from dual
union select to_date('03/01/2018', 'MM/DD/YYYY'),
to_date('06/01/2018', 'MM/DD/YYYY'), 103 from dual
union select to_date('01/01/2018', 'MM/DD/YYYY'),
to_date('04/25/2018', 'MM/DD/YYYY'), 104 from dual
)
select c.month_start, count(1)
from candidates c
join sample_data d
on c.month_start between d.active_dt and nvl(d.term_dt,current_date)
group by c.month_start
order by c.month_start
An alternative solution would be to use a hierarchical query, e.g.:
WITH your_table AS (SELECT to_date('01/01/2018', 'dd/mm/yyyy') active_dt, NULL term_dt, 101 ID FROM dual UNION ALL
SELECT to_date('01/01/2018', 'dd/mm/yyyy') active_dt, to_date('15/05/2018', 'dd/mm/yyyy') term_dt, 102 ID FROM dual UNION ALL
SELECT to_date('01/03/2018', 'dd/mm/yyyy') active_dt, to_date('01/06/2018', 'dd/mm/yyyy') term_dt, 103 ID FROM dual UNION ALL
SELECT to_date('01/01/2018', 'dd/mm/yyyy') active_dt, to_date('25/04/2018', 'dd/mm/yyyy') term_dt, 104 ID FROM dual)
SELECT active_month,
COUNT(*) num_active_ids
FROM (SELECT add_months(TRUNC(active_dt, 'mm'), -1 + LEVEL) active_month,
ID
FROM your_table
CONNECT BY PRIOR ID = ID
AND PRIOR sys_guid() IS NOT NULL
AND LEVEL <= FLOOR(months_between(coalesce(term_dt, SYSDATE), active_dt)) + 1)
GROUP BY active_month
ORDER BY active_month;
ACTIVE_MONTH NUM_ACTIVE_IDS
------------ --------------
01/01/2018 3
01/02/2018 3
01/03/2018 4
01/04/2018 4
01/05/2018 3
01/06/2018 2
01/07/2018 1
01/08/2018 1
01/09/2018 1
01/10/2018 1
Whether this is more or less performant than the other answers is up to you to test.

Get rows from current month if older is not available

I have a table that looks like this:
+--------------------+---------+
| Month (date) | amount |
+--------------------+---------+
| 2016-10-01 | 20 |
| 2016-08-01 | 10 |
| 2016-07-01 | 17 |
+--------------------+---------+
I'm looking for a query (sql statement) which satisfies the following conditions:
Give me the value of the previous month.
If there is no value for the previous month lock back in time until one can be found.
If there is just a value for the current month give me this value.
In the example table the row I'm looking for would be this:
+--------------------+---------+
| 2016-08-01 | 10 |
+--------------------+---------+
Has anyone a idea for a non complex select query?
Thanks in advance,
Peter
You may need the following:
SELECT *
FROM ( SELECT *
FROM test
WHERE TRUNC(SYSDATE, 'month') >= month
ORDER BY CASE
WHEN TRUNC(SYSDATE, 'month') = month
THEN 0 /* if current month, ordered last */
ELSE 1 /* previous months are ordered first */
END DESC,
month DESC /* among previous months, the greatest first */
)
WHERE ROWNUM = 1
Another way using MAX
WITH tbl AS (
SELECT TO_DATE('2016-10-01', 'YYYY-MM-DD') AS "month", 20 AS amount FROM dual
UNION
SELECT TO_DATE('2016-08-01', 'YYYY-MM-DD') AS "month", 10 AS amount FROM dual
UNION
SELECT TO_DATE('2016-07-01', 'YYYY-MM-DD') AS "month", 5 AS amount FROM dual
)
SELECT *
FROM tbl
WHERE TRUNC("month", 'MONTH') = NVL((SELECT MAX(t."month")
FROM tbl t
WHERE t."month" < TRUNC(SYSDATE, 'MONTH')),
TRUNC(SYSDATE, 'MONTH'));
I would use row_number():
select t.*
from (select t.*,
row_number() over (order by (case when to_char(dte, 'YYYY-MM') = to_char(sysdate, 'YYYY-MM') then 1 else 2 end) desc,
dte desc
) as seqnum
from t
) t
where seqnum = 1;
Actually, you don't need row_number() for this:
select t.*
from (select t.*
from t
order by (case when to_char(dte, 'YYYY-MM') = to_char(sysdate, 'YYYY-MM') then 1 else 2 end) desc,
dte desc
) t
where rownum = 1;
It's not the nicest query but it should work.
select amount, date from (
select amount, date, row_number over(partition by HERE_PUT_ID order by
case trunc(date, 'month') when trunc(sysdate, 'month') then to_date('00010101', 'yyyymmdd') else trunc(date, 'month') end
desc) r)
where r = 1;
I guess you have some id in table so put id column instead of HERE_PUT_ID if you want query for whole table just delete: partition by HERE_PUT_ID
I added more data for testing, and an "id" column (a more realistic scenario) to show how this would work. If there is no "id" in your data, simply delete any reference to it from the solution.
Notes - month is a reserved Oracle word, don't use it as a column name. The solution assumes the date column contains dates that are already truncated to the beginning of the month. The trick in "order by" in the dense_rank last is to assign a value (ANY value!) when the month is the current month; by default, the value assigned to all other months is NULL, which by default come after any non-null value in an ascending order.
You may want to test the various solutions for efficiency if execution time is important.
with
inputs ( id, mth, amount ) as (
select 1, date '2016-10-01', 20 from dual union all
select 1, date '2016-08-01', 10 from dual union all
select 1, date '2016-07-01', 17 from dual union all
select 2, date '2016-10-01', 30 from dual union all
select 2, date '2016-09-01', 25 from dual union all
select 3, date '2016-10-01', 20 from dual union all
select 4, date '2016-08-01', 45 from dual union all
select 4, date '2016-06-01', 30 from dual
)
-- end of TEST DATA - the solution (SQL query) is below this line
select id,
max(mth) keep(dense_rank last order by
case when mth = trunc(sysdate, 'mm') then 0 end, mth) as mth,
max(amount) keep(dense_rank last order by
case when mth = trunc(sysdate, 'mm') then 0 end, mth) as amount
from inputs
group by id
order by id -- ORDER BY is optional
;
ID MTH AMOUNT
--- ---------- -------
1 2016-08-01 10
2 2016-09-01 25
3 2016-10-01 20
4 2016-08-01 45
You could sort the data in the direction you want to:
with MyData as
(
SELECT to_date('2016-10-01','YYYY-MM-DD') MY_DATE, 20 AMOUNT FROM DUAL UNION
SELECT to_date('2016-08-01','YYYY-MM-DD') MY_DATE, 10 AMOUNT FROM DUAL UNION
SELECT to_date('2016-07-01','YYYY-MM-DD') MY_DATE, 17 AMOUNT FROM DUAL
),
MyResult AS (
SELECT
D.*
FROM MyData D
ORDER BY
DECODE(
12*TO_CHAR(MY_DATE,'YYYY') + TO_CHAR(MY_DATE,'MM'),
12*TO_CHAR(SYSDATE,'YYYY') + TO_CHAR(SYSDATE,'MM'),
-1,
12*TO_CHAR(MY_DATE,'YYYY') + TO_CHAR(MY_DATE,'MM'))
DESC
)
SELECT * FROM MyResult WHERE RowNum = 1

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