JPQL: Update if not exists - mysql

I'm trying to achieve something like this in may JPA/Hibernate/MySQL application:
UPDATE UserRating ur SET ur.item = :newItem WHERE ur.item = :oldItem AND NOT EXISTS (SELECT ur2 FROM UserRating ur2 WHERE ur.user = ur2.user AND ur2.item = :newItem)
So, I want to update some rows only if they will be unique (user/item combination must be unique).
I'm getting
Caused by: java.sql.SQLException: You can't specify target table 'user_rating' for update in FROM clause
with the current JPQL.

One way is to turn in it into JOIN
UPDATE UserRating r JOIN
(
SELECT user, item
FROM UserRating ur
WHERE item = :oldItem
AND NOT EXISTS
(
SELECT *
FROM UserRating
WHERE user = ur.user
AND item = :newItem
)
) q ON r.user = q.user
AND r.item = q.item
SET r.item = :newItem
Here is SQLFiddle demo

Related

MySQL Error 1093 - Can't specify target table for update in FROM clause (two joins)

I'm trying to update the unit_price in a sales_order_parts detail table with calculation from the applied_discount field from sales_orders. Also, the price is in the master_part_list table as price. When I run from the select statement down, it runs fine and returns the list of values from order_number 209 with the correct calculation. When I run it complete from the update line, it returns "Error Code: 1093. You can't specify target table 'sop' for update in FROM clause" Any ideas?
update sales_order_parts as sop
set unit_price =
(select (master_part_list.price * (1-(so.applied_discount/100)))
from sales_orders as so
inner join sales_order_parts as sop2
on so.id = sop2.order_id
inner join master_part_list
on sop2.part_id = master_part_list.id
where so.order_number = 209);
You could try with a join without subquery
update sales_order_parts as sop
INNER JOIN sales_orders as so on so.id = sop.order_id
AND so.order_number = 209
inner join master_part_list on sop.part_id = master_part_list.id
SET sop.unit_price = master_part_list.price * (1-(so.applied_discount/100))
Use a subquery for sales_order_parts. mysql then treats as new temporary table
Like
update sales_order_parts as sop
set unit_price =
(select (master_part_list.price * (1-(so.applied_discount/100)))
from sales_orders as so
inner join (SELECT * FROM sales_order_parts) as sop2
on so.id = sop2.order_id
inner join master_part_list
on sop2.part_id = master_part_list.id
where so.order_number = 209);

MySql-Update: Can't specify target table for update in FROM clause

I am trying to performing update operation on value which meet certain criteria. My tables CAPD, CAMP, CAD. But I get error of
Error Code: 1093. You can't specify target table 'CAPD' for update in
FROM clause
UPDATE CAPD SET CAPD.Is_Active = 1
WHERE CAPD.Per_Id IN (
SELECT CAMP.Id
FROM CAMP
INNER JOIN CAPD ON (
CAPD.Per_Id = CAMP.Id
AND CAPD.Is_Active = 0
)
INNER JOIN CAD ON (
CAD.Id = CAPD.Deploy_Id
AND BINARY CAD.Access_Id = "486579446F6E277-4436F6E7665727449742E2E4C-4F4C203A5020584F586F"
)
WHERE CAMP.Serial = "ABC1230071"
)
You cannot use the target table which you are updating inside the
subquery. You need to use the JOIN in case you want to use it -- First
answer by #Rahul Tripathi
You have to try this query then you need to set SET SQL_SAFE_UPDATES
SET SQL_SAFE_UPDATES=0;
UPDATE CAPD
INNER JOIN CAD ON ( CAD.Id = CAPD.Deploy_Id)
SET CAPD.Is_Active = 1
WHERE CAPD.Per_Id IN (
SELECT CAMP.Id
FROM CAMP
WHERE CAMP.Serial = "ABC1230071"
)
AND BINARY CAD.Access_Id = "486579446F6E277-4436F6E7665727449742E2E4C-4F4C203A5020584F586F";
SET SQL_SAFE_UPDATES=1;
You cannot use the target table which you are updating inside the subquery. You need to use the JOIN in case you want to use it. Try like this:
UPDATE CAPD
INNER JOIN CAD ON ( CAD.Id = CAPD.Deploy_Id)
SET CAPD.Is_Active = 1
WHERE CAPD.Per_Id IN (SELECT CAMP.Id
FROM CAMP
WHERE CAMP.Serial = "ABC1230071" )
and CAD.Access_Id = "486579446F6E277-4436F6E7665727449742E2E4C-4F4C203A5020584F586F"
and CAPD.Is_Active = 0

Update Statement using Derived Table

I am trying to write the following update statement;
UPDATE #eticat
SET eticat_purchase_total = t.eticat_purchase_total
FROM (
SELECT eticat_id, COUNT(eticat_id) as eticat_purchase_count
FROM etransaction
INNER JOIN etransaction_item
INNER JOIN etransaction_item_catalog ON eti_eticat_id = eticat_id
ON eti_et_id = et_id
WHERE et_cmc_id = #can_cmc_id
GROUP by eticat_id
) as t
WHERE eticat_id = t.eticat_id
But it keeps complaining about ambigous columns. Can someone please tell me what I am doing wrong.
EDIT: Error Message is "Ambiguous column name 'eticat_id'."
That line is 'WHERE eticat_id = t.eticat_id'
First, that's not a CTE; it's a derived table. Similar, but different :)
Second, you're updating a table variable that's not included in your FROM clause, which is confusing SQL Server. Try something like:
UPDATE x
SET eticat_purchase_total = t.eticat_purchase_total
FROM (
SELECT eticat_id, COUNT(eticat_id) as eticat_purchase_count
FROM etransaction
INNER JOIN etransaction_item
INNER JOIN etransaction_item_catalog ON eti_eticat_id = eticat_id
ON eti_et_id = et_id
WHERE et_cmc_id = #can_cmc_id
GROUP by eticat_id
) as t JOIN #eticat x ON x.eticat_id = t.eticat_id

Is there a way to optimize this update query?

I have a master table called "parent" and a related table called "childs"
Now I run a query against the master table to update some values with the sum from the child table like this.
UPDATE master m SET
quantity1 = (SELECT SUM(quantity1) FROM childs c WHERE c.master_id = m.id),
quantity2 = (SELECT SUM(quantity2) FROM childs c WHERE c.master_id = m.id),
count = (SELECT COUNT(*) FROM childs c WHERE c.master_id = m.id)
WHERE master_id = 666;
Which works as expected but is not a good style because I basically make multiple SELECT querys on the same result. Is there a way to optimize that? (Making a query first and storing the values is not an option.
I tried this:
UPDATE master m SET (quantity1, quantity2, count) = (
SELECT SUM(quantity1), SUM(quantity2), COUNT(*)
FROM childs c WHERE c.master_id = m.id
) WHERE master_id = 666;
but that doesn't work.
Update: Here is the solution, thanks to everbody:
You can do something like this:
UPDATE master m
INNER JOIN childs c ON m.master_id = c.master_id
SET master.quantity1 = c.quantity1,
master.count = 1
If you have only one child record at a time. However if you want to use a group function like SUM() in the joined table that doesn't work. Either you get a "Invalid use of group function" if you leave the "group by" part or a "You have an error in your sql syntax if you use "GROUP BY c.master_id"
-- This doesnt work :(
UPDATE master m
INNER JOIN childs c ON m.master_id = c.master_id
SET master.quantity1 = SUM(c.quantity1),
master.count = COUNT(c.*)
GROUP by c.master_id
The solution is to use JOIN with a subquery:
UPDATE master m
INNER JOIN
(
SELECT master_id,
SUM(quantity1) as quantity1,
COUNT(*) as count
FROM childs c
GROUP BY master_id
) c
ON c.master_id = m.master_id
SET m.quantity1 = c.quantity1,
m.count = c.count
WHERE m.master_id = 666;
But since this pulls every row from the childtable the overhead would likely be bigger than using more subqueries like in the original sql. So you should add a WHERE clause to the joined table to get only the rows you need.
Another interesting approach is this syntax, which does the same as the JOIN with the WHERE clause but you should only use if if you want to update all rows with the same values and your subquery only returns one row, since the result from the subquery gets appended to the result and can be used like any column.
UPDATE master m,
(
SELECT SUM(c.quantity1) as sum_of_quantity,
COUNT(*) as rowcount FROM child c WHERE c.master_id = 666
) as c
SET m.quantity1 = c.sum_of_quantity,
m.count = c.rowcount
WHERE m.master_id = 666;
Rewriting Lieven's solution to MySQL:
UPDATE master m
JOIN (
SELECT master_id
, SUM(quantity1) as quantity1
, SUM(quantity2) as quantity2
, COUNT(*) as count
FROM childs c
GROUP BY
master_id
) c
ON c.master_id = m.master_id
SET
m.quantity1 = c.quantity1
,m.quantity2 = c.quantity2
,m.count = c.count
WHERE m.master_id = 666;
I don't know if it is allowed in MySQL, but SQL Server allows you to use the result of a select in an update.
UPDATE master m SET
quantity1 = c.quantity1
, quantity2 = c.quantity2
, count = c.count
FROM master m
INNER JOIN (
SELECT master_id
, quantity1 = SUM(quantity1)
, quantity2 = SUM(quantity2)
, count = COUNT(*)
FROM childs c
WHERE master_id = 666
GROUP BY
master_id
) c ON c.master_id = m.master_id
You could select your data into a temporary table, and then update using that data.
If you also want to insert "new" data in the same roundtrip, look into INSERT INTO ... SELECT FROM ... ON DUPLICATE KEY UPDATE ...
If you already are doing inserts if row doesn't exist, then that would be redundant with this example.
example:
INSERT INTO master m (id, quantity1, quantity2, count)
SELECT master_id, SUM(quantity1) q1, SUM(quantity2) q1, COUNT(*) c
FROM childs
GROUP BY master_id
ON DUPLICATE KEY UPDATE
m.quantity1 = q1,
m.quantity2 = q2,
m.count = c
NOTE! This is untested code, but I think it should be possible to backreference the select result in the UPDATE.
Syntax reference: http://dev.mysql.com/doc/refman/5.0/en/insert.html

How to Fix This MySQL Query So It Works?

I have the following query:
UPDATE lessonstatus
INNER JOIN user ON lessonstatus.user_id = user.user_id
SET user_id = (SELECT user_id FROM user WHERE username = 'too_many_accounts')
WHERE last_name = 'stupid'
AND first_name = 'user'
AND username != 'too_many_accounts'
AND lessonstatus.lesson_id NOT IN (SELECT lesson_id FROM lessonstatus WHERE user_id = 1);
However, I get the following error when trying to execute it:
Error Code : 1093
You can't specify target table 'lessonstatus_rtab' for update in FROM clause
How would I fix this query so that it works?
You can't SELECT from a table (even in a subquery) that you're updating in the same query. That's what the error "can't specify target table" means.
But you can join user and lessonstatus multiple times in the UPDATE statement, and use the join criteria creatively to pick out the individual row you want.
The way to simulate NOT IN with a join is to do a LEFT OUTER JOIN. Where the right side of that join is not matched, that's where NOT IN would be true.
UPDATE lessonstatus l1
INNER JOIN user u1 ON (l1.user_id = u1.user_id)
INNER JOIN user u2 ON (u2.username = 'too_many_accounts')
LEFT OUTER JOIN lessonstatus l2
ON (l1.lesson_id = l2.lesson_id AND l2.user_id = 1)
SET l1.user_id = u2.user_id
WHERE u1.last_name = 'stupid' AND u1.first_name = 'user'
AND u1.username != 'too_many_accounts'
AND l2.lesson_id IS NULL; -- equivalent to "l NOT IN l2"
nb: I have tested this query for syntax, but not with real data. Anyway, it should get you started.
There are more errors ("user" table and "user_rtab" alias do not match, use of non-qualified field names is not recommended), but UPDATE syntax itself should be similar:
UPDATE lessonstatus
SET user_id = (SELECT TOP 1 user_id FROM user WHERE username = 'too_many_accounts')
FROM lessonstatus
INNER JOIN user ON lessonstatus.user_id = user_rtab.user_id
WHERE last_name = 'stupid'
AND first_name = 'user'
AND username != 'too_many_accounts'
AND lessonstatus.lesson_id NOT IN (
SELECT lesson_id FROM lessonstatus WHERE user_id = 1
);