Can I update a table by inner join? - mysql

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.

Related

Error 1093 when trying to perform delete in MySQL

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.

Does the join order matter in SQL (MySQL specific) - NOT table in FROM clause

This is not a duplicate of this Q&A because the question and answers here concerns the table mentioned in the FROM clause. Which mine doesn't.
Assuming the table in the FROM clause is always the same and I'm never going to change it. Does it matter which order I add my joins?
I am using an in-house built query builder. (Yes I know there are things out there already but that's out of scope for the question).
I want to be able to set some of the joins at the beginning of my script and some later based on conditionals, the query builder adds them to the query from the top down. Will the SQL engine optimize the order of the joins anyway, regardless of their order in the query?
example:
SELECT a.col1, d.col2, c.col1, b.col3
FROM table1 A
INNER JOIN table2 B
ON B.a_id = A.id
LEFT JOIN table3 C
ON C.id = A.c_id
LEFT JOIN table4 D
ON D.id = C.d_id;
SELECT a.col1, d.col2, c.col1, b.col3
FROM table1 A
LEFT JOIN table4 D
ON D.id = C.d_id
INNER JOIN table2 B
ON B.a_id = A.id
LEFT JOIN table3 C
ON C.id = A.c_id;
Here you can see that I have declared the join for table4 D before the join for it's dependent table is declared in the script (C). Does this matter?
Simple answer: No you can't reference a table object/alias before the object has been declared.
mySQL will throw an error on 2nd query. `Unknown column 'C.d_id' in 'on clause'
So yes... the compiler doesn't look ahead to see if it's been referenced later.. It only knows the order first then it tries to figure out which method of joining is best.
SQLFiddle
*To address question of: Will the SQL engine optimize the order of the joins anyway, regardless of their order in the query? *
Yes it would optimize the order; but the "FROM" order can't include a reference to a table before it's been declared or the query will not compile. (See error above and link for example)

Join/Update between Three Tables

Currently to get data updated in Table3 that depends on conditions in Tables1 and Table2 I am doing this:
Update Table_B as T1
Inner Join Table_A as T2
On T1.S_ID=T2.ID
Set T1.Percent = T2.Percent
Update Table_C as T1
Inner Join Table_B as T2
On T1.ID=T2.J_ID
Set T1.B = T2.B
Where T2.Percent=100
I would like to not store or update TableB with TableA Percent and somehow do the join in a single statement.
update table_b T1 set T1.Percent=(select T2.Percent from table_a T2 where T1.S_ID=T2.ID)
update table_c T1 set T1.B=(select T2.B from table_b T2 where T1.ID=T2.J_ID) where exists(select * from table_b T3 where T1.ID=T3.J_ID and T3.percent=100)
This isn't valid syntax in an UPDATE statement
Set T1.Percent as T1.Percent
^^
And this isn't valid
Set T1.B as T2.B
^^
The syntax in the UPDATE statements in the question do not look valid.
The SET assignment operator is an equals sign, not an As keyword.
And T1.Percent = T1.Percent doesn't make much sense, since there doesn't seem to be any point to setting a column to its current value.
It's not clear what it is you are actually trying to achieve, without some example data, and the desired end result.
To write a multi-table UPDATE statement, I first write a SELECT statement that returns the primary (or unique) keys of the tables that I am going to update, along with the current values of the columns to be updated, and expressions that return the new values that are going to be assigned to the columns
Based on the SQL in the question, it looks like you might want two joins, something like this:
SELECT ...
FROM table_a a
JOIN table_b b
ON b.s_id = a.id
JOIN table_c c
ON c.id = b.j_id
WHERE a.percent = 100
ORDER BY ...
(We don't know anything about the cardinality of the columns and relationships, whether those are one-to-one, one-to-many, zero-or-one-to-one, etc. We're just guessing.)
To evaluate and test the query, I'd include the primary keys of the tables. I'm going to guess id is the primary key of (the unfortunately named) Table_A and Table_C. And guess that the tuple (j_id,s_id) is a UNIQUE KEY in Table_B.
I will also include the column to be updated (to show the current value), and an expression that returns the new value to be assigned to the column.
Something like this:
SELECT a.id AS `a__id`
, a.percent AS `a__percent`
, b.j_id AS `b__j_id`
, b.s_id AS `b__s_id`
, c.id AS `c__id`
, c.b AS `c__b_old`
, b.b AS `c__b_new`
FROM table_a a
JOIN table_b b
ON b.s_id = a.id
JOIN table_c c
ON c.id = b.j_id
WHERE a.percent = 100
ORDER
BY c.id
, a.id
I would test that, and make sure it's returning the rows from Table_C that I want to update, and that the value returned for the c__b_new expression is the value that I want to assign to the b column in Table_C.
Once I get the SELECT statement working correctly (and only after I get it working correctly), I convert that to a multitable UPDATE.
Just replace the SELECT ... FROM part with the keyword UPDATE.
And before the WHERE clause, add a SET clause that does the assignment of the new value to the column. (I use the same expression I used in the SELECT list to return the new value.)
UPDATE table_a a
JOIN table_b b
ON b.s_id = a.id
JOIN table_c c
ON c.id = b.j_id
SET c.b = b.b
WHERE a.percent = 100
That's just an example of how I go about writing a multi-table UPDATE. There is no guarantee that this statement is going to work for what you need. Again, it's not clear what you are trying to achieve; the specification is too vague; so we're only guessing.

Add a column with a value from another table with if condition [duplicate]

I want to update a table in a statement that has several joins. While I know the order of joins doesn't really matter (unless you you are using optimizer hints) I ordered them a certain way to be most intuitive to read. However, this results in the table I want to update not being the one I start with, and I am having trouble updating it.
A dummy example of what I'd like to do is something like:
UPDATE b
FROM tableA a
JOIN tableB b
ON a.a_id = b.a_id
JOIN tableC c
ON b.b_id = c.b_id
SET b.val = a.val+c.val
WHERE a.val > 10
AND c.val > 10;
There are many posts about updating with joins here however they always have table being updated first. I know this is possible in SQL Server and hopefully its possible in MySQL Too!
The multi-table UPDATE syntax in MySQL is different from Microsoft SQL Server. You don't need to say which table(s) you're updating, that's implicit in your SET clause.
UPDATE tableA a
JOIN tableB b
ON a.a_id = b.a_id
JOIN tableC c
ON b.b_id = c.b_id
SET b.val = a.val+c.val
WHERE a.val > 10
AND c.val > 10;
There is no FROM clause in MySQL's syntax.
UPDATE with JOIN is not standard SQL, and both MySQL and Microsoft SQL Server have implemented their own ideas as an extension to standard syntax.
You have the ordering of the statements wrong. You can read up on the syntax here (I know, it's pretty hard to read.
UPDATE tableA a
JOIN tableB b
ON a.a_id = b.a_id
JOIN tableC c
ON b.b_id = c.b_id
SET b.val = a.val+c.val
WHERE a.val > 10
AND c.val > 10;
sql fiddle
Another correct construction, which we can use in this situation:
UPDATE T1, T2,
[INNER JOIN | LEFT JOIN] T1 ON T1.C1 = T2.C1
SET T1.C2 = T2.C2,
T2.C3 = expr
WHERE condition
The above example is take from: MySQL UPDATE JOIN.
Reaching for the MySQL 8.0 Reference Manual we will find such a description of multiple-table UPDATE syntax:
UPDATE [LOW_PRIORITY] [IGNORE] table_references
SET assignment_list
[WHERE where_condition]
The table_references clause lists the tables involved in the join.
So multiple-table MySQL's syntax doesn't support FROM, ORDER BY or LIMIT clauses as opposed to single-table syntax.
This link should give you the syntax that MySQL needs and here is an example. Why do you need to join the two tables? is it to limit the records updated? I am asking because you can also do something like the following:
update B set B.x=<value>
where
B.<value> is in(
select A.y
from A left outer join B on A.<value>=B.<value>
)

What's the purpose of specifying a condition in 'Full Outer Join' in MySQL?

I've a following query of 'Full Outer Join':
SELECT Customers.CustomerName, Orders.OrderID
FROM Customers
FULL OUTER JOIN Orders
ON Customers.CustomerID=Orders.CustomerID
ORDER BY Customers.CustomerName;
As per my knowledge The purpose of 'Full Outer Join' in MySQL is to return all the rows from the left table (Customers), and all the rows from the right table (Orders).
If there are rows in "Customers" that do not have matches in "Orders", or if there are rows in "Orders" that do not have matches in "Customers", those rows will be listed as well.
My doubt/query is if the 'Full Outer Join' in MySQL is going to return rows from both the tables irrespective of a matching element then is it necessary to specify a condition in WHERE clause? Can't I skip it?
Please give me canonical and to the point answer with respect to MySQL RDBMS.
Thanks.
When you specify something in the WHERE clause you have to make sure, that you check for NULL values, too, like this:
...
WHERE (col1 = 'whatever' OR col1 IS NULL)
AND (col2 = 'other_value' OR col2 IS NULL)
...
If you don't check for NULL your FULL OUTER JOIN actually becomes an INNER JOIN or LEFT/RIGHT JOIN.
If you had a typo and meant "why specify a condition in the ON clause?", then the answer is that without an ON clause your JOIN is actually a CROSS JOIN, which would mean, that every row of one table is associated with every row from the other table, which is a totally different thing.
To make this answer more MySQL specific, there is actually no FULL OUTER JOIN in MySQL and you have to emulate it with LEFT JOIN ... UNION ... RIGHT JOIN. But still the same principal applies.
a left join b using (id)
where b.col1 = 'x'
is the same as
a inner join b using (id)
where b.col1 = 'x'
and you actually have to write it like
a left join b on a.id = b.id and b.col1 = 'x'
to really have a left join. Or of course
a left join b using (id)
where b.col1 = 'x' or b.col1 is null