Incorrect results from max and rank function - oracle

I am trying to return a row with the latest psi score but my code returns multiple rows and the row that I actually want is returned as the last row on result set. What can I do with this code to resolve the issue.
S
ELECT REQUEST_NUM,PI_CANDIDATE_NUM,
COALESCE (MAX_GCO_AD_KNOWLEDGE_TEST_SCORE, MAX_PSI_OVERALL_SCORE) AS PSI_SCORE
--COALESCE(MAX_GCO_AD_KNOWLEDGE_TEST_SCORE,MAX_PSI_OVERALL_SCORE)-39.035024/8.439997 AS PSI XMation
FROM(
SELECT
REQUEST_NUM,PI_CANDIDATE_NUM,
MAX(AA.PSI_OVERALL_SCORE) KEEP (DENSE_RANK FIRST ORDER BY AA.ARANK) MAX_PSI_OVERALL_SCORE,
MAX(AA.GCO_AD_KNOWLEDGE_TEST_SCORE) KEEP (DENSE_RANK FIRST ORDER BY AA.ARANK) MAX_GCO_AD_KNOWLEDGE_TEST_SCORE
FROM (
select
RANK() OVER (PARTITION BY REQUEST_NUM ORDER BY REQUEST_LAST_MODIFIED_DT ) ARANK
,PARENT_PI_NUMBER,REQUEST_NUM,PI_CANDIDATE_NUM,PSI_OVERALL_SCORE,GCO_AD_KNOWLEDGE_TEST_SCORE,REQUEST_LAST_MODIFIED_DT
from
WC_APPLICATION_EVENT_F
-- GCO_AD_KNOWLEDGE_TEST_SCORE != '10100' and
where PI_CANDIDATE_NUM = '4173093'
--nd GCO_AD_KNOWLEDGE_TEST_SCORE is null
) AA
--where AA.ARANK=1
GROUP BY REQUEST_NUM,PI_CANDIDATE_NUM
--ORDER By PARENT_PI_NUMBER,PI_NUMBER,REQUEST_LAST_MODIFIED_DT;
)
BB;
Sample data:
REQUEST_NUM PI_CANDIDATE_NUM REQUEST_LAST_MODIFIED_DT PSI_SCORE
----------- ---------------- ------------------------ ---------
4639022
1655626 4639022 5-Mar-17
1662401 4639022 8-Mar-17 22.6
1662470 4639022 6-Apr-17
1662486 4639022 6-Apr-17
1662499 4639022 8-Mar-17 30.3
1771817 4639022 7-Jun-17 35.3
1797323 4639022 24-Jun-17 38.5
My expected results is the last row with a value of 38.5 since has the latest date.

Here is one way to do this. It assumes you input a specific candidate (otherwise the analytic function in the subquery can be modified to get the most recent NON-NULL score for each candidate).
EDIT: If you need to retrieve the most recent score for ALL candidates, then: (1) remove the condition that filters on a single candidate in the inner query; (2) add partition by pi_candidate_num right after rank() over ( (after the opening parenthesis) and add a space before order by ... (still in the over(.....) clause of rank()). \EDIT
If two NON-NULL scores were achieved ON THE SAME DATE (perhaps that is impossible in your data, but if it is...) then BOTH rows will be returned.
I assumed the candidate number is a NUMBER, so I removed the single quotes in the condition; and I changed the candidate number to match your sample inputs.
The WITH clause is NOT PART OF THE QUERY - remove it before you try the solution.
with
test_data ( request_num, pi_candidate_num, request_last_modified_dt, psi_score ) as (
select null, 4639022, null , null from dual union all
select 1655626, 4639022, to_date( '5-Mar-17', 'dd-Mon-rr'), null from dual union all
select 1662401, 4639022, to_date( '8-Mar-17', 'dd-Mon-rr'), 22.6 from dual union all
select 1662470, 4639022, to_date( '6-Apr-17', 'dd-Mon-rr'), null from dual union all
select 1662486, 4639022, to_date( '6-Apr-17', 'dd-Mon-rr'), null from dual union all
select 1662499, 4639022, to_date( '8-Mar-17', 'dd-Mon-rr'), 30.3 from dual union all
select 1771817, 4639022, to_date( '7-Jun-17', 'dd-Mon-rr'), 35.3 from dual union all
select 1797323, 4639022, to_date('24-Jun-17', 'dd-Mon-rr'), 38.5 from dual
)
select request_num, pi_candidate_num, request_last_modified_dt, psi_score
from (
select t.*, rank() over (order by request_last_modified_dt desc) rn
from test_data t
where pi_candidate_num = 4639022
and psi_score is not null
)
where rn = 1
;
REQUEST_NUM PI_CANDIDATE_NUM REQUEST_LAST_MODIFIED_DT PSI_SCORE
----------- ---------------- ------------------------ ----------
1797323 4639022 24-Jun-17 38.5

Related

oracle how to generate a list of period from a list of date

i would like to write a query to transform a list of date
list of date
15/02/2021
12/04/2021
28/07/2021
31/08/2021
to a list of period
start
end
15/02/2021
11/04/2021
12/04/2021
27/07/2021
28/07/2021
31/08/2021
Is it possible to do it in a oracle query ? Thanks for your help
Variant 1:
select *
from (
select
dt as dt_start,
case when lead(dt,2)over(order by dt) is not null
then lead(dt)over(order by dt)-1
else lead(dt)over(order by dt)
end as dt_end
from t
)
where dt_end is not null;
Full test with test data:
alter session set nls_date_format='dd/mm/yyyy';
with t(dt) as (
select to_date('15/02/2021','dd/mm/yyyy') from dual union all
select to_date('12/04/2021','dd/mm/yyyy') from dual union all
select to_date('28/07/2021','dd/mm/yyyy') from dual union all
select to_date('31/08/2021','dd/mm/yyyy') from dual
)
select *
from (
select
dt as dt_start,
case when lead(dt,2)over(order by dt) is not null
then lead(dt)over(order by dt)-1
else lead(dt)over(order by dt)
end as dt_end
from t
)
where dt_end is not null;
DT_START DT_END
---------- ----------
15/02/2021 11/04/2021
12/04/2021 27/07/2021
28/07/2021 31/08/2021
Variant 2: match_recognize (Oracle 12+):
select
dt_start,dt_end
from t
match_recognize(
order by dt
MEASURES
prev(dt) as dt_start,
nvl2(next(dt),e.dt-1,e.dt) as dt_end
all rows per match
PATTERN (e+)
DEFINE
e AS dt > prev(dt)
);
Full example with test data:
with t(dt) as (
select to_date('15/02/2021','dd/mm/yyyy') from dual union all
select to_date('12/04/2021','dd/mm/yyyy') from dual union all
select to_date('28/07/2021','dd/mm/yyyy') from dual union all
select to_date('31/08/2021','dd/mm/yyyy') from dual
)
select
dt_start,dt_end
from t
match_recognize(
order by dt
MEASURES
prev(dt) as dt_start,
nvl2(next(dt),e.dt-1,e.dt) as dt_end
all rows per match
PATTERN (e+)
DEFINE
e AS dt > prev(dt)
);
DT_START DT_END
---------- ----------
15/02/2021 11/04/2021
12/04/2021 27/07/2021
28/07/2021 31/08/2021
DBFiddle: https://dbfiddle.uk/?rdbms=oracle_18&fiddle=a9f9e256762c87e67ce3783f51f8d7c8
That's exactly what I'm looking for !
thanks !
I've just changed few things because I'm in Oracle 10g probably
with t as (
select to_date('15/02/2021','dd/mm/yyyy') dt from dual union all
select to_date('12/04/2021','dd/mm/yyyy') from dual union all
select to_date('28/07/2021','dd/mm/yyyy') from dual union all
select to_date('31/08/2021','dd/mm/yyyy') from dual
)
select *
from (
select
dt as dt_start,
case when lead(dt,2)over(order by dt) is not null
then lead(dt)over(order by dt)-1
else lead(dt)over(order by dt)
end as dt_end
from t
)
where dt_end is not null;

Oracle: Is it possible to filter out duplicates only when they appear in succession?

Thank you in advance for your help.
I have a table that holds itinerary information for drivers. There will be times when the itinerary seems to have the same stop (but is several days apart). I'd like to be able to query the table and filter out any record where the address is the same AND the dates are consecutive.
Is this possible?
Thanks again,
josh
with tst as(
select timestamp '2020-08-01 00:00:00' dt, '123 street' loc from dual
union all
select timestamp '2020-08-01 00:00:00', '89 street' from dual
union all
select timestamp '2020-08-02 00:00:00', '456 airport' from dual
union all
select timestamp '2020-08-04 00:00:00', '456 airport' from dual
union all
select timestamp '2020-08-05 00:00:00', '67 street' from dual
union all
select timestamp '2020-08-06 00:00:00', '89 street' from dual
union all
select timestamp '2020-08-07 00:00:00', '123 street' from dual
)
select dt, loc
from (
select dt, loc, nvl(lag(loc) over(order by dt), 'FIRST_ROW') prev_loc
from tst
) where loc <> prev_loc;
fiddle
Another approach would be to use Tabibitosan method which assign consecutive rows a group number and then count number of rows per group.(found in asktom website).
with test_data as(
select date'2020-08-01' dt, '123 street' loc from dual
union all
select date '2020-08-01', '89 street' from dual
union all
select date '2020-08-02', '456 airport' from dual
union all
select date '2020-08-04', '456 airport' from dual
union all
select date '2020-08-05', '67 street' from dual
union all
select date '2020-08-06', '89 street' from dual
union all
select date '2020-08-07', '123 street' from dual
)
select max(dt),loc
from
(
select t.*
,row_number() over (order by dt) -
row_number() over (partition by loc order by dt) grp
from test_data t
)
group by grp,loc
having count(*) > 1;
Another approach using match_recognize available from 12c onwards.patter used {1,} says repeated one or more times
more to learn match_recognize here
with test_data as(
select date'2020-08-01' dt, '123 street' loc from dual
union all
select date '2020-08-01', '89 street' from dual
union all
select date '2020-08-02', '456 airport' from dual
union all
select date '2020-08-04', '456 airport' from dual
union all
select date '2020-08-05', '67 street' from dual
union all
select date '2020-08-06', '89 street' from dual
union all
select date '2020-08-07', '123 street' from dual
)
select *
from test_data
match_recognize (
order by dt
all rows per match
pattern (equal{1,})
define
equal as loc = prev(loc)
);
Playground: Dbfiddle

Oracle - Return a column per date in a range

Let's say I have Table A: assistance
PersonID Date CHECK
123456 2012-01-01 F
213415 2012-01-03 A
PersonID ArrivalDate Jan-01 Jan-02 Jan-03
123456 2012-01-01 F NULL NULL
213415 2012-01-03 NULL NULL A
The system is for checks, between 1 to 15 days but no more than that. Any ideas would be very much appreciated.
you can try this, but I'm not sure if this is what you need,
with inputs_
as (select 123456 person_id, to_date('2012-01-01', 'YYYY-MM-DD') date_, 'F' check_
from dual
union all
select 213415 person_id, to_date('2012-01-03', 'YYYY-MM-DD'), 'A' check_
from dual
UNION ALL
select 123456 person_id, to_date('2012-01-01', 'YYYY-MM-DD') date_, 'F' check_
from dual
union all
select 213415 person_id, to_date('2012-01-03', 'YYYY-MM-DD'), 'A' check_
from dual
union all
select 123456 person_id, to_date('2012-01-04', 'YYYY-MM-DD') date_, 'F' check_
from dual
union all
select 213415 person_id, to_date('2012-01-05', 'YYYY-MM-DD'), 'A' check_
from dual
union all
select 123456 person_id, to_date('2012-01-02', 'YYYY-MM-DD') date_, 'A' check_
from dual
union all
select 213415 person_id, to_date('2012-01-04', 'YYYY-MM-DD'), 'A' check_
from dual
UNION ALL
select 213415 person_id, to_date('2012-01-02', 'YYYY-MM-DD'), 'F' check_
from dual)
select *
from (select person_id, date_ arrival_date, check_, TO_CHAR(date_, 'DD-MON') date_
from inputs_)
pivot (min(check_) for date_ in ('01-JAN', '02-JAN', '03-JAN', '04-JAN', '05-JAN')
)
order by 2;
Output:
Also this is not dynamic, so if you want dynamic pivot, you can see this link, Dynamic pivot in oracle sql
You could construct dynamic date values for PIVOT's IN clause for the range min date to max date using a query. Then open a dynamic cursor with the required arguments in the IN clause of PIVOT.
DBMS_SQL.RETURN_RESULT ( 12c and later ) will display the desired result.
For older versions, you may refer my answer here to easily display the cursor's output: Display result
DECLARE
v_instring VARCHAR2 (1000);
v_cur SYS_REFCURSOR;
BEGIN
WITH dt ( min_t ,max_t ) AS
( SELECT MIN(Date_t) ,MAX(Date_t) FROM TableA
) ,
datevalues (date_ch) AS
(SELECT TO_CHAR(min_t + lvl - 1, 'DD-MON')
FROM dt CROSS APPLY
( SELECT LEVEL lvl FROM DUAL CONNECT BY LEVEL <= max_t - min_t + 1
)
)
SELECT LISTAGG(''''
|| date_ch
|| ''' AS "'
|| date_ch, '",') WITHIN GROUP (
ORDER BY date_ch )||'"'
INTO v_instring
FROM
( SELECT DISTINCT date_ch FROM datevalues
);
OPEN v_cur FOR 'select * from (select PersonID, date_t arrival_date, check_t,
TO_CHAR(date_t, ''DD-MON'') date_t from TableA) pivot ( min(check_t)
for date_t in ('||v_instring||')) ORDER BY arrival_date';
DBMS_SQL.RETURN_RESULT(v_cur);
END;
/
PERSONID ARRIVAL_DATE 01-JAN 02-JAN 03-JAN 04-JAN 05-JAN 06-JAN
------------- --------------- ------ ------ ------ ------ ------ ------
123456 01-01-12 F
213415 03-01-12 A
213416 04-01-12 F
345677 06-01-12 A

Oracle - Finding the NEXT largest number

Say I have a list of dates & prices:
20170322 109.89
20170321 107.02
20170320 109.25
20170317 108.44
20170316 108.53
20170315 107.94
20170314 106.83
20170313 110.02
20170310 107.31
20170309 107.54
20170308 107.67
20170307 108.98
What I need is, from the most recent date: 20170322 (109.89), what is the FIRST date / price value that is HIGHER than the original value, which is 20170313 (110.02). Note these are in DESC order of the date
Been at this ALL day.
Assuming the columns are called DT and PRICE, and assuming there is only one "thing" whose price you monitor (otherwise you would need a GROUP BY clause):
select min(dt) as dt, min(price) keep (dense_rank first order by dt) as price
from your_table
where price > ( select min(price) keep (dense_rank first order by dt)
from your_table
)
https://docs.oracle.com/cd/B19306_01/server.102/b14200/functions056.htm
Solution using the MATCH_RECOGNIZE clause (requires Oracle 12 and above).
I created the test data in a WITH clause. That is NOT PART OF THE SOLUTION; the SQL query begins after the WITH clause, at SELECT TICKER, ....
The question mark in the reluctant match in the PATTERN trips the JDBC driver, so this query can't be run from SQL Developer; it needs to be run in SQL*Plus or similar front-end. (The workaround is to change b*? to b* and to add to the DEFINE clause: b as b.price <= a.price.)
To illustrate more of the flexibility of MATCH_RECOGNIZE, I assumed there may be several "tickers", each with its inception date (earliest date with a price), and the query looks for the first occurrence of a price higher than the original one, per ticker.
with
test_data ( ticker, dt, price ) as (
select 'XYZ', to_date('20170322', 'yyyymmdd'), 109.89 from dual union all
select 'XYZ', to_date('20170321', 'yyyymmdd'), 107.02 from dual union all
select 'XYZ', to_date('20170320', 'yyyymmdd'), 109.25 from dual union all
select 'XYZ', to_date('20170317', 'yyyymmdd'), 108.44 from dual union all
select 'XYZ', to_date('20170316', 'yyyymmdd'), 108.53 from dual union all
select 'XYZ', to_date('20170315', 'yyyymmdd'), 107.94 from dual union all
select 'XYZ', to_date('20170314', 'yyyymmdd'), 106.83 from dual union all
select 'XYZ', to_date('20170313', 'yyyymmdd'), 110.02 from dual union all
select 'XYZ', to_date('20170310', 'yyyymmdd'), 107.31 from dual union all
select 'XYZ', to_date('20170309', 'yyyymmdd'), 107.54 from dual union all
select 'XYZ', to_date('20170308', 'yyyymmdd'), 107.67 from dual union all
select 'XYZ', to_date('20170307', 'yyyymmdd'), 108.98 from dual
)
select ticker, dt, price
from test_data
match_recognize (
partition by ticker
order by dt
measures c.dt as dt, c.price as price
one row per match
pattern ( ^ a b*? c )
define c as c.price > a.price
)
;
TICKER DT PRICE
------ ---------- -------
XYZ 2017-03-13 110.02
1 row selected.

Select Maximum record

Here is my Table EMP_EARN_DETAILS.
Emp_Ern_No is the primary key.
I need to get the amount for each emp_no for each earn_no where the emp_earn_no is the maximum.
The output should be as follows.
0004321 ERN001 2345 11
0004321 ERN002 345 10
0004321 ERN003 345 9
000507 ER-01 563 4
000732 ERN001 2345 12
000732 ERN002 9 13
000732 ERN003 678 8
Please help me with the query
You can aggregate by the fields you need and, at the same time, order by the EMP_EARN_NO value; this can be a solution, by analytic functions:
WITH TEST(emp_no, earn_no, amount, emp_earn_no) AS
(
SELECT '0004321' , 'ERN001' ,2345 ,11 FROM DUAL UNION ALL
SELECT '0004321' , 'ERN002' ,345 , 10 FROM DUAL UNION ALL
SELECT '0004321' , 'ERN003' ,345 ,9 FROM DUAL UNION ALL
SELECT '000507' , 'ER-01' ,56 ,1 FROM DUAL UNION ALL
SELECT '000507' , 'ER-01' ,563 , 2 FROM DUAL UNION ALL
SELECT '000507' , 'ER-01' ,563 ,3 FROM DUAL UNION ALL
SELECT '000507' , 'ER-01' ,563 ,4 FROM DUAL UNION ALL
SELECT '00732' , 'ERN001' ,123 ,7 FROM DUAL UNION ALL
SELECT '00732' , 'ERN001' ,2345 ,12 FROM DUAL UNION ALL
SELECT '00732' , 'ERN002' ,9 ,13 FROM DUAL UNION ALL
SELECT '00732' , 'ERN003' ,67 ,5 FROM DUAL UNION ALL
SELECT '00732' , 'ERN003' ,456 ,6 FROM DUAL UNION ALL
SELECT '00732' , 'ERN003' ,678 ,8 FROM DUAL
)
SELECT emp_no, earn_no, amount, emp_earn_no
FROM (
SELECT emp_no,
earn_no,
amount,
emp_earn_no, ROW_NUMBER() OVER ( PARTITION BY EMP_NO, EARN_NO ORDER BY emp_earn_no DESC) AS ROW_NUM
FROM TEST
)
WHERE ROW_NUM = 1
Give this a shot,
SELECT EMP_NO, SUM(AMOUNT)
FROM EMP_EARN_DETAILS
GROUP BY EMP_NO
HAVING EMP_EARN_NO = MAX(EMP_EARN_NO)
Try this query:
select emp_no, earn_no,
sum(amount) keep (dense_rank last order by emp_earn_no) as sum_amount
from emp_earn_details
group by emp_no, earn_no
First by following query , your conditions achieved :
select t.emp_no a ,t.earn_no b ,max(t.amount) c
from EMP_EARN_DETAILS t
group by t.emp_no,t.earn_no
order by t.emp_no
Only things that you must specify , in a same record with different EMP_EARN_NO. You have to specify in same record which must be in result.
So if you want maximum EMP_EARN_NO be in result you can use following query as final query (exactly your target in question):
select t.emp_no a ,t.earn_no b ,max(t.amount) c, max(t.emp_earn_no) emp_earn_no
from EMP_EARN_DETAILS t
group by t.emp_no,t.earn_no
order by t.emp_no
If you want minimum or others EMP_EARN_NO be in result you can above query replace max function by your conditions.

Resources