In oracle SQL DB same primary Id is present more then once with different batch_id. How can I know the batch ID just before the current batch ID - oracle

I am working on oracle database.
We load customer data in source table which eventually migrates to target table.
Every time customer data is loaded in source table it is having a unique batch_id.
If we want to update some field in customer table, then we again load the same customer in source table but this time with different batch_id.
Now I want to know batch_id of the customer just before the latest batch_id.
Batch_id we take is usually the current date.

Use ROW_NUMBER analytic function
your sample data
select * from tab
order by 1,2
CUSTOMER_ID BATCH_ID
----------- -------------------
1 09.12.2019 00:00:00
1 10.12.2019 00:00:00
2 10.12.2019 00:00:00
Row_number assihns sequence number starting from 1 for each customer order descending on BATCH_ID - you are interested on one before the latest, i.e. the rows with the number 2.
with cust as (
select
customer_id, batch_id,
row_number() over (partition by customer_id order by batch_id desc) rn
from tab)
select CUSTOMER_ID, BATCH_ID
from cust
where rn = 2;
CUSTOMER_ID BATCH_ID
----------- -------------------
1 09.12.2019 00:00:00

It seems that you're basically looking for the second biggest value in the SOURCE table.
In this example code the SOURCE_TABLE represents the table containing same CUSTOMER_NO with different BATCH_NO:
create table source_table (customer_no integer, batch_no date);
insert into source_table values ('1', SYSDATE-2);
insert into source_table values ('1', SYSDATE-1);
insert into source_table values ('1', SYSDATE);
SELECT batch_no
FROM (
SELECT batch_no, row_number() over (order by batch_no desc) as row_num
FROM source_table
) t
WHERE row_num = 2
Where row_num = 2 represents the second biggest value in the table.
The query returns SYSDATE-1.

Related

Okay so I am trying to a rownum into a variable but I need it to give me only one value, so 2 if it's the second number in the row

select rownum into v_rownum
from waitlist
where p_callnum=callnum
order by sysdate;
tried doing this but gives too many values.
and if I do p_snum=snum, it will keep returning 1. I need it to return 2 if it's #2 on the waitlist.
select rn into v_rownum
from (select callnum,
row_number() over (order by sysdate) rn
from waitlist)
where p_snum=snum;
Almost got it to work. Running into issues in the first select. I believe I might have to use v_count instead. Also Ordering by Sysdate even if a second apart will order it correctly.
SNU CALLNUM TIME
--- ---------- ---------
101 10125 11-DEC-18
103 10125 11-DEC-18
BTW time is = date which I entered people into waitlist using sysdate. So I suppose ordering by time could work.
create table waitlist(
snum varchar2(3),
callnum number(8),
time date,
constraint fk_waitlist_snum foreign key(snum) references students(snum),
constraint fk_waitlist_callnum foreign key(callnum) references schclasses(callnum),
primary key(snum,callnum)
);
is the waitlist table.
I used Scott's DEPT table to create your WAITLIST; department numbers represent CALLNUM column:
SQL> select * From waitlist;
CALLNUM WAITER
---------- --------------------
10 ACCOUNTING
20 RESEARCH
30 SALES
40 OPERATIONS
How to fetch data you need?
using analytic function (ROW_NUMBER) which orders values by CALLNUMs, you'll know the order
that query will be used as an inline view for the main query that returns number in the waitlist for any CALLNUM
Here's how:
SQL> select rn
2 from (select callnum,
3 row_number() over (order by callnum) rn
4 from waitlist
5 )
6 where callnum = 30;
RN
----------
3
SQL>
rownum in oracle is a generated column, it does not refer to any specific row, it is just the nth row in a set.
With a select into it can only return one row (hence the two many rows error) so rownum will always be 1.
Without more details about your table structure, and how you are uniquely identifying records it is hard to give assist you further with a solution.

Query taking long when i use user defined function with order by in oracle select

I have a function, which will get greatest of three dates from the table.
create or replace FUNCTION fn_max_date_val(
pi_user_id IN number)
RETURN DATE
IS
l_modified_dt DATE;
l_mod1_dt DATE;
l_mod2_dt DATE;
ret_user_id DATE;
BEGIN
SELECT MAX(last_modified_dt)
INTO l_modified_dt
FROM table1
WHERE id = pi_user_id;
-- this table contains a million records
SELECT nvl(MAX(last_modified_ts),sysdate-90)
INTO l_mod1_dt
FROM table2
WHERE table2_id=pi_user_id;
-- this table contains clob data, 800 000 records, the table 3 does not have user_id and has to fetched from table 2, as shown below
SELECT nvl(MAX(last_modified_dt),sysdate-90)
INTO l_mod2_dt
FROM table3
WHERE table2_id IN
(SELECT id FROM table2 WHERE table2_id=pi_user_id
);
execute immediate 'select greatest('''||l_modified_dt||''','''||l_mod1_dt||''','''||l_mod2_dt||''') from dual' into ret_user_id;
RETURN ret_user_id;
EXCEPTION
WHEN OTHERS THEN
return SYSDATE;
END;
this function works perfectly fine and executes within a second.
-- random user_id , just to test the functionality
SELECT fn_max_date_val(100) as max_date FROM DUAL
MAX_DATE
--------
27-02-14
For reference purpose i have used the table name as table1,table2 and table3 but my business case is similar to what i stated below.
I need to get the details of the table1 along with the highest modified date among the three tables.
I did something like this.
SELECT a.id,a.name,a.value,fn_max_date_val(id) as max_date
FROM table1 a where status_id ='Active';
The above query execute perfectly fine and got result in millisecods. But the problem came when i tried to use order by.
SELECT a.id,a.name,a.value,a.status_id,last_modified_dt,fn_max_date_val(id) as max_date
FROM table1 where status_id ='Active' a
order by status_id desc,last_modified_dt desc ;
-- It took almost 300 seconds to complete
I tried using index also all the values of the status_id and last_modified, but no luck. Can this be done in a right way?
How about if your query is like this?
select a.*, fn_max_date_val(id) as max_date
from
(SELECT a.id,a.name,a.value,a.status_id,last_modified_dt
FROM table1 where status_id ='Active' a
order by status_id desc,last_modified_dt desc) a;
What if you don't use the function and do something like this:
SELECT a.id,a.name,a.value,a.status_id,last_modified_dt x.max_date
FROM table1 a
(
select max(max_date) as max_date
from (
SELECT MAX(last_modified_dt) as max_date
FROM table1 t1
WHERE t1.id = a.id
union
SELECT nvl(MAX(last_modified_ts),sysdate-90) as max_date
FROM table2 t2
WHERE t2.table2_id=a.id
...
) y
) x
where a.status_id ='Active'
order by status_id desc,last_modified_dt desc;
Syntax might contain errors, but something like that + the third table in the derived table too.

Oracle join rows in order where order is defined differently on each table

I have two tables
TABLE_A with columns project_id, id and load_date
and TABLE_B with columns project_id, delete_flag and delete_date
where TABLE_A.load_date is a new column and I want to populate it based on TABLE_B.delete_date for historic data. Basically, a file has been repeatedly loaded into the system and historically we didn't keep track of when it was loaded. However, each time the file is re-loaded, the previous version of it is updated in TABLE_B with a delete_date (i.e. a soft delete). The previous version just stays in TABLE_A without any changes.
I would like to populate TABLE_A.load_date based on matching projects in TABLE_B. The oldest row in TABLE_A (smallest TABLE_A.id) matches the oldest row in TABLE_B (oldest delete_date), etc. So the rows should match up if you keep picking the next one in order from each table. But I don't know how to turn that into an Oracle statement. What I've got so far is this which doesn't deal with matching on row order:
MERGE INTO TABLE_A a
USING
(
SELECT PROJECT_ID, DELETE_DATE
FROM TABLE_B
WHERE DELETE_FLAG = 'Y'
ORDER BY DELETE_DATE ASC
) b ON (a.PROJECT_ID = b.PROJECT_ID)
WHEN MATCHED THEN UPDATE
SET a.LOAD_DATE = p.DELETE_DATE;
This merge should do the work, as far as I properly understood your criteria:
merge into table_a ta
using (
select pid project_id, id, delete_date
from (
select project_id pid, id,
row_number() over (partition by project_id order by id) rn
from table_a) a
join (
select project_id pid, delete_date,
row_number() over (partition by project_id order by delete_date ) rn
from table_b
where delete_flag='Y') b using (pid, rn) ) tb
on (ta.project_id = tb.project_id and ta.id = tb.id)
when matched then update
set ta.load_date = tb.delete_date

Retrieving a row number of a specific row from a group of rows in a table in Oracle 10g

I have a table named USER_ROLES in Oracle 10g as follows.
USER_ROLE_ID USER_ID AUTHORITY
28 2 ROLE_ADMIN
45 3 ROLE_USER
61 3 ROLE_ASSISTANT
27 3 ROLE_SUPERVISOR
Where USER_ID is a foreign key of the table USER_TABLE. I need to know a row number of a specific row from a group of rows associated with a specific user (USER_ID) (and not based on all the rows in this table).
The following SQL retrieves the row number for a given USER_ROLE_ID (28 in this case) based on all the rows in this table.
select row_num from
(select row_number() over (order by user_role_id desc) as row_num, user_role_id
from user_roles order by user_role_id desc)
where user_role_id=28
It displays 3, in this case.
But I need to retrieve the row number from a group of rows which is associated only with a particular user. For this to be so, if I try the following SQL,
select row_num from
(select row_number() over (order by user_role_id desc) as row_num, user_role_id
from user_roles order by user_role_id desc)
where user_id=2 and user_role_id=28
In this case, I have modified the WHERE clause from
where user_role_id=28
to
where user_id=2 and user_role_id=28
It gives the following error.
ORA-00904: "USER_ID": invalid identifier
So, what is the way to fetch a row number from only those rows which are associated with a specific user, in this scenario?
just add the user_id to the inner query. i think you also want to partition that analytic if you want the row_num to be per user_id (if not ignore that bit):
select row_num
from (select row_number() over (partition by user_id
order by user_role_id desc) as row_num,
user_role_id, user_id
from user_roles)
where user_role_id=28 and user_id = 2;

ORACLE: How to select previous different value?

I have table that stores employee job name, it has the following columns:
id; date_from; date_to; emp_id; jobname_id; grade;
Each emp_id can have many consecutive records with the same jobname_id due to many grade changes.
How can I select previous different jobname_id omitting those that are the same like the most current one?
This solution uses the FIRST_VALUE() analytic function to identify each employee's current job. It then filters for all the jobs which dfon't match that one:
select distinct id
, jobname_id
from ( select id
, jobname_id
, first_value(jobname_id) over (partition by id
order by from_date desc) as current_job
from employee
where emp_id = 1234 )
where jobname_id != current_job
order by id, jobname_id
/
Will this work for your issue:
SELECT DISTINCT
e1.emp_id,
e1.jobname_id
FROM employee e1
WHERE NOT EXISTS
(SELECT 1
FROM employee e2
WHERE e1.emp_id = e2.emp_id
AND SYSDATE BETWEEN e2.date_from
AND NVL(e2.date_to, SYSDATE + 1));
(This asumes your table is named "employee" and emp_id is the PK value).
It selects unique emp_id, jobname_id values where the emp_id, jobname_id values are not current.
EDIT: I agree with Chin Boon that fundamentally this is a design issue and perhaps that should be addressed rather than working around the problem.

Resources