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.
Related
I've got a task to fetch a set of entities for a specific period of time.
I'm using code first approach and MySql server.
With the code
IQueryable<Entity> query = dbContext.Set<Entity>().AsNoTracking();
Entity[] entities = (from e in query
let last = (from ee in query orderby ee.Id descending select ee).FirstOrDefault()
where e.Timestamp >= DbFunctions.AddHours(last.Timestamp, -hours)
select e).ToArray();
And the following SQL generated by EF 6.1.3
SELECT
`Extent1`.`Id`,
`Extent1`.`Timestamp`
FROM `Entities` AS `Extent1` LEFT OUTER JOIN (SELECT
`Extent2`.`Id`,
`Extent2`.`Timestamp`
FROM `Entities` AS `Extent2`
ORDER BY
`Extent2`.`Id` DESC LIMIT 1) AS `Limit1` ON 1 = 1
WHERE `Extent1`.`Timestamp` >= (AddHours(`Limit1`.`Timestamp`, -(#p__linq__0)))
I face an error
Failed in 6 ms with error: FUNCTION mydb.AddHours does not exist
And indeed I haven't got that function at DB side.
Does anybody know how those DbFunctions work?
UPD
Similar questions
EntityFramework 6.1.3 and MySQL DbFunctions.TruncateTime does not exist?
how to use canonical functions in Entity Framework and Mysql
A workaround for now - just added an implementation of the function in DbInitializer
CREATE FUNCTION AddHours(timeValue datetime, addValue int) RETURNS datetime
RETURN DATE_ADD(timeValue, INTERVAL addValue HOUR);
You are trying to call a stored function of your database, but that does not exist. The solution is to properly create the AddHours db function. More information can be found here.
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.
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.
Hi i am new to mysql and i have made the following statement
UPDATE parent, chill SET parent.iod=chill.iod WHERE parent.id=chill.id
what i want to do is copy all the iod's from chill to parent where id should be matched.
But the result is that all the iod updated are set to 11 rather than iod's in chill.
iod's is of type
BIGINT
Is there ant mistake in the syntax or anything that i am doing wrong?
help with little explanation appreciated.
mysql update syntax for join tables:
The preceding example shows an inner join that uses the comma operator, but multiple-table UPDATE statements can use any type of join permitted in SELECT statements, such as LEFT JOIN.
try the following (although your example should work as well):
UPDATE parent
INNER JOIN chill
ON parent.id = chill.id
SET parent.iod = chill.iod
I know there is more than one question out there that matches this, but I am relatively new to mysql, and I can't seem to make this work using sub quests or the USING key word, plus I find the mysql on line docs a complete mystery.
I started trying to build my DELETE query using a SELECT query as my base and was able to get all the rows that I wanted to delete:
select *
from writings_tags_link
join writing_tags on writing_tags.id = writings_tags_link.tag_id
where writing_tags.tag = 'tag one'
and then just replaced select all with DELETE so:
delete
from writings_tags_link
join writing_tags on writing_tags.id = writings_tags_link.tag_id
where writing_tags.tag = 'tag one'
I gather from both the error message and from other similar posts that you can't use 'ON' to join tables in a delete query, you have to use USING or a sub query. The query I built with USING returns a really strange error, first the query:
DELETE
FROM writings_tags_link
USING writing_tags_link INNER JOIN writing_tags
WHERE writing_tags.id = writings_tags_link.tag_id
AND writing_tags.tag ='tag one'
error:
#1109 - Unknown table 'writings_tags_link' in MULTI DELETE
This table does exist, obviously, my original select query returned the desired results. Any help / explanation would be so very appreciated!
Please keep in mind, I'm only trying to delete the data in the linking table.
Your information is incorrect about requiring the use of the USING keyword in DELETE syntax when using JOINs - the documentation provides examples in the multi-delete section:
DELETE wtl
FROM WRITINGS_TAGS_LINK wtl
JOIN WRITING_TAGS wt ON wt.id = wtl.tag_id
WHERE wt.tag = 'tag one'