MySQL: delete rows with "WHERE ... NOT IN" from only one single table - mysql

In a MySQL database I have a many-to-many relationship between two tables. For the sake of simplicity let's assume those tables map homes and their residents. I have a third table to map those relations (home_resident_relations). The latter table has an additional column datemodified that stores the date of the latest update of each row via triggers.
Now I want to get rid of all former residents for each home and only keep the current ones - that is those with the newest date.
I have already a working SELECT clause that will list me all old relations I want to delete:
SELECT * FROM `home_resident_relations` WHERE `resident_id` NOT IN
(SELECT tbl.`resident_id`
FROM `home_resident_relations` tbl
WHERE tbl.`datemodified` =
(SELECT max(tbl2.`datemodified`)
FROM `home_resident_relations` tbl2
WHERE tbl2.`home` = tbl.`home`
GROUP BY tbl2.`home`)
OR tbl.`datemodified` IS NULL
);
Now it would be a straight-forward idea to simply replace the SELECT * with a DELETE command to remove all those rows. However, this does not work due to error
#1093 - You can't specify target table 'home_resident_relations' for update in FROM clause
So here's my question:
How do I delete from a table while using it in the WHERE ... NOT IN clause?

Use a left join instead:
DELETE hrr
FROM `home_resident_relations` hrr LEFT JOIN
(SELECT tbl.`resident_id`
FROM `home_resident_relations` tbl
WHERE tbl.`datemodified` = (SELECT max(tbl2.`datemodified`)
FROM `home_resident_relations` tbl2
WHERE tbl2.`home` = tbl.`home`
GROUP BY tbl2.`home`
) OR
tbl.`datemodified` IS NULL
) tt
ON hrd.resident_id = tt.resident_id
WHERE tt.resident_id IS NULL;
This works for both the SELECT and DELETE.

Try using DELETE with join:
DELETE FROM `home_resident_relations`
LEFT OUTER JOIN
(SELECT tbl.`resident_id`
FROM `home_resident_relations` tbl
WHERE tbl.`datemodified` =
(SELECT max(tbl2.`datemodified`)
FROM `home_resident_relations` tbl2
WHERE tbl2.`home` = tbl.`home` )
OR tbl.`datemodified` IS NULL) s
ON(s.`resident_id` = `home_resident_relations`.`resident_id`)
WHERE s.`resident_id` is null

Related

How to update a table with sum of values from the table itself and another table in MySQL with WHERE clause

I know there are similar questions to this, but the answers are not quite what I have been looking for. I have two MySQL tables, I'll say table1 and table2. The fields in table1 are StudentID , Grade and Total_Score. And the fields in table2 are StudentID ,Grade and Total_Score as well. What I want to do is SUM(table1.Total_Score) and add it to SUM(table2.Total_Score). So that Total_Score in table2 is accumulated with scores from table1.Total_Score.
I have tried to achieve that with my code below but the scores in table2.Total_Score never accumulate. They stay the same, even though my code gave no error. See my code below.
update table2 o inner join
(
SELECT op.StudentID,
sum(ot.Total_Score) as Total_Score_ot
sum(op.Total_Score) as Total_Score_op
FROM table1 ot inner join table2 op on op.StudentID = ot.StudentID
WHERE ot.Grade = 'Grade8'
GROUP BY op.StudentID
) as o1 on o.StudentID = o1.StudentID
SET Total_Score=Total_Score_op + Total_Score_ot
Hmmm . . . I don't think you need a join in the subquery:
update table2 o inner join
(select ot.StudentID, sum(ot.Total_Score) as Total_Score_ot
from table1 ot
where ot.Grade = 'Grade8'
group by ot.StudentId
) ot
using (studentid)
set o.Total_Score = o.Total_Score + Total_Score_ot;

delete duplicates in sql: you can't specify target table 'table' for update in FROM clause

I want to delete all my records duplicate from my mysql table. I'm trying this but I have followint error:
You can't specify target table 'maTable' for update in FROM clause.
I have already see another subject about this but I want you to see what I'm doing wrong with my queries and how can I make it work
DELETE
FROM maTable
WHERE id_table IN (SELECT id_table
FROM maTable GROUP by id_table
HAVING COUNT(id_table)>1)
This is a limitation in MySQL. One way around it is to use a join:
DELETE m
FROM maTable m JOIN
(SELECT id_table, COUNT(*) as cnt
FROM maTable
GROUP BY maTable
) mm
ON mm.id_table = m.id_table
WHERE cnt > 1;
If you want to delete all but one of the duplicates (that is, keep one of the values), then it is best to have some sort of unique column, such as a creation date:
DELETE m
FROM maTable m LEFT JOIN
(SELECT id_table, min(creation_date) as mincd
FROM maTable
GROUP BY maTable
) mm
ON mm.id_table = m.id_table AND m.creation_date = mm.mincd
WHERE mm.id_table IS NULL;
Try wrapping the IN query in a subquery:
DELETE
FROM maTable
WHERE id_table IN (
SELECT id_table
FROM (SELECT id_table
FROM maTable
GROUP by id_table
HAVING COUNT(id_table)>1) AS t)
Demo here

SQL Delete with SELECT

I've got the following select query:
SELECT opt.product_option_id
FROM `oc_product_option_value` AS opt_val, `oc_product_option` AS opt
WHERE `opt`.`product_option_id` = `opt_val`.`product_option_id`
AND `opt_val`.`price` = '0.0000'
I thought that i could use that in an delete query:
DELETE oc_product_option, oc_product_option_value FROM oc_product_option
INNER JOIN oc_product_option_value
WHERE `oc_product_option`.`product_option_id` = `oc_product_option_value`.`product_option_id`
AND `oc_product_option_value`.`price` = '0.0000'
But that deleted ALL rows in oc_product_option. So what did i do wrong?
Can you give this a try?
DELETE t1 FROM oc_product_option t1
JOIN oc_product_option_value t2
WHERE `t1`.`product_option_id` = `t2`.`product_option_id`
AND `t2`.`price` = '0.0000'
Is it the intetion to delete the rows of both tables?
You make a circular dependence and this is why you get this result
Bellow query should delete only rows that have id in oc_product_option and price zero.
Or you have to define range of ids in oc_product_option using another criteria to filter oc_product_option table
DELETE FROM oc_product_option o
WHERE o.product_option_id in (SELECT opt.product_option_id FROM oc_product_option_value
where `oc_product_option_value`.`price` = '0.0000')

SQL - joining data

Is it possible to get 1 result where I require data from 3 tables.
First table: I will need to grab all the fields (1 row found by a primary key)
Second table: I will need to grab the field 'username' (connected to first table by 'master_id')
Third table: I will need to grab the latest added row with the associated master_id key (table has 'date', 'master_id', 'previous_name').
select top 1 first.*, second.username, third.*
from first
inner join second on first.id = second.master_id
inner join third on first.id = third.master_id
order by
third.date desc
As always there are dozens of ways to skin a cat, I'm not sure if this is optimized as the subquery methods, but it should work.
You can join the three tables together. Then, you can use a "filter" join to keep only the latest Table3 row:
select *
from Table1 t1
join Table2 t2
on t2.master_id = t1.master_id
join Table3 t3
on t3.master_id = t1.master_id
join (
select master_id
, max(date) as max_date
from Table3
group by
master_id
) as filter
on t3.master_id = filter.master_id
and t3.date = filter.max_date
You'll need a correlated subquery for that third table.
SELECT t1.*, username, date, previous_name
FROM FirstTable t1
INNER JOIN SecondTable t2 ON t1.master_id=t2.master_id
INNER JOIN
(SELECT master_id, date, previous_name
FROM ThirdTable AS t3_1
WHERE date = (
SELECT MAX(date)
FROM ThirdTable AS t3_2
WHERE t3_2.master_id=t3_1.master_id)) q1 ON q1.master_id=t1.master_id;
NOTE: Untested.

MYSQL delete all results having count(*)=1

I have a table taged with two fields sesskey (varchar32 , index) and products (int11), now I have to delete all rows that having group by sesskey count(*) = 1.
I'm trying a fews methods but all fails.
Example:
delete from taged where sesskey in (select sesskey from taged group by sesskey having count(*) = 1)
The sesskey field could not be a primary key because its repeated.
DELETE si
FROM t_session si
JOIN (
SELECT sesskey
FROM t_session so
GROUP BY
sesskey
HAVING COUNT(*) = 1
) q
ON q.sesskey = si.sesskey
You need to have a join here. Using a correlated subquery won't work.
See this article in my blog for more detail:
Keeping rows
Or if you're using an older (pre 4.1) version of MySQL and don't have access to subqueries you need to select your data into a table, then join that table with the original:
CREATE TABLE delete_me_table (sesskey varchar32, cur_total int);
INSERT INTO delete_me_table SELECT sesskey, count(*) as cur_total FROM orig_table
WHERE cur_total = 1 GROUP BY sesskey;
DELETE FROM orig_table INNER JOIN delete_me_table USING (sesskey);
Now you have a table left over named delete_me_table which contains a history of all the rows you deleted. You can use this for archiving, trending, other fun and unusual things to surprise yourself with.
The SubQuery should work
Delete from taged
Where sesskey in
(Select sesskey
From taged
Group by sesskey
Having count(*) = 1)
EDIT: Thanks to #Quassnoi comment below... The above will NOT work in MySql, as MySql restricts referencing the table being updated or deleted from, in a Subquery i you must do the same thing using a Join ...