This question already has an answer here:
MySQL 5.7 error (1093: You can't specify target table ___ for update in FROM clause) - usual solution not working
(1 answer)
Closed 7 years ago.
I have this update statement in a trigger in one of my Databases:
UPDATE trainees
SET rsi_total = (
SELECT SUM(RsiTotal) as RsiTotal
FROM (
SELECT MAX(hours) as RsiTotal
FROM courses
LEFT JOIN do_not_add ON courses.fk_class_id = do_not_add.fk_class_id
INNER JOIN trainees ON courses.FK_TRAINEES_ID = trainees.PK_TRAINEE_ID
WHERE do_not_add.fk_class_id IS NULL
AND trainees.pk_trainee_id = new.fk_trainees_id
GROUP BY courses.FK_CLASS_ID
) courses
)
WHERE trainees.pk_trainee_id = new.fk_trainees_id
and does exactly what I expect, recently I migrated my database to another machine and moved and renamed a few tables and columns. So I re-wrote this trigger to:
UPDATE main
SET rsi_total = (
SELECT SUM(RsiTotal) as RsiTotal
FROM (
SELECT MAX(completed_hrs) as RsiTotal
FROM courses
LEFT JOIN jac.do_not_add ON courses.fk_class_id = do_not_add.fk_class_id
INNER JOIN main ON courses.fk_main_id = main.pk_main_id
WHERE do_not_add.fk_class_id IS NULL
AND main.pk_main_id = new.fk_main_id
GROUP BY courses.FK_CLASS_ID
) courses
)
WHERE main.pk_main_id = new.fk_main_id
But I get Error 1093? I looked up this error and it says I can not update a table that is being changed, what I dont understand is why does the old trigger continue to work but not this new?
Migrating to another machine, you probably got a more recent version of MySql, where the optimizer doesn't accept any longer subqueries to access the same table which is being updated.
Look at this detailed answer.
Related
I'm trying to write a SQL query to update a field in a different database (Hosted on the same server with phpMyAdmin). I just want it to update the most recent row as the query will be run directly after a new user is added (on that new user). My goal output is to change the username field of this user to be the same as the email field of the user in the original database. The syntax error states
'syntax to use near 'FROM db2.users AS "data" WHERE db1.user.email =
...' at line 3'
I can't see where I'm going wrong - I saw another answer where a user said that mySQL does not support the FROM keyword, however I have not been able to find anywhere else that backs that up. This is what I currently have for my code:
UPDATE db1.user
SET username = data.username
FROM db2.users AS "data"
WHERE db1.user.email = data.email
AND db1.user.id = (
SELECT MAX(id)
FROM db1.user
)
LIMIT 1
If anyone knows where I'm going wrong with this it would be much appreciated!
In MySQL, the syntax would be:
UPDATE db1.user u JOIN
db2.users u2
ON u.email = u2.email JOIN
(SELECT MAX(u2.id) as max_id
FROM db1.user u2
) uu
ON uu.max_id = u.id
SET u.username = u2.username;
Notes:
It seems odd that you are not filtering by email to get the maximum id, but that is how your question is stated. Also, MySQL doesn't support LIMIT in multi-table UPDATE queries.
As in your SQL you are okay with using subqueries, I would suggest to try the following:
UPDATE
db1.user
SET
username = (
SELECT
data.email
FROM
db2.users AS "data"
WHERE
db1.user.email = data.email)
WHERE
db1.user.id = (
SELECT
MAX(id)
FROM
db1.user)
LIMIT 1;
This question already has answers here:
MySQL Error 1093 - Can't specify target table for update in FROM clause
(16 answers)
Closed 4 years ago.
I am trying to simple update query with following syntax:
UPDATE cl
SET cl_s="active_"
WHERE id in (
SELECT distinct
c.id
from
cl c,
ac a,
cl_ac ac
where
c.id=ac.cl_id
and
a.id=ac.ac_id
and
c.cl_s="someval"
and
a.ac_no !=""
)
;
The error I get is: Error Code: 1093. You can't specify target table for update in FROM clause
I've read through posts which mention that MySQL is unable to run query like this. I'd want to know a seamless way to restructure such queries to a structure which works. The solution seems to be using joins instead but I can't seem to grasp the concept.
A pseudo code would be helpful, to help with the understanding. Something like
(Main query)
where var in (subquery generating var list)
to
(main query) inner join (subquery generating var list) on var ???
You can use JOIN in UPDATE statements in MySQL
UPDATE cl
JOIN cl_ac ON cl.id = cl_ac.cl_id
JOIN ac ON cl_ac.ac_id = ac.id
SET cl.cl_s = 'active_'
WHERE cl.cl_s = 'someval' AND ac.ac_no != ''
PS: Use single-quotes for string literals and date literals in SQL. Double-quotes may be used for delimited identifiers, depending on the sql_mode.
This question already has answers here:
MySQL Error 1093 - Can't specify target table for update in FROM clause
(16 answers)
Closed 7 years ago.
I have problems with subquerys.
In phpMyAdmin when I enter this:
SELECT companys.id
FROM companys
JOIN users
ON users.company = companys.id
WHERE users.id = '$LOGGED_IN_USER'
I get number 1 returned. That is correct.
And then I tested:
UPDATE companys
SET companys.signature = '$SIGNATURE'
WHERE companys.id = 1
That also works, it updates the value with $SIGNATURE where companys.id = 1. So far so good. Now to the problem. The problem appears when I combine those two:
UPDATE companys
SET companys.signature = '$SIGNATURE'
WHERE companys.id = ( SELECT companys.id
FROM companys
JOIN users
ON users.company = companys.id
WHERE users.id = '$LOGGED_IN_USER')
The DB never gets updated. It is like the inner select doesn't work suddenly for some reason.
With the update query you have you should be getting an error: You can't specify target table 'companys' for update in FROM clause
One solution is to force MySQL to create a temporary result set that it can use as the source:
UPDATE companys
SET companys.signature = '$SIGNATURE'
WHERE companys.id = (
SELECT id FROM (
SELECT companys.id
FROM companys
JOIN users
ON users.company = companys.id
WHERE users.id = '$LOGGED_IN_USER'
) src
);
See this SQL fiddle for a working example.
I am tying to run an update query with a subquery against a MySQL database using ruby. I am using ruby 1.9.3 and rails 4.1.
The query I am trying to create is as below:
UPDATE `items`
SET
`items`.`status_id` = 12
WHERE
`items`.`id` IN (SELECT DISTINCT
`items`.`id`
FROM
`items`
LEFT OUTER JOIN
`statuses` ON `items`.`status_id` = `statuses`.`id`
LEFT OUTER JOIN
`resources` ON `items`.`resource_id` = `resources`.`id`
WHERE
`statuses`.`title` LIKE 'On Loan'
AND `items`.`duedate` < '2015-04-24'
AND `items`.`return_date` IS NULL
ORDER BY `items`.`duedate`)
I can produce this query in ruby using AREL with the code shown below:
# Declare Arel objects
i = Item.arel_table
s = Status.arel_table
r = Resource.arel_table
# This is the AREL query that returns the data
overdues = i.project(i[:id]).
join(s, Arel::Nodes::OuterJoin).on(i[:status_id].eq(s[:id])).
join(r, Arel::Nodes::OuterJoin).on(i[:resource_id].eq(r[:id])).
where(s[:title].matches("On Loan").
and(i[:duedate].lt(DateTime.now.to_date)).
and(i[:return_date].eq(nil))
).
order(i[:duedate])
# Note: You can't chain distinct, otherwise "overdues" becomes a string with the value "DISTINCT".
overdues.distinct
# This creates the update...
u = Arel::UpdateManager.new i.engine
u.table(i)
u.set([[i[:status_id], 10]]).where(i[:id].in(overdues))
This does not work and returns an error message:
ActiveRecord::StatementInvalid: Mysql2::Error: You can't specify target table 'items' for update in FROM clause:
I tried using AR "update_all" but it produces the same SQL and hence the same error.
Item.where(i[:id].in(overdues)).update_all(:status_id => (Status.find_by(:title => "Overdue").id))
Having done some research I have found that you cannot run a update with a subquery that references the table you want to update in MySQL. I have seen a number of posts on this site and the wider internet that detail work arounds.
One suggestion says that the update should use a join instead of a sub query. Having looked at the code behind the update manager it has no "join" so I can't do that.
Another says run this in two parts but I can't see how to because AREL and AciveRecord both chain actions.
The only way I can see of doing this is by aliasing the table and adding an additional select (see below). This isn't great but it would be useful to see if it is possible to do.
UPDATE `items`
SET `status_id` = 10
WHERE `items`.`id` IN (
SELECT x.id
FROM
(SELECT DISTINCT `items`.`id`
FROM `items`
LEFT OUTER JOIN `statuses` ON `items`.`status_id` = `statuses`.`id`
LEFT OUTER JOIN `resources` ON `items`.`resource_id` = `resources`.`id`
WHERE `statuses`.`title` LIKE 'On Loan'
AND `items`.`duedate` < '2015-04-24'
AND `items`.`return_date` IS NULL
ORDER BY `items`.`duedate`) x
);
If I can't get this to work I could adopt two other approaches:
1) I could just hard-code the SQL but I want to use ActiveRecord and reference the models to keep it database agnostic.
2) The other way is to return an instance of all the records and loop through them doing individual updates. This will have a performance issue but I can accept this because its a background job that won't be updating more than a handful of records each day.
Update
I have the AREL query below that produces the subquery in the format I need.
x = Arel::Table.new('x')
overdues = Item.select(x[:id]).from(
Item.select(Item.arel_table[:id]).where(
Status.arel_table[:title].matches("On Loan").and(
Item.arel_table[:duedate].lt(DateTime.now.to_date).and(
Item.arel_table[:return_date].eq(nil))
)
).joins(
Item.arel_table.join(Status.arel_table, Arel::Nodes::OuterJoin).on(
Item.arel_table[:status_id].eq(Status.arel_table[:id])
).join_sources
).joins(
Item.arel_table.join(Resource.arel_table, Arel::Nodes::OuterJoin).on(
Item.arel_table[:resource_id].eq(Resource.arel_table[:id])
).join_sources
).order(Item.arel_table[:duedate]).uniq.as('x')
)
Sadly it returns an error when I use it in my update statement.
TypeError: Cannot visit Item::ActiveRecord_Relation
Having revisited this question I am at the conclusion that it's not possible to do this because of a limitation with MySQL:
ActiveRecord::StatementInvalid: Mysql2::Error: You can't specify target table 'items' for update in FROM clause:
It should be possible to do with other databases (although I haven't tested that).
I could create a temporary table, which is the copy of the original table, reference that and then drop the temporary table like this post suggests:
http://richtextblog.blogspot.co.uk/2007/09/mysql-temporary-tables-and-rails.html. That seems a lot of overhead to do a simple subquery.
What I am going to do is find all the ID's and loop through them and update the records that way (using a simple find and update). This has an overhead but it should only be updating a handful of records each run (no more than 100). The update will be running as a scheduled job outside user working hours so it won't impact performance.
I still find it bizarre that in all other flavours of SQL I have never encountered this problem before. Still you live and learn.
UPDATE:
Since updating my version of MySQL the select statement now works. I had to take out the order by for it to work.
ORDER BY `items`.`duedate`
I am now using version: 5.7.19.
I have the following query in HQL:
update ProjectFile pf1
set pf1.validUntil.id =123
where pf1 = (
select pf from ProjectVersion pv, ProjectFile as pf
where pf.validFrom.sequence <= pv.sequence
and pf.validUntil.sequence >= pv.sequence
and pf.state <> 12
and pf.projectVersion.project.id = 1
and pv.project.id = 1
and pv.id = 12
and pf.id not in (2,3,4)
)
Hibernate parses the query correctly and generates SQL, but the database (MySQL) fails with error:
You can't specify target table 'ProjectFile' for update in FROM clause
The problem seems to be that the table to be updated is queried in the same context. Is there any way to rewrite the HQL query to produce SQL that can be executed in MySQL correctly? The other approach would be to create an intermediate table, which is what exactly I am trying to avoid.
I bumped into the same problem and posted a question here: MySQL/SQL: Update with correlated subquery from the updated table itself.
To solve your problem, you need to join at the UPDATE level, please take a look at the answer to my question.