A little help with this query please. When trying to perform a delete in MySQL, it returns an error: "MySQL Error 1093 - Can't specify target table for update in FROM clause". The select statement works fine otherwise. Here's the script:
DELETE
FROM redcap_data
WHERE record IN (SELECT DISTINCT
redcap_data.record,
redcap_data.field_name,
redcap_data.value
FROM redcap_metadata
INNER JOIN redcap_data
ON redcap_metadata.project_id = redcap_data.project_id
INNER JOIN redcap_events_metadata
ON redcap_data.event_id = redcap_events_metadata.event_id
INNER JOIN redcap_events_arms
ON redcap_events_metadata.arm_id = redcap_events_arms.arm_id
WHERE (redcap_data.project_id = '50'
AND redcap_events_arms.arm_num = '6'
AND redcap_data.record IN ('record_ids go here')
))
Try using an inner join on the subselect you are using as IN clause instead of the IN clause
DELETE r.*
FROM redcap_data r
INNER JOIN (
SELECT DISTINCT
redcap_data.record,
redcap_data.field_name,
redcap_data.value
FROM redcap_metadata
INNER JOIN redcap_data
ON redcap_metadata.project_id = redcap_data.project_id
INNER JOIN redcap_events_metadata
ON redcap_data.event_id = redcap_events_metadata.event_id
INNER JOIN redcap_events_arms
ON redcap_events_metadata.arm_id = redcap_events_arms.arm_id
WHERE (redcap_data.project_id = '50'
AND redcap_events_arms.arm_num = '6'
AND redcap_data.record IN ('record_ids go here')
)) t ON r.record = t.record
It's a documented restriction in MySQL.
One workaround is to use an inline view (derived table), and reference that in a JOIN operation.
Write it as SELECT first
SELECT t.*
FROM target_table t
JOIN ( SELECT q.id
FROM target_table q
JOIN ... r
ON ...
WHERE ...
GROUP BY ...
HAVING ...
) s
ON s.id = t.id
Then we can convert that to a DELETE statement. In my experience, the inline view gives enough separation of target_table in the outer query, that it doesn't raise the MySQL error.
At the time the outer query runs, the query that materializes derived table s has already been executed. The operations that MySQL performs is akin to this (not exactly this, but this illustrates the pattern, how we can think about the derived table ...
CREATE TEMPORARY TABLE _s_ ... ;
INSERT INTO _s_ (...) SELECT ... FROM target_table q ... ;
DELETE t.*
FROM target_table t
JOIN _s_
ON ...
;
DROP TEMPORARY TABLE _s_ ... ;
At the time the DELETE statement runs, it doesn't matter that target_table is referenced in the preceding SELECT. The work to materialize the temporary table s has already been done.
Related
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
I have a weird situation. I need to select all data from table name with distinct values from other table.
Here is database scheme of database that I need to get distinct values:
When I run both queries without INNER JOIN they run without error but when I use INNER JOIN I got error
This is query that I used:
SELECT * FROM `todo`
INNER JOIN
SELECT `task`.`status`,COUNT(*) as count FROM `task`
ON `todo`.`id`=`task`.`id_list` WHERE `todo`.`user_id` = 43
As you can see I need to get total count of status column from other table. Can it be done using one single query or do I need to run two querys to get data...
You need to wrap the join In parenthesis
SELECT td.*, t.*
FROM `todo` td
JOIN
( SELECT `status`, SUM(status=0) as status_0, SUM(status=1) as status_1 , id_list
FROM `task`
GROUP BY id_list
) t ON td.id= t.id_list
WHERE td.user_id = 43
You can do this in one query. Even without a subquery:
SELECT ta.status, COUNT(*) as count
FROM todo t INNER JOIN
task ta
ON t.id = ta.id_list
WHERE t.user_id = 43
GROUP BY ta.status;
EDIT:
If the above produces what you want, then you probably need:
SELECT t.*, ta.status, taa.cnt
FROM todo t INNER JOIN
task ta
ON t.id = ta.id_list INNER JOIN
(SELECT count(*) as cnt, ta.status
FROM task ta
GROUP BY ta.status
) taa
on ta.status = taa.status
WHERE t.user_id = 43 ;
You seem to want a summary at the status level, which is only in task. But you want the information at the row level for todo.
I have just studied FROM clause and derived tables in mysql and most of the websites provided the examples using SELECT command
Example SELECT * FROM (SELECT * FROM usrs) as u WHERE u.name = 'john'
But when I have tried using delete or update command it does not seem to work.
Example DELETE FROM (SELECT * FROM usrs) as u WHERE u.name = 'john'
1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near (SELECT * FROM usrs) as u WHERE u.name = 'john' at line
UPDATE (SELECT * FROM usrs) as u SET u.lname ='smith' WHERE u.name = 'john'
1288 The target table e of the UPDATE is not updatable
So derived tables does not work with delete or update commands? or is there a way to make it work.
Instead of writing the table name for update and delete I want to write a subquery that gets the records and perform the delete operation on that records? Is that possible in mysql?
UPDATED I have to delete a record and i have three tables, the record may exist in any of the table
My approach delete from first table rows effected? quit: else check second table rows effected? quit : else check third table
But if I use UNION ALL I can do this way
Delete from (select * from tb1 union all select * from tb2 union all select * from tb3) e as e.uname = 'john'
but this query does not seem to work , now could anyone tell me how do i delete or update a record when i have more than one table to search. Any help is greatly appreciated.
You can't directly delete from the subquery, but you can still use it if you'd like, you'll just need to use it in a JOIN:
DELETE usrs
FROM usrs
INNER JOIN (
SELECT * FROM usrs WHERE name = 'john'
) t ON usrs.Id = t.Id
Or you could use IN:
DELETE usrs
WHERE ID IN (
SELECT ID
FROM usrs
WHERE name = 'John'
)
With this said, for this example, I don't know why you'd want a subquery:
DELETE usrs WHERE name = 'John'
Edit base on comments. To delete from multiple tables at the same time, you can either have multiple DELETE statements, or you can use something like the following:
delete t1, t2, t3
from (select 'john' as usr) t
left join t1 on t.usr=t1.usr
left join t2 on t.usr=t2.usr
left join t3 on t.usr=t3.usr
SQL Fiddle Demo
Derived tables exist only for the duration of the parent query they're a member of. Assuming that this syntax and the operations were allowed by MySQL, consider what happens:
a) Your main query starts executing
b) the sub-query executes and returns its results as a temporary table
c) the parent update changes that temporary table
d) the parent query finishes
e) temporary tables are cleaned up and deleted
Essentially you'll have done nothing except waste a bunch of cpu cycles and disk bandwidth.
UPDATE queries DO allow you to join against other tables to use in the WHERE clause, e.g..
UPDATE maintable
LEFT JOIN othertable ON maintable.pk = othertable.fk
SET maintable.somefield='foo'
WHERE othertable.otherfield = 'bar'
I'm trying to update a table by inner join? I guess I'm missing something in my code; Am I doing correctly?
UPDATE a
SET product_name = "value2"
from tbl1 a
INNER JOIN tbl2 b
ON a.sid = s.id
Having b.product_name="value1"
#Gordon: can the following query do the same as yours:
UPDATE tbl1 a
INNER JOIN tbl2 b
ON a.sid = s.id
SET product_name = 'value2'
having b.product_name = 'value1';
Please let me know if you need more clarification.
Thanks
The correct syntax for MySQL is:
UPDATE tbl1 a INNER JOIN
tbl2 b
ON a.sid = s.id AND b.product_name = 'value1'
SET product_name = 'value2' ;
EDIT (in response to comment):
Formally, you would only be allowed to use a having clause when you have an aggregation. And, you cannot update aggregated queries in any dialect of SQL.
MySQL extends the having clause so it behaves a lot like a where clause when there is no group by clause. Actually, the having clause is extended so it can use column aliases defined in the select statement (which is not applicable in an update query).
Your query with the having clause doesn't actually work in MySQL, because the extension is for the select, but not theupdate. Replace it with awherestatement or move the condition to theon` clause.
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 ...