How to fix Table is specified twice in MySQL 8.2? - mysql

Here is my query:
UPDATE order_product
SET amount = coalesce(
(
SELECT amount_origin + (SELECT sum(amount_to_change)
FROM order_product_edits
WHERE order_product_id = NEW.order_product_id
)
FROM order_product
WHERE id = NEW.order_product_id
)
, 0)
It works well on my local computer (MySQL 8.3) .. but it throws this error on the server (MySQL 8.2):
Table is specified twice, both as a target for 'UPDATE' and as a separate source for data in mysql
Any idea how can I fix it?

The problem is the nested correlation clause. I think you don't need it:
UPDATE order_product op
SET amount = COALESCE(op.amount_origin + (SELECT sum(ope.amount_to_change)
FROM order_product_edits ope
WHERE ope.order_product_id = op.order_product_id
), 0
)
WHERE op.id = NEW.order_product_id;
I am surprised the default value is 0 rather than op.amount_origin.

Coming from the T-sql world, I would suggest that you make use of a temp table or table variable, such that you can store something in the idea of key, updated value and once you have that populated you can use that within your update query to set the values to the updated values.

Related

Table is specified twice, both as a target for 'UPDATE' and as a separate source for data in mysql

I have below query in mysql where I want to check if branch id and year of finance type from branch_master are equal with branch id and year of manager then update status in manager table against branch id in manager
UPDATE manager as m1
SET m1.status = 'Y'
WHERE m1.branch_id IN (
SELECT m2.branch_id FROM manager as m2
WHERE (m2.branch_id,m2.year) IN (
(
SELECT DISTINCT branch_id,year
FROM `branch_master`
WHERE type = 'finance'
)
)
)
but getting error
Table 'm1' is specified twice, both as a target for 'UPDATE' and as a
separate source for data
This is a typical MySQL thing and can usually be circumvented by selecting from the table derived, i.e. instead of
FROM manager AS m2
use
FROM (select * from manager) AS m2
The complete statement:
UPDATE manager
SET status = 'Y'
WHERE branch_id IN
(
select branch_id
FROM (select * from manager) AS m2
WHERE (branch_id, year) IN
(
SELECT branch_id, year
FROM branch_master
WHERE type = 'finance'
)
);
The correct answer is in this SO post.
The problem with here accepted answer is - as was already mentioned multiple times - creating a full copy of the whole table. This is way far from optimal and the most space complex one. The idea is to materialize the subset of data used for update only, so in your case it would be like this:
UPDATE manager as m1
SET m1.status = 'Y'
WHERE m1.branch_id IN (
SELECT * FROM(
SELECT m2.branch_id FROM manager as m2
WHERE (m2.branch_id,m2.year) IN (
SELECT DISTINCT branch_id,year
FROM `branch_master`
WHERE type = 'finance')
) t
)
Basically you just encapsulate your previous source for data query inside of
SELECT * FROM (...) t
Try to use the EXISTS operator:
UPDATE manager as m1
SET m1.status = 'Y'
WHERE EXISTS (SELECT 1
FROM (SELECT m2.branch_id
FROM branch_master AS bm
JOIN manager AS m2
WHERE bm.type = 'finance' AND
bm.branch_id = m2.branch_id AND
bm.year = m2.year) AS t
WHERE t.branch_id = m1.branch_id);
Note: The query uses an additional nesting level, as proposed by #Thorsten, as a means to circumvent the Table is specified twice error.
Demo here
Try :::
UPDATE manager as m1
SET m1.status = 'Y'
WHERE m1.branch_id IN (
(SELECT DISTINCT branch_id
FROM branch_master
WHERE type = 'finance'))
AND m1.year IN ((SELECT DISTINCT year
FROM branch_master
WHERE type = 'finance'))
The problem I had with the accepted answer is that create a copy of the whole table, and for me wasn't an option, I tried to execute it but after several hours I had to cancel it.
A very fast way if you have a huge amount of data is create a temporary table:
Create TMP table
CREATE TEMPORARY TABLE tmp_manager
(branch_id bigint auto_increment primary key,
year datetime null);
Populate TMP table
insert into tmp_manager (branch_id, year)
select branch_id, year
from manager;
Update with join
UPDATE manager as m, tmp_manager as tmp_m
inner JOIN manager as man on tmp_m.branch_id = man.branch_id
SET status = 'Y'
WHERE m.branch_id = tmp_m.branch_id and m.year = tmp_m.year and m.type = 'finance';
This is by far the fastest way:
UPDATE manager m
INNER JOIN branch_master b on m.branch_id=b.branch_id AND m.year=b.year
SET m.status='Y'
WHERE b.type='finance'
Note that if it is a 1:n relationship the SET command will be run more than once. In this case that is no problem. But if you have something like "SET price=price+5" you cannot use this construction.
Maybe not a solution, but some thoughts about why it doesn't work in the first place:
Reading data from a table and also writing data into that same table is somewhat an ill-defined task. In what order should the data be read and written? Should newly written data be considered when reading it back from the same table? MySQL refusing to execute this isn't just because of a limitation, it's because it's not a well-defined task.
The solutions involving SELECT ... FROM (SELECT * FROM table) AS tmp just dump the entire content of a table into a temporary table, which can then be used in any further outer queries, like for example an update query. This forces the order of operations to be: Select everything first into a temporary table and then use that data (instead of the data from the original table) to do the updates.
However if the table involved is large, then this temporary copying is going to be incredibly slow. No indexes will ever speed up SELECT * FROM table.
I might have a slow day today... but isn't the original query identical to this one, which souldn't have any problems?
UPDATE manager as m1
SET m1.status = 'Y'
WHERE (m1.branch_id, m1.year) IN (
SELECT DISTINCT branch_id,year
FROM `branch_master`
WHERE type = 'finance'
)

mySQL update a value

Modified some stuff from my pic so you guys can understand it
I have this database. I am trying to update a value from a table based on another value from an another table.
I want to update the SUM from salary like this :
( sum = presence * 5 )
This is what I've been trying to use ( unsuccessful )
update table salary
set suma.salary = users.presence * 5
FROM salary INNER JOIN users1 INNER JOIN presence on id_salary = id_presence
I am not sure what to do, I'd appreciate some help, Thanks
In MySQL to UPDATE tables with a join you use this syntax:
UPDATE table1, table2
SET table1.column = some expression
WHERE table1.column = table2.column
That said, even with the updated picture, in your SQL you are mentioning columns that I cannot understand in which table are to be found. You also have an inner join between salariu and users1, with no join condition. Could you please clean up the question and make everything clear?
Assuming you are making the updates to the db structure you were talking about, then you can start working on this one maybe:
UPDATE salary, presence
SET salary.sum = SUM(presence.hours) * 5
WHERE presence.id = salary.id
AND <some filter on the month that depends on salary.date>
Another way, but I'm not sure it is supported in all RDBMS, would be something like this:
UPDATE salary
SET sum = (
SELECT SUM(presence.hours) * 5
FROM user, presence
WHERE presence.id = salary.id
AND <some filter on the month that depends on salary.date>
)

Update Query using SELECT results

As my application is expanding, I now am changing the structure of my database; I now want to control file types within the database. I wanted to start with the current file types already in the database. My Database now has a [simplified] 2 table structure like:
tbFiles: pkFileID, fileType, fileName
tblFileType: pkFileType, typeName, typeDesc
I am trying to have the output of a SELECT query update into the newly created tblFileType table. I have tried among other things:
UPDATE tblFileType
INNER JOIN
(SELECT DISTINCT fileType FROM tblFiles) as X
SET typeName = fileType
but I always seem to get 0 row(s) affected.
When I run
SELECT DISTINCT fileType
FROM `tblFiles`
I get Showing rows 0 - 22 (~23 total, Query took 0.0074 sec)
I know this must be simple, but why is the UPDATE query not affecting 23 rows?
You need to add a JOIN condition like ON t1.fileType = x.fileType as follows:
UPDATE tblFileType t1
INNER JOIN
(
SELECT DISTINCT fileType
FROM tblFiles
)as X ON t1.fileType = x.fileType
SET t1.typeName = X.fileType
Update: Since the table tblFileType is blank, you will need to use INSERT something like:
INSERT INTO tblFileType(typeName )
SELECT DISTINCT fileType
FROM tblFiles
WHERE -- a condition here
you just want to populate the table - not update anything in there (especially since nothing exists yet)
INSERT INTO tblFileType(typeName )
SELECT DISTINCT fileType FROM tblFiles

subquery in where clause of UPDATE statement

I have the database of ATM card in which there are fields account_no,card_no,is_blocked,is_activated,issue_date
Fields account number and card numbers are not unique as old card will be expired and marked as is_block=Y and another record with same card number ,account number will be inserted into new row with is_blocked=N . Now i need to update is_blocked/is_activated with help of issue_date i.e
UPDATE card_info set is_blocked='Y' where card_no='6396163270002509'
AND opening_date=(SELECT MAX(opening_date) FROM card_info WHERE card_no='6396163270002509')
but is doesn't allow me to do so
it throws following error
1093 - You can't specify target table 'card_info' for update in FROM clause
Try this instead:
UPDATE card_info ci
INNER JOIN
(
SELECT card_no, MAX(opening_date) MaxOpeningDate
FROM card_info
GROUP BY card_no
) cm ON ci.card_no = cm.card_no AND ci.opening_date = cm.MaxOpeningDate
SET ci.is_blocked='Y'
WHERE ci.card_no = '6396163270002509'
That's one of those stupid limitations of the MySQL parser. The usual way to solve this is to use a JOIN query as Mahmoud has shown.
The (at least to me) surprising part is that it really seems a parser problem, not a problem of the engine itself because if you wrap the sub-select into a derived table, this does work:
UPDATE card_info
SET is_blocked='Y'
WHERE card_no = '6396163270002509'
AND opening_date = ( select max_date
from (
SELECT MAX(opening_date) as_max_date
FROM card_info
WHERE card_no='6396163270002509') t
)

JOIN/UPDATE TABLES WITH CONDITIONAL STATEMENT?

i have a registry table ( id , counter , group_name , type )
and group tables which includes these tables
software_group
website_group
news_group
etc ..
every group is in the registry table as well as it's own group table
now if i want to update a row in the registry i want appropriate group table to updated too
type column indicates the group table so i have it something like :
UPDATE registry JOIN
CASE WHEN registry.type = 1 THEN software_group
CASE WHEN registry.type = 2 THEN website_group
CASE WHEN registry.type = 3 THEN news_group
AS other_table
ON registry.id = other_table.reg_id
SET registry.name = $newname
,
other_table.name = $newname
WHERE registry.id = $id
is it possible to do something like this ? i can just select the registry row and do the job with php but i thought join/update would be faster than select/update
I think the dynamic join to tables on run time will be best if you handle the conditions in application layer, rather than the DB layer