How to get latest active record - oracle

Here is my data. Member can be enroll multiple times and can change plan anytime,I need to get C_level data for latest near to getdate that is "8/1/2017" for ID 1, For ID 2 doesn't have latest data, that case we have to show 12/31/2016 record.
ID Start_Date End_Date C_Level
1 1/1/2016 12/31/2016 1
1 1/1/2017 8/1/2017 2
1 9/1/2017 12/31/2017 3
1 1/1/2018 12/31/2018 0
2 1/1/2015 12/31/2015 2
2 1/1/2016 12/31/2016 3

If I understand your requirements right, then this query should give what you want:
WITH current_running AS (
SELECT to_date( '8/1/2017','mm/dd/rrrr') As Current_running_date
FROM dual
)
SELECT * FROM (
SELECT t.*,
row_number() Over (partition by id order by end_date desc ) As rn,
c.Current_running_date
FROM Table1 t
JOIN current_running c
ON c.Current_running_date >= ANY( t.Start_Date, t.End_Date )
)
WHERE rn = 1
;
Demo: http://sqlfiddle.com/#!4/33de0/10
For current_running_date = 2017-08-01 it gives:
| ID | START_DATE | END_DATE | C_LEVEL | RN | CURRENT_RUNNING_DATE |
|----|-----------------------|-----------------------|---------|----|----------------------|
| 1 | 2017-01-01 00:00:00.0 | 2017-08-01 00:00:00.0 | 2 | 1 | 2017-08-01T00:00:00Z |
| 2 | 2016-01-01 00:00:00.0 | 2016-12-31 00:00:00.0 | 3 | 1 | 2017-08-01T00:00:00Z |
while for current_running_date = 2016-07-15
| ID | START_DATE | END_DATE | C_LEVEL | RN | CURRENT_RUNNING_DATE |
|----|-----------------------|-----------------------|---------|----|----------------------|
| 1 | 2016-01-01 00:00:00.0 | 2016-12-31 00:00:00.0 | 1 | 1 | 2016-07-15T00:00:00Z |
| 2 | 2016-01-01 00:00:00.0 | 2016-12-31 00:00:00.0 | 3 | 1 | 2016-07-15T00:00:00Z |

Related

Oracle values change when row_number() added

I have an Oracle query that uses DUAL to produce a list of dates in a subquery, and a case when to identify business days:
SELECT DATES
,case when to_char(DATES, 'd') in (1,7)
then 0
else 1 end as business_day
FROM (
SELECT to_date('1/1/2020','MM/DD/YYYY') + (LEVEL -1) AS DATES
FROM DUAL connect by level <=(to_date('1/1/2021','MM/DD/YYYY') - to_date('1/1/2020','MM/DD/YYYY'))
) L1
So far so good. Now when I nest this in a subquery, and add a row_number() function, all my business_day values become 0. If I remove the row_number() function, business_day goes back to normal.
SELECT L2.DATES
, L2.business_day
, row_number() OVER (PARTITION BY L2.business_day ORDER BY L2.DATES ASC) as dateindex
FROM (
SELECT DATES
,case when to_char(DATES, 'd') in (1,7)
then 0
else 1 end as business_day
FROM (
SELECT to_date('1/1/2020','MM/DD/YYYY') + (LEVEL -1) AS DATES
FROM DUAL connect by level <=(to_date('1/1/2021','MM/DD/YYYY') - to_date('1/1/2020','MM/DD/YYYY'))
) L1
) L2
Any idea how adding a new column causes another's values to change?
I suspect you aren't paying attention to the actual dates; running your code in this db<>fiddle, the first query returns:
DATES | BUSINESS_DAY
:-------- | -----------:
01-JAN-20 | 1
02-JAN-20 | 1
03-JAN-20 | 1
04-JAN-20 | 1
05-JAN-20 | 0
06-JAN-20 | 0
07-JAN-20 | 1
08-JAN-20 | 1
09-JAN-20 | 1
10-JAN-20 | 1
11-JAN-20 | 1
...
while the second returns:
DATES | BUSINESS_DAY | DATEINDEX
:-------- | -----------: | --------:
05-JAN-20 | 0 | 1
06-JAN-20 | 0 | 2
12-JAN-20 | 0 | 3
13-JAN-20 | 0 | 4
19-JAN-20 | 0 | 5
20-JAN-20 | 0 | 6
26-JAN-20 | 0 | 7
27-JAN-20 | 0 | 8
02-FEB-20 | 0 | 9
03-FEB-20 | 0 | 10
...
All the business_day values are indeed zero... or at least, if you only look at the start of the result set. If you look further down:
...
27-DEC-20 | 0 | 103
28-DEC-20 | 0 | 104
01-JAN-20 | 1 | 1
02-JAN-20 | 1 | 2
03-JAN-20 | 1 | 3
04-JAN-20 | 1 | 4
...
You don't have an order-by clause, and the analytic processing internally happens to return in an order you aren't expecting. If you add an order-by then it looks more sensible, as in this db<>fiddle:
DATES | BUSINESS_DAY | DATEINDEX
:-------- | -----------: | --------:
01-JAN-20 | 1 | 1
02-JAN-20 | 1 | 2
03-JAN-20 | 1 | 3
04-JAN-20 | 1 | 4
05-JAN-20 | 0 | 1
06-JAN-20 | 0 | 2
07-JAN-20 | 1 | 5
08-JAN-20 | 1 | 6
09-JAN-20 | 1 | 7
10-JAN-20 | 1 | 8
11-JAN-20 | 1 | 9
12-JAN-20 | 0 | 3
...
Incidentally, the 'd' format element is NLS-sensitive, so someone else running this code in a session with different settings could see different results. It would safer to do:
when to_char(DATES, 'Dy', 'NLS_DATE_LANGUAGE=ENGLISH') in ('Sat', 'Sun')

Count Max number of day continious overdraff

Date | Account | Amount | Count max number of day continuous < 0 |
1 | 1001 | 100 | 0 |
2 | 1001 | -100 | 1 |
3 | 1001 | -100 | 2 |
4 | 1001 | 100 | 2 |
5 | 1001 | -100 | 2 |
6 | 1001 | -100 | 2 |
7 | 1001 | -100 | 3 |
8 | 1001 | -100 | 4 |
9 | 1001 | 100 | 4 |
I have sample data. I want have column "Count max number of day continuous < 0". How i can select it in database oracle
In order to find continuous periods you can use Tabibitosian method. Then use analytical count and finally max:
select date_, account, amount,
max(cnt) over (partition by account order by date_) max_overdraft_period
from (
select date_, account, amount,
count(case when amount <= 0 then 1 end)
over (partition by account, grp order by date_) cnt
from (
select date_, account, amount,
date_ - sum(case when amount <= 0 then 1 else 0 end)
over (partition by account order by date_) grp
from t ))
demo
I assumed that dates are continuous, if not then use row numbering at first.

List all days between two dates in Oracle

I am converting a postgres app to an Oracle app.
I came across this query:
WITH cost AS (SELECT
well_schedules.id,
generate_series(well_schedules.start_date::timestamp, well_schedules.end_date, '1 Day') AS "Date",
(well_schedules.drilling_engineering_estimate * well_schedules.well_estimated_working_interest)/((well_schedules.end_date - well_schedules.start_date) + 1) AS "Cost Per Day"
FROM
well_schedules
)
SELECT date_trunc('quarter', "Date"), COUNT("Cost Per Day"), id
FROM cost
GROUP BY id, date_trunc('quarter', "Date")
ORDER BY date_trunc('quarter', "Date")
The part I am struggling with is the generate_series line.
That line takes a start_date and end_date and lists all days between those two dates. We need that information to compile per day/week/month/quarter/year reports (or at least we assume we need that info).
Our data looks like this:
well_schedules
| id | start_date | end_date | cost |
| 1 | '2015-01-01' | '2015-03-20' | 100 |
We assume cost_per_day is equal across all days, so we'd like to generate a report that lets us look at cost_per_day, cost_per_week, cost_per_month, cost_per_year, and cost_per_quarter. cost_per_week/month/quarter/year is calculated by grouping the days by week/month/quarter/year and summing the associated cost_per_days
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE well_schedules ( id, start_date, end_date, cost ) AS
SELECT 1 , DATE '2015-01-01', DATE '2015-01-20', 100 FROM DUAL;
Query 1:
SELECT ID,
COLUMN_VALUE AS Day,
COST / ( end_date - start_date + 1 ) AS Cost_per_day
FROM well_schedules,
TABLE (
CAST(
MULTISET(
SELECT start_date + LEVEL - 1
FROM DUAL
CONNECT BY start_date + LEVEL - 1 <= end_date
)
AS SYS.ODCIDATELIST
)
)
Results:
| ID | DAY | COST_PER_DAY |
|----|---------------------------|--------------|
| 1 | January, 01 2015 00:00:00 | 5 |
| 1 | January, 02 2015 00:00:00 | 5 |
| 1 | January, 03 2015 00:00:00 | 5 |
| 1 | January, 04 2015 00:00:00 | 5 |
| 1 | January, 05 2015 00:00:00 | 5 |
| 1 | January, 06 2015 00:00:00 | 5 |
| 1 | January, 07 2015 00:00:00 | 5 |
| 1 | January, 08 2015 00:00:00 | 5 |
| 1 | January, 09 2015 00:00:00 | 5 |
| 1 | January, 10 2015 00:00:00 | 5 |
| 1 | January, 11 2015 00:00:00 | 5 |
| 1 | January, 12 2015 00:00:00 | 5 |
| 1 | January, 13 2015 00:00:00 | 5 |
| 1 | January, 14 2015 00:00:00 | 5 |
| 1 | January, 15 2015 00:00:00 | 5 |
| 1 | January, 16 2015 00:00:00 | 5 |
| 1 | January, 17 2015 00:00:00 | 5 |
| 1 | January, 18 2015 00:00:00 | 5 |
| 1 | January, 19 2015 00:00:00 | 5 |
| 1 | January, 20 2015 00:00:00 | 5 |
I will suggest the code below that consider the first and last day of the month from two dates:
Example:
Date Initial: 01/10/2014
Date Final: 12/21/2018
The code will return:
01/01/2014
02/01/2014
03/01/2014
04/01/2014
...
12/28/2018
12/29/2018
12/30/2018
12/31/2018
The Code:
SELECT
CAL.DT AS "Date"
,TO_NUMBER(TO_CHAR(CAL.DT,'DD')) AS "Day"
,TO_NUMBER(TO_CHAR(CAL.DT,'MM')) AS "Month"
,TO_NUMBER(TO_CHAR(CAL.DT,'YY')) AS "YearYY"
,TO_NUMBER(TO_CHAR(CAL.DT,'YYYY')) AS "YearYYYY"
,TO_CHAR(CAL.DT,'day') AS "Description_Day"
,TO_CHAR(CAL.DT,'dy') AS "Description_Day_Abrev"
,TO_CHAR(CAL.DT,'Month') AS "Description_Month"
,TO_CHAR(CAL.DT,'Mon') AS "Description_Month_Abrev"
,TO_CHAR(CAL.DT,'dd month yyyy') AS "Date_Text"
FROM (
SELECT
(
TO_DATE(SEQ.MM || SEQ.YYYY, 'MM/YYYY')-1
) + SEQ.NUM AS "DT"
FROM
(
SELECT RESULT NUM,
TO_CHAR(( -- Minimum Date
TO_DATE('01/01/2014', 'DD/MM/YYYY')
) , 'MM') AS "MM",
TO_CHAR(( -- Minimum Date
TO_DATE('01/01/2014', 'DD/MM/YYYY')
) , 'YYYY') AS "YYYY"
FROM
(
SELECT ROWNUM RESULT FROM DUAL CONNECT BY LEVEL <= (
(
-- Maximum Date
LAST_DAY(TO_DATE('31/12/2018', 'DD/MM/YYYY')) -- Always Last Day
-
-- Maximum Date
TRUNC(TO_DATE('01/01/2014', 'DD/MM/YYYY')) -- Always First Day of Month
) + 1 -- Because the First Day (RESULT) don't begin at zero
)
) -- How many sequences (RESULT) to generate
) SEQ
) CAL
;

Get all rows from a table having a particular column value but ordered by different column value

I am having trouble formulating this question, so I would give an example to demonstrate. Consider my table as,
CREATE TABLE ABC
(
PID NUMBER(10,0) NOT NULL,
DISP_COL VARCHAR2(100 BYTE),
VAL_COL VARCHAR2(20 BYTE),
ORD_COL1 NUMBER(5,2),
ORD_COL2 NUMBER(5,2),
CONSTRAINT PK_PID PRIMARY KEY
(
PID
)
);
And the data I have is,
PID | DISP_COL | VAL_COL | ORD_COL1 | ORD_COL2
----------------------------------------------
1 | DISP1 | VAL1 | 1 | 14
2 | DISP2 | VAL26 | 2 | 22
3 | DISP1 | VAL8 | 1 | 17
4 | DISP1 | VAL56 | 1 | 9
5 | DISP2 | VAL9 | 2 | -10
6 | DISP3 | VAL12 | 2 | 20
7 | AISP1 | VAL7 | 2 | -3
Now based on the descending ordering of ORD_COL1, ORD_COL2, I want to get the unique DISP_COL values and then all rows of that DISP_COL value to follow. So my data should like,
PID | DISP_COL | VAL_COL | ORD_COL1 | ORD_COL2
----------------------------------------------
2 | DISP2 | VAL26 | 2 | 22
5 | DISP2 | VAL9 | 2 | -10
6 | DISP3 | VAL12 | 2 | 20
7 | AISP1 | VAL7 | 2 | -3
3 | DISP1 | VAL8 | 1 | 17
1 | DISP1 | VAL1 | 1 | 14
4 | DISP1 | VAL56 | 1 | 9
A simple ORDER BY ORD_COL1 DESC, ORD_COL2 DESC does get me the order I want DISP_COL to occur but then I want the same valued rows to follow that.
I am kind of new to oracle and pl/sql, so all help is appreciated. Thanks in advance.
SELECT * FROM ABC ORDER BY ORD_COL1 DESC, DISP_COL ASC, ORD_COL2 DESC;
http://sqlfiddle.com/#!4/40401/18
You will need to order by the DISP_COL in ASC in order to get the result you want. See the updated fiddle and the code above. This will give you what you want from you question.
The following query worked (http://sqlfiddle.com/#!4/c340b/2/0)
SELECT A1.* FROM (SELECT * FROM ABC) A1 INNER JOIN
(SELECT DISP_COL, MAX(ORD_COL1) COL1, MAX(ORD_COL2) COL2 FROM ABC
GROUP BY DISP_COL
ORDER BY COL1 DESC, COL2 DESC) A2 ON (A1.DISP_COL = A2.DISP_COL)
ORDER BY A2.COL1 DESC, A2.COL2 DESC, A2.DISP_COL, A1.ORD_COL1 DESC,
A1.ORD_COL2 DESC;

ORACLE: INSERT SELECT FROM 2 views and value from param

I'm trying to insert some fields into MYTABLE from views MYVIEW1 and MYVIEW2 and then add a value from a parameter (this is part of a stored procedure) for UPDATED_BY, SYSDATE for UPDATED_ON. How can I correctly do this with INSERT SELECT or some other way entirely?
MYVIEW1
+------+----+-----+-----------+---------+
| YR | MO | QTR | USER_CODE | MO_PERF |
+------+----+-----+-----------+---------+
| 2012 | 1 | 1 | 1099 | 89 |
| 2012 | 2 | 1 | 1099 | 86 |
| 2012 | 3 | 1 | 1099 | 95 |
+------+----+-----+-----------+---------+
MYVIEW2
+------+-----+-----------+----------+
| YR | QTR | USER_CODE | QTR_PERF |
+------+-----+-----------+----------+
| 2012 | 1 | 1099 | 90 |
+------+-----+-----------+----------+
MYTABLE
+------+-----+-----------+---------+---------+---------+---------+-------------+------------+
| YR | QTR | USER_CODE | MO1_PCT | MO2_PCT | MO3_PCT | INC | UPDATED_BY | UPDATED_ON |
+------+-----+-----------+---------+---------+---------+---------+-------------+------------+
| 2012 | 1 | 1099 | 89 | 86 | 95 | 7000 | SAMPLE NAME | 01/16/2013 |
+------+-----+-----------+---------+---------+---------+---------+-------------+------------+
INSERT INTO MYTABLE
(YR,QTR,USER_CODE,MO1_PCT,MO2_PCT,MO3_PCT,INC,UPDATED_BY,UPDATED_ON)
SELECT b.YR,b.QTR,b.USER_CODE,b.MO1_PCT,b.MO2_PCT,b.MO3_PCT,c.INC
FROM MYVIEW1 b,
MYVIEW2 c
How do I insert values for (first month of QTR's MO_PERF) as MO1_PCT and (second month of QTR's MO_PERF) as MO2_PCT and (last month of QTR's MO_PERF) as MO3_PCT, making sure that I've inserted the right month within the right quarter and year.And then check if the MO_PERF values of each month has reached at least 85, else set INC as NULL.
,CASE WHEN MO1_PCT>=85 AND MO2_PCT>=85 AND MO3_PCT>=85 THEN 7000
ELSE NULL
END INC
If you're using oracle 11g then you can use PIVOT like this:
select YR, QTR, USER_CODE, "1_MO_PCT" MO1_PCT, "2_MO_PCT" MO2_PCT, "3_MO_PCT" MO3_PCT ,
case when "1_MO_PCT" >= 85 and "2_MO_PCT" >= 85 and "2_MO_PCT" >= 85 then 7000 end INC,
user updated_by, sysdate updated_on
from (
select m1.yr, m1.mo, m1.qtr, m1.user_code, m1.mo_perf, m2.qtr_perf
from myview1 m1 join myview2 m2 on m1.yr=m2.yr
and m1.qtr = m2.qtr and m1.user_code = m2.user_code )t
pivot(
max(mo_perf) MO_PCT for mo in (1,2,3)
)
Here is a sqlfiddle demo

Resources