Handle Duplicate Record in Oracle SQL - sql

I have a table with records like below
NAME STATUS xml_configparamdb_id xml_configuration_id
STO INACTIVE 1 1
STO ACTIVE 1 2
BOS ACTIVE 1 3
KYC INACTIVE 1 4
KYC INACTIVE 1 5
ACC ACTIVE 1 6
ACC ACTIVE 1 7
Now result I am interested in is as follows:
NAME STATUS xml_configparamdb_id xml_configuration_id
STO ACTIVE 1 2
BOS ACTIVE 1 3
KYC INACTIVE 1 4
ACC ACTIVE 1 6
That is, I want to select data on basis of STATUS .
Condition -- If STATUS is ACTIVE for both case of same Parameter - select first coming ACTIVE
Condition -- If STATUS is INACTIVE for both case of same Parameter - select first coming INACTIVE
Condition -- If STATUS is ACTIVE & INACTIVE for same Parameter - select ACTIVE
Now I used below query to populate result like above without using PRIMARY KEY Column (xml_configuration_id)
CURSOR cCnfgTypData IS
select distinct name, description, STATUS
from stg_xml_cpdb_configuration
WHERE process_exec_num = 1
AND STATUS = 'ACTIVE'
UNION ALL
select name, description, STATUS
from stg_xml_cpdb_configuration t
where process_exec_num = 1
and STATUS = 'INACTIVE'
and not exists (select * from stg_xml_cpdb_configuration
where name = t.name
and STATUS = 'ACTIVE') order by name, description;
It's showing fine data. But when I execute using PRIMARY KEY Column (xml_configuration_id) like below it's displaying all data without satisfying condition
select distinct name, description, STATUS, xml_configparamdb_id, xml_configuration_id
from stg_xml_cpdb_configuration
WHERE process_exec_num = 1
AND STATUS = 'ACTIVE'
UNION ALL
select name, description, STATUS, xml_configparamdb_id, xml_configuration_id
from stg_xml_cpdb_configuration t
where process_exec_num = 1
and STATUS = 'INACTIVE'
and not exists (select * from stg_xml_cpdb_configuration
where name = t.name
and STATUS = 'ACTIVE') order by name, description;

Use analytic function ROW_NUMBER.
SQL> SELECT name,
2 status,
3 xml_configparamdb_id,
4 xml_configuration_id
5 FROM
6 ( SELECT t.*, row_number() over (partition BY name order by status) rn FROM t
7 )
8 WHERE rn = 1
9 ORDER BY xml_configuration_id
10 /
NAM STATUS XML_CONFIGPARAMDB_ID XML_CONFIGURATION_ID
--- -------- -------------------- --------------------
STO ACTIVE 1 2
BOS ACTIVE 1 3
KYC INACTIVE 1 4
ACC ACTIVE 1 6
SQL>

Related

Return Max ID SQL

I have a table with several rows as mentioned below having same account id :
id account_id id_intern Name Active mobile_no landline_no email
1 0011abs 66654 A yes 098937888 098937888 a#gmail.com
2 0011abs 66655 B yes 098937666 098937666 b#gmail.com
3 0011abs 66656 C no 098937777 098937777 c#gmail.com
4 0011abs 66657 D yes 098937666 d#gmail.com
5 0011abs 66658 E yes 098937111 e#gmail.com
6 0011abs 66659 F yes 098937111 098937665
I am searching for script that can return me just one line for all the common account_id present in the table with the following condition:
For an account_id with several id_intern:
consider those lines with the status active 'yes',
then those lines with entered mobile_no (not empty),
then those lines with entered landline_no (not empty),
then those lines with entered email (not empty),
then if still we have several lines (in this case for users with name A and B)
then we will consider the line with max id_intern.
Expected Result :
id account_id id_intern Name active mobile_no landline_no email
2 0011abs 66655 B yes 098937666 098937666 b#gmail.com
I tried the script but i am unable to fulfil these conditions :(
Thanks in advance for your help.
Use row_number() window function:
select id, account_id, id_intern, Name, Active, mobile_no, landline_no, email
from (
select *, row_number() over (partition by account_id order by id_intern desc) rn
from tablename
where Active = 'yes'
and mobile_no is not null and landline_no is not null and email is not null
) t
where rn = 1
If you want 1 row for each account_id even if not all the conditions are satisfied, the you must use a conditional ORDER BY clause:
select id, account_id, id_intern, Name, Active, mobile_no, landline_no, email
from (
select *,
row_number() over (
partition by account_id
order by
case when Active = 'yes' then 1 else 2 end,
case when mobile_no is not null then 1 else 2 end,
case when landline_no is not null then 1 else 2 end,
case when email is not null then 1 else 2 end,
id_intern desc
) rn
from tablename
) t
where rn = 1

How i want make 3 rows of the same primary key into 1 row by checking and choose its value using case

For example here, from this table
key | status
1001 | A
1001 | D
1001 | C
the hierarchy will be C>D>A
If the the stats contain C as the value, the person status will become C in one row. It will be like this:
key | status
1001 | C
it will ignore D and A because it has value C. If it doesn't has C,then it will check for D first before A.
So, how can i do that? I dont how to make the 3 row into 1. I tried using
''' CASE WHEN STATUS IN('C')THEN 'C'
ELSE WHEN STATUS IN('D') THEN 'D'
ELSE WHEN STATUS IN('A') THEN 'A'
END AS STATUS '''
But it give error and still can't make the row into 1
You can try the below -
select key, status
from tablname
order by case when status='C' then 1 when status='D' then 2 when status='A' then 3 else 99 end
FETCH FIRST 1 ROW
You can use analytical function row_number as follows:
Select * from
(Select t.*,
Row_number() over (partition by key order by case when status = 'C' then 1
when status = 'D' then 2
when status = 'A' then 3 end) as rn
From your_table)
Where rn = 1;

SQL group by multiple fields get first occurrence

I have this table (sales_lines):
id sale_id sale_seq_id other_fields
----------------------------------------
1 1 1
2 1 2
3 2 1
4 3 1
5 3 2
But this table can have a duplicated sale_seq_id (yes, it's an error). Like this:
id sale_id sale_seq_id other_fields
----------------------------------------
1 1 1
2 1 2
3 1 2
4 2 1
5 3 1
6 3 1
7 3 2
Lines 3 and 6 are errors, so I should discard them.
How can I do it?
To delete the wrong records do
delete from sales_lines
where id not in
(
select min(id)
from sales_lines
group by sale_id, sale_seq_id
)
To just delete the correct data do
select min(id), sale_id, sale_seq_id
from sales_lines
group by sale_id, sale_seq_id
I would use correlated sub-query :
select sl.*
from sales_line sl
where sl.id = (select min(sl1.id)
from sales_line sl1
where sl1.sale_id = sl.sale_id and
sl1.sale_seq_id = sl.sale_seq_id
);
If your DBMS supports window function then you can do :
select sl.*
from (select sl.*,
row_number() over (partition by sl.sale_id, sl.sale_seq_id order by sl.id) as seq
from sales_line sl
) sl
where seq = 1;
By this way, you will get full row with other fields too.

Query the Most recent Record based on Status and CreatedDate

Table
Email Status CreatedDate(mm/dd/yyyy)
abc#gmail.com Open 10/02/2018
abc#gmail.com Closed 10/06/2018
abc#gmail.com Prospect 10/04/2018
xyz#gmail.com closed 10/21/2018
xyz#gmail.com closed 10/01/2018
123#mail.com Open 10/04/2018
123#gmail.com Open 10/03/2018
123#gmail.com closed 10/02/2018
123#gmail.com closed 10/01/2018
Output
abc#gmail.com Prospect 10/04/2018
xyz#gmail.com closed 10/21/2018
123#gmail.com open 10/04/2018
The above output is based on the following conditions:
Take the unique record of each email.
Condition 1 suppose"abc#gmail.com" has three records and if the
status = "prospect" found select that record irrespective to
createdDate and any other status.
Condition 2 Suppose "xyz#gmail.com" has two records and both are of
status = "Closed" than check the CreatedDate and select the latest
date.
Condition 3 Suppose "123#gmail.com" has four records and two of them are in open status and two are in Closed, we should take the open status and select based on the latest created date.
One method uses row_number() in a subquery:
select t.*
from (select t.*,
row_number() over (partition by email
order by (case when status = 'prospect' then 1
when status = 'open' then 2
when status = 'closed' then 3
else 4
end), CreatedDate desc
) as seqnum
from t
) t
where seqnum = 1;
You can also express this without the subquery:
select top (1) t.*
from t
order by row_number() over (partition by email
order by (case when status = 'prospect' then 1
when status = 'open' then 2
when status = 'closed' then 3
else 4
end), CreatedDate desc
)

How to get all columns when you do group by for multiple columns?

Here is my table
UserDetail
Id UserId CourseId SubjectId TeacherCode RDate status
1 1 1 1 1 08/02/2016 Waiting
2 1 1 1 2 08/01/2016 Recceived
3 1 1 1 3 08/02/2016 Processed
4 1 1 2 1 08/03/2016 Recceived
5 1 1 2 2 08/04/2016 Processed
6 1 2 1 3 08/05/2016 Processed
7 1 2 2 1 08/06/2016 Processed
User can have multiple courses,multiple subjects.One teacher can teach multiple subject.I want to fetch all column from table, base on distinct userid,courseid and subjectid.Out of 7 row,want to show only 4 rows.
Any one record from below
Id UserId CourseId SubjectId TeacherCode RDate status
1 1 1 1 1 08/02/2016 Waiting
2 1 1 1 2 08/01/2016 Recceived
3 1 1 1 3 08/02/2016 Processed
If we pick teachercode 1,then rdate to be 08/02/2016 and status to be Waiting
neither Recceived nor Processed.
Any one from below
Id UserId CourseId SubjectId TeacherCode RDate status
4 1 1 2 1 08/03/2016 Recceived
5 1 1 2 2 08/04/2016 Processed
How to do that?
You gotta know which rows of that distincted datas you want to choose. First rows ? Last rows ?
Since for every group of distincted data would be multiple rows so you mist choose which row you want.
I assume you want the first row from every distincted group. You can do it like below:
Select first(Id) AS Id, first(UserId) AS UserId, first(CrouseId) AS CourseId, first(SubjectId) AS SubjectId, first(TeacherId) AS TescherId,first(RDate) AS RDate, first(status) as status
FROM UserDetail
Group By UserId, CourseId, SubjectId
In sql2005+, do it like below:
WITH temp AS (
SELECT *,
ROW_NUMBER() OVER(PARTITION BY UserId,CourseId,SubjectId ORDER BY Id) AS rn FROM UserDetail)
SELECT t.*
FROM temp t
WHERE t.rn = 1
Or you can do it using inner select instead of WITH:
SELECT *
FROM (SELECT *,
ROW_NUMBER() OVER(PARTITION BY UserId,CourseId,SubjectId ORDER BY Id) AS rn FROM UserDetail)
WHERE rn = 1