Delete duplicated record - sql

I have a table which contains a lot of duplicated rows like this:
id_emp id date ch_in ch_out
1 34103 2019-09-01
1 34193 2019-09-01 17:00
1 34194 2019-09-02 07:03:21 16:59:26
1 34104 2019-09-02 07:03:21 16:59:26
1 33361 2019-09-02 NULL NULL
I want just one row for each date and others must delete with condition like I want the output must be:
id_emp id date ch_in ch_out
1 34193 2019-09-01 17:00
1 34104 2019-09-02 07:03:21 16:59:26
I tried to use distinct but nothing working:
select distinct id_emp, id, date_1, ch_in,ch_out
from ch_inout
where id_emp=1 order by date_1 asc
And I tried too using this query to delete:
select *
from (
select *, rn=row_number() over (partition by date_1 order by id)
from ch_inout
) x
where rn > 1;
But nothing is working the result is empty.

You can use aggregation:
select id_emp, max(id) as id, date, min(ch_in), max(ch_out)
from ch_inout
group by id_emp, date;
This returns the maximum id for each group of rows. That is not exactly what is returned in the question, but you don't specify the logic.
EDIT:
If you want to delete all but the largest id for each id_emp/date combination, you can use:
delete c from ch_inout c
where id < (select max(c2.id)
from ch_inout c2
where c2.id_emp = c.id_emp and c2.date = c.date
);

You can use ROW_NUMBER() to identify the records you want to delete. Assuming that you want to keep the record with the lowest id on each date:
SELECT *
FROM (
SELECT
t.*,
ROW_NUMBER() OVER(PARTITION BY date ORDER BY id) rn
FROM ch_inout t
) x
WHERE rn > 1
You can easily turn this into a DELETE statement:
WITH cte AS (
SELECT
t.*,
ROW_NUMBER() OVER(PARTITION BY date ORDER BY id) rn
FROM ch_inout t
)
DELETE FROM cte WHERE rn > 1

Related

Get most recent measurement

I have a table that has has some measurements, ID and date.
The table is built like so
ID DATE M1 M2
1 2020 1 NULL
1 2020 NULL 15
1 2018 2 NULL
2 2019 1 NULL
2 2019 NULL 1
I would like to end up with a table that has one row per ID with the most recent measurement
ID M1 M2
1 1 15
2 1 1
Any ideas?
You can use correlated sub-query with aggregation :
select id, max(m1), max(m2)
from t
where t.date = (select max(t1.date) from t t1 where t1.id = t.id)
group by id;
Use ROW_NUMBER combined with an aggregation:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY ID ORDER BY DATE DESC) rn
FROM yourTable
)
SELECT ID, MAX(M1) AS M1, MAX(M2) AS M2
FROM cte
WHERE rn = 1
GROUP BY ID;
The row number lets us restrict to only records for each ID having the most recent year date. Then, we aggregate to find the max values for M1 and M2.
In standard SQL, you can use lag(ignore nulls):
select id, coalesce(m1, prev_m1), coalesce(m2, prev_m2)
from (select t.*,
lag(m1 ignore nulls) over (partition by id order by date) as prev_m1,
lag(m2 ignore nulls) over (partition by id order by date) as prev_m2,
row_number() over (partition by id order by date desc) as seqnum
from t
) t
where seqnum = 1;

selecting set of second lowest values

I have two columns of interest ID and Deadline:
ID Deadline (DD/MM/YYYY)
1 01/01/2017
1 05/01/2017
1 04/01/2017
2 02/01/2017
2 03/01/2017
2 06/02/2017
2 08/03/2017
Each ID can have multiple (n) deadlines. I need to select all rows where the Deadline is second lowest for each individual ID.
Desired output:
ID Deadline (DD/MM/YYYY)
1 04/01/2017
2 03/01/2017
Selecting minimum can be done by:
select min(deadline) from XXX group by ID
but I am lost with "middle" values. I am using Rpostgresql, but any idea helps as well.
Thanks for your help
One way is to use ROW_NUMBER() window function
SELECT id, deadline
FROM (
SELECT *, ROW_NUMBER() OVER (PARTITION BY id ORDER BY deadline) rn
FROM xxx
) q
WHERE rn = 2 -- get only second lowest ones
or with LATERAL
SELECT t.*
FROM (
SELECT DISTINCT id FROM xxx
) i JOIN LATERAL (
SELECT *
FROM xxx
WHERE id = i.id
ORDER BY deadline
OFFSET 1 LIMIT 1
) t ON (TRUE)
Output:
id | deadline
----+------------
1 | 2017-04-01
2 | 2017-03-01
Here is a dbfiddle demo
Using ROW_NUMBER() after taking distinct records will eliminate the chance of getting the lowest date instead of second lowest if there are duplicate records.
select ID,Deadline
from (
select ID,
Deadline,
ROW_NUMBER() over(partition by ID order by Deadline) RowNum
from (select distinct ID, Deadline from SourceTable) T
) Tbl
where RowNum = 2

How to select unique records by ORACLE

When I perform "SELECT * FROM table" I got results like below:
ID Date Time Type
----------------------------------
60 03/03/2013 8:55:00 AM 1
60 03/03/2013 2:10:00 PM 2
110 17/03/2013 9:15:00 AM 1
67 24/03/2013 9:00:00 AM 1
67 24/03/2013 3:05:00 PM 2
as you see each ID has a transaction Type 1 and 2 in the same Date
except ID 110 HAS only Type 1
So how could I just get result like this:
ID Date Time Type
----------------------------------
110 17/03/2013 9:15:00 AM 1
as only one record are returned from the first result
Change the partition definition (partition by id,date) according to your needs
select *
from (select t.*
,count(*) over (partition by id,date) as cnt
from mytable t
) t
where t.cnt = 1
;
You can use this:
select * from my_table t
where exists (
select 1 from my_table
where id = t.id
group by id
having count(*) = 1
)
If you want only type 1, then compare the minimum and maximum values. I prefer window functions:
select t.*
from (select t.*, min(type) over (partition by id) as mintype,
max(type) over (partition by id) as maxtype
from t
) t
where mintype = maxtype and mintype = 1;
If you want only records of the same type (and not specifically type = 1), then remove that condition.
If you want only records on the same day, then include the date in the partition by.
Under some circumstances, not exists can be faster:
select t.*
from t
where not exists (select 1 from t t2 where t2.id = t.id and t2.type <> 1);

Need to delete duplicate records from the table using row_number()

I am having a table test having data as follows and I want to delete the trsid 124 and I have millions entry in my DB it is just a scenarion. Concept is to delete the duplicate entry from the table
--------------------------------------------
TrsId | ID | Name |
--------------------------------------------
123 | 1 | ABC |
124 | 1 | ABC |
I am trying something like
delete from test
select T.* from
(
select ROW_NUMBER() over (partition by ID order by name) as r,
Trsid,
ID,
name
from test
) t
where r = 2
Even if I update the query which is Ok for me
update test set id=NULL
select T.* from
(
select ROW_NUMBER() over (partition by ID order by name) as r,
Trsid,
ID,
name
from test
) t
where r = 2
But if i run both this query it deletes all the records from table test. And if i update it update both the records.
I dont know what I am doing wrong here
WITH cte AS
(
SELECT ROW_NUMBER() OVER(PARTITION by ID ORDER BY name) AS Row
FROM test
)
DELETE FROM cte
WHERE Row > 1
Use the below query.
;WITH cte_1
AS (SELECT ROW_NUMBER() OVER(PARTITION BY ID,NAME ORDER BY TrsId ) Rno,*
FROM YourTable)
DELETE
FROM cte_1
WHERE RNO>1
WITH cte_DUP AS (
SELECT * FROM (
select <col1,col2,col3..coln>, row_number()
over(partition by <col1,col2,col3..coln>
order by <col1,col2,col3..coln> ) rownumber
from <your table> ) AB WHERE rownumber > 1)
DELETE FROM cte_DUP WHERE ROWNUMBER > 1
To find duplicate records we can write like below query,
;WITH dup_val
AS (SELECT a,
b,
Row_number()
OVER(
partition BY a, b
ORDER BY b, NAME)AS [RANK]
FROM table_name)
SELECT *
FROM dup_val
WHERE [rank] <> 1;

Comparing row values in oracle

I have Table1 with three columns:
Key | Date | Price
----------------------
1 | 26-May | 2
1 | 25-May | 2
1 | 24-May | 2
1 | 23 May | 3
1 | 22 May | 4
2 | 26-May | 2
2 | 25-May | 2
2 | 24-May | 2
2 | 23 May | 3
2 | 22 May | 4
I want to select the row where value 2 was last updated (24-May). The Date was sorted using RANK function.
I am not able to get the desired results. Any help will be appreciated.
SELECT *
FROM (SELECT key, DATE, price,
RANK() over (partition BY key order by DATE DESC) AS r2
FROM Table1 ORDER BY DATE DESC) temp;
Another way of looking at the problem is that you want to find the most recent record with a price different from the last price. Then you want the next record.
with lastprice as (
select t.*
from (select t.*
from table1 t
order by date desc
) t
where rownum = 1
)
select t.*
from (select t.*
from table1 t
where date > (select max(date)
from table1 t2
where t2.price <> (select price from lastprice)
)
order by date asc
) t
where rownum = 1;
This query looks complicated. But, it is structured so it can take advantage of indexes on table1(date). The subqueries are necessary in Oracle pre-12. In the most recent version, you can use fetch first 1 row only.
EDIT:
Another solution is to use lag() and find the most recent time when the value changed:
select t1.*
from (select t1.*
from (select t1.*,
lag(price) over (order by date) as prev_price
from table1 t1
) t1
where prev_price is null or prev_price <> price
order by date desc
) t1
where rownum = 1;
Under many circumstances, I would expect the first version to have better performance, because the only heavy work is done in the innermost subquery to get the max(date). This verson has to calculate the lag() as well as doing the order by. However, if performance is an issue, you should test on your data in your environment.
EDIT II:
My best guess is that you want this per key. Your original question says nothing about key, but:
select t1.*
from (select t1.*,
row_number() over (partition by key order by date desc) as seqnum
from (select t1.*,
lag(price) over (partition by key order by date) as prev_price
from table1 t1
) t1
where prev_price is null or prev_price <> price
order by date desc
) t1
where seqnum = 1;
You can try this:-
SELECT Date FROM Table1
WHERE Price = 2
AND PrimaryKey = (SELECT MAX(PrimaryKey) FROM Table1
WHERE Price = 2)
This is very similar to the second option by Gordon Linoff but introduces a second windowed function row_number() to locate the most recent row that changed the price. This will work for all or a range of keys.
select
*
from (
select
*
, row_number() over(partition by Key order by [date] DESC) rn
from (
select
*
, NVL(lag(Price) over(partition by Key order by [date] DESC),0) prevPrice
from table1
where Key IN (1,2,3,4,5) -- as an example
)
where Price <> prevPrice
)
where rn = 1
apologies but I haven't been able to test this at all.