Deleting a single duplicate row in SQL - sql

The image below is an example of what my table looks like. I have duplicate rows, but a unique feature date_scanned. What I am trying to do is remove a single row by specifying the id, but when I do that all the rows with that id are removed and postgres does not let you use LIMIT after DELETE.
The below works when I remove WHERE id = 'F284', but will remove any row with rn =1. I need to specify the row id
DELETE FROM allergen_list WHERE date_scanned IN(
WITH cte AS (
SELECT date_scanned, row_number() OVER(PARTITION BY id, lot, expiration_date ORDER BY date_scanned DESC) as rn
FROM allergen_list WHERE id = 'F284'
)
SELECT date_scanned FROM cte WHERE rn = 1;
https://i.stack.imgur.com/Zf18E.png
Any help?

Why not just delete all rows where date is less than max?
DELETE FROM allergen_list
WHERE
id = 'F284' AND date_scanned < (
SELECT MAX(date_scanned)
FROM allergen_list WHERE id = 'F284'
)

I think Caius has the right approach. However, the logic would appear to be:
DELETE FROM allergen_list al
WHERE al.id = 'F284' AND
al.date_scanned < (SELECT MAX(al2.date_scanned)
FROM allergen_list al2
WHERE al2.id = al.id and al2.lot = al.lot and al2.expiration_date = al.expiration_date
);

Related

SQL - delete record where sum = 0

I have a table which has below values:
If Sum of values = 0 with same ID I want to delete them from the table. So result should look like this:
The code I have:
DELETE FROM tmp_table
WHERE ID in
(SELECT ID
FROM tmp_table WITH(NOLOCK)
GROUP BY ID
HAVING SUM(value) = 0)
Only deletes rows with ID = 2.
UPD: Including additional example:
Rows in yellow needs to be deleted
Your query is working correctly because the only group to total zero is id 2, the others have sub-groups which total zero (such as the first two with id 1) but the total for all those records is -3.
What you're wanting is a much more complex algorithm to do "bin packing" in order to remove the sub groups which sum to zero.
You can do what you want using window functions -- by enumerating the values for each id. Taking your approach using a subquery:
with t as (
select t.*,
row_number() over (partition by id, value order by id) as seqnum
from tmp_table t
)
delete from t
where exists (select 1
from t t2
where t2.id = t.id and t2.value = - t.value and t2.seqnum = t.seqnum
);
You can also do this with a second layer of window functions:
with t as (
select t.*,
row_number() over (partition by id, value order by id) as seqnum
from tmp_table t
),
tt as (
select t.*, count(*) over (partition by id, abs(value), seqnum) as cnt
from t
)
delete from tt
where cnt = 2;

Deleting specific rows - SQLite

I'm trying to delete duplicate rows from my table 'exchange_transactions' associated with the surgeon name 'Lucille Torres' using a cte. The transaction_id column should be unique but is duplicated in this case hence the attempt to delete them. I tried this code but it doesn't seem to work. Replacing 'DELETE' with 'SELECT *' shows me all the rows I want to delete. What am I doing wrong?
WITH cte AS (
SELECT
transaction_id,
surgeon,
ROW_NUMBER() OVER (
PARTITION BY
transaction_id
) row_num
FROM exchange_transactions)
DELETE FROM cte
WHERE surgeon = 'Lucille Torres' AND row_num > 1
Use the column ROWID to get the minimum value for each transaction_id that you will not delete:
delete from exchange_transactions
where surgeon = 'Lucille Torres'
and exists (
select 1 from exchange_transactions t
where t.surgeon = exchange_transactions.surgeon
and t.transaction_id = exchange_transactions.transaction_id
and t.rowid < exchange_transactions.rowid
)
Deleting directly from a CTE won't work in SqLite.
But if that table has a primary key (f.e. id)
then the result of the CTE can be used in the delete.
For example:
WITH CTE_DUPS AS
(
SELECT id,
ROW_NUMBER() OVER (
PARTITION BY surgeon, transaction_id
ORDER BY id) AS rn
FROM exchange_transactions
WHERE surgeon = 'Lucille Torres'
)
DELETE
FROM exchange_transactions
WHERE id IN (select id from CTE_DUPS where rn > 1)
Test on db<>fiddle here

Delete Distinct column and latest date of other column

I have a table where the primary key is a composite key of ID and date. Is there a way that I can delete a single row where ID matches and the date is the latest date?
I am new to SQL, so I have tried a few things, but I either don't get the results I am looking for or cant get the syntax correct
DELETE FROM Master
WHERE ((Identifier = 'SomeID')
AND (EffectiveDate = MAX(EffectiveDate));
There are multiple columns with the same ID, but different dates, ie.
ID EffectiveDate
-------------------------
A '2019-09-18'
A '2019-09-17'
A '2019-09-16'
Is there a way I can delete only the row with A | '2019-09-18'?
You can use window functions and an updatable CTE:
with todelete as (
select t.*, row_number() over (partition by id order by effective_date desc) as seqnum
from t
)
delete from todelete
where seqnum = 1;
Note: If you want to limit this to a single id, then be sure to include a where id = 'a' in either the subquery or outer query.
use row_number()
delete from (select *, row_number() over(partition by id order by effectivedate desc) rn from table_name
) a where a.rn=1
A correlated subquery might get the job done:
DELETE FROM Master
WHERE
Identifier = 'SomeID'
AND EffectiveDate = (
SELECT MAX(EffectiveDate) FROM Master WHERE Identifier = 'SomeID'
)
;
Use the CTE Function to Delete the Row but the below Query will not delete the Record of Max Date of those ID's where Single Record exist against that.
with todelete as (
select t.*, row_number() over (partition by id order by effective_date desc) as seqnum
from t
)
delete from todelete
where seqnum = 1 and id in(select distinct id from todelete where seqnum<>1)
With correlated subquery for all IDs:
delete table1
from table1 t1
where t1.EffectiveDate =
(
select max(t2.EffectiveDate)
from table1 t2
where t2.ID = t1.ID
)

SQL update last occurrence

I have a simple SELECT query that works fine and returns one row, which is the last occurrence of a specific value in order_id column. I want to update this row. However, I cannot combine this SELECT query with the UPDATE query.
This is the working query that returns one row, which I want to update:
SELECT *
FROM (
SELECT *,
ROW_NUMBER() OVER(PARTITION BY order_id
ORDER BY start_hour DESC) rn
FROM general_report
WHERE order_id = 16836
) q
WHERE rn = 1
And I tried many combinations to update the row returned by this statement. For example, I tried to remove SELECT *, and update the table q as in the following, but it didn't work telling me that relation q does not exist.
UPDATE q
SET q.cost = 550.01685
FROM (
SELECT *,
ROW_NUMBER() OVER(PARTITION BY order_id
ORDER BY start_hour DESC) rn
FROM general_report
WHERE order_id = 16836
) q
WHERE rn = 1
How can I combine these codes with a correct UPDATE syntax? In case needed, I test my codes at SQL Manager for PostgreSQL.
Try something like this. I am not sure on PostgreSQL syntax:
UPDATE general_report AS d
SET cost = 550.01685
FROM (
SELECT *
FROM (
SELECT *,
ROW_NUMBER() OVER(PARTITION BY order_id
ORDER BY start_hour DESC) rn
FROM general_report
WHERE order_id = 16836
) q
WHERE rn = 1
) s
WHERE d.id = s.id
Ana alternative method for update the most recent record is to use NOT EXISTS (even more recent):
UPDATE general_report dst
SET cost = 550.01685
WHERE order_id = 16836
AND NOT EXISTS (
SELECT *
FROM general_report nx
WHERE nx.order_id = dst.order_id
AND nx.start_hour > dst.start_hour
);
Test below query
UPDATE q
SET q.cost = 550.01685
where id in
(select id from
(
SELECT *
ROW_NUMBER() OVER(PARTITION BY order_id
ORDER BY start_hour DESC) rn
FROM general_report
WHERE order_id = 16836
) q
WHERE rn = 1)

oracle sql wih rownum <=

why below query is not giving results if I remove the < sign from query.Because even without < it must match with results?
Query used to get second max id value:
select min(id)
from(
select distinct id
from student
order by id desc
)
where rownum <=2
student id
1
2
3
4
Rownum has a special meaning in Oracle. It is increased with every row, but the optimizer knows that is increasing continuously and all consecutive rows must met the rownum condition. So if you specify rownum = 2 it will never occur since the first row is already rejected.
You can see this very nice if you do an explain plan on your query. It will show something like:
Plan for rownum <=:
COUNT STOPKEY
Plan for rownum =:
FILTER
A ROWNUM value is not assigned permanently to a row (this is a common misconception). A row in a table does not have a number; you cannot ask for row 2 or 3 from a table
click Here for more Info.
This is from the link provided:
Also confusing to many people is when a ROWNUM value is actually assigned. A ROWNUM value is assigned to a row after it passes the predicate phase of the query but before the query does any sorting or aggregation. Also, a ROWNUM value is incremented only after it is assigned, which is why the following query will never return a row:
select *
from t
where ROWNUM > 1;
Because ROWNUM > 1 is not true for the first row, ROWNUM does not advance to 2. Hence, no ROWNUM value ever gets to be greater than 1. Consider a query with this structure:
select ..., ROWNUM
from t
where <where clause>
group by <columns>
having <having clause>
order by <columns>;
I think this is the query you are looking for:
select id
from (select distinct id
from student
order by id desc
) t
where rownum <= 2;
Oracle processes the rownum before the order by, so you need a subquery to get the first two rows. The min() was forcing an aggregation that returned only one result, but before the rownum was applied.
If you actually want only the second value, you need an additional layer of subqueries:
select min(id)
from (select id
from (select distinct id
from student
order by id desc
) t
where rownum <= 2
) t;
However, I would do:
select id
from (select id, dense_rank() over (order by id) as seqnum
from student
) t
where seqnum = 2;
Order asc instead of desc
select id from student where rownum <=2 order by id asc;
Why not just use
select id
from ( select distinct id
, row_number() over (order by id desc) x
from student
)
where x = 2
Or even really bad. Getting the count and index :)
select id
from ( select id
, row_number() over (order by id desc) idx
, sum(1) over (order by null) cnt
from student
group
by id
)
where idx = cnt - 1 -- get the pre-last
Or
where idx = cnt - 2 -- get the 2nd-last
Or
where idx = 3 -- get the 3rd
Try this
SELECT *
FROM (
SELECT id, row_number() over (order by id asc) row_num
FROM student
) AS T
WHERE row_num = 2 -- or 3 ... n
ROW_NUMBER