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
Related
I have the following scenario: I am trying to pass result of one query to another , In this case I am trying to pass view_id in another query since this are ids they are unique and cant be duplicate in any case .
select view_id from view where view_query_id = "18711987173"
select queue_id from queue where queue_view = view_id
`
I saw some examples and I tried executing them something like as
select view_id from view where view_query_id = "18711987173 exists (select queue_id from queue where queue_view = view.view_id)
But surely this didnt helped :)
You can use a common table expression
WITH temp AS (SELECT view_id FROM view WHERE view_query_id = "18711987173")
SELECT queue_id FROM queue q INNER JOIN temp t ON q.queue_id = t.view_id;
This should work regardless of what relationship is between those tables. You can replace the JOIN by WHERE..IN, but this way seems cleaner and takes care if the no. of values in IN becomes too large.
Use table expression
WITH temp AS (SELECT view_id FROM view WHERE view_query_id = "18711987173")
SELECT queue_id FROM queue q INNER JOIN temp t ON q.queue_id = t.view_id;
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.
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'
)
I have 2 tables: tbl_taxclasses, tbl_taxclasses_regions
This is a one to many relationship, where the main record ID is classid.
I have a column inside the first table called regionscount
So, I create a Tax Class, in table 1. Then I add regions/states in table 2, assigning the classid to each region.
I perform a SELECT statement to count the regions with that same classid, and then I perform an UPDATE statement on tbl_taxclasses with that number. I update the regionscount column.
This means I'm writing 2 queries. Which is fine, but I was wondering if there was a way to do a SELECT statement inside the UPDATE statement, like this:
UPDATE `tbl_taxclasses` SET `regionscount` = [SELECT COUNT(regionsid) FROM `tbl_taxclasses_regions` WHERE classid = 1] WHERE classid = 1
I'm reaching here, since I'm not sure how robust MySQL is, but I do have the latest version, as of today. (5.5.15)
You could use a non-correlated subquery to do the work for you:
UPDATE
tbl_taxclasses c
INNER JOIN (
SELECT
COUNT(regionsid) AS n
FROM
tbl_taxclasses_regions
GROUP BY
classid
) r USING(classid)
SET
c.regionscount = r.n
WHERE
c.classid = 1
Turns out I was actually guessing right.
This works:
UPDATE `tbl_taxclasses`
SET `regionscount` = (
SELECT COUNT(regionsid) AS `num`
FROM `tbl_taxclasses_regions`
WHERE classid = 1)
WHERE classid = 1 LIMIT 1
I just needed to replace my brackets [] with parenthesis ().
I have a table like this (MySQL 5.0.x, MyISAM):
response{id, title, status, ...} (status: 1 new, 3 multi)
I would like to update the status from new (status=1) to multi (status=3) of all the responses if at least 20 have the same title.
I have this one, but it does not work :
UPDATE response SET status = 3 WHERE status = 1 AND title IN (
SELECT title FROM (
SELECT DISTINCT(r.title) FROM response r WHERE EXISTS (
SELECT 1 FROM response spam WHERE spam.title = r.title LIMIT 20, 1)
)
as u)
Please note:
I do the nested select to avoid the famous You can't specify target table 'response' for update in FROM clause
I cannot use GROUP BY for performance reasons. The query cost with a solution using LIMIT is way better (but it is less readable).
EDIT:
It is possible to do SELECT FROM an UPDATE target in MySQL. See solution here
The issue is on the data selected which is totaly wrong.
The only solution I found which works is with a GROUP BY:
UPDATE response SET status = 3
WHERE status = 1 AND title IN (SELECT title
FROM (SELECT title
FROM response
GROUP BY title
HAVING COUNT(1) >= 20)
as derived_response)
Thanks for your help! :)
MySQL doesn't like it when you try to UPDATE and SELECT from the same table in one query. It has to do with locking priorities, etc.
Here's how I would solve this problem:
SELECT CONCAT('UPDATE response SET status = 3 ',
'WHERE status = 1 AND title = ', QUOTE(title), ';') AS sql
FROM response
GROUP BY title
HAVING COUNT(*) >= 20;
This query produces a series of UPDATE statements, with the quoted titles that deserve to be updated embedded. Capture the result and run it as an SQL script.
I understand that GROUP BY in MySQL often incurs a temporary table, and this can be costly. But is that a deal-breaker? How frequently do you need to run this query? Besides, any other solutions are likely to require a temporary table too.
I can think of one way to solve this problem without using GROUP BY:
CREATE TEMPORARY TABLE titlecount (c INTEGER, title VARCHAR(100) PRIMARY KEY);
INSERT INTO titlecount (c, title)
SELECT 1, title FROM response
ON DUPLICATE KEY UPDATE c = c+1;
UPDATE response JOIN titlecount USING (title)
SET response.status = 3
WHERE response.status = 1 AND titlecount.c >= 20;
But this also uses a temporary table, which is why you try to avoid using GROUP BY in the first place.
I would write something straightforward like below
UPDATE `response`, (
SELECT title, count(title) as count from `response`
WHERE status = 1
GROUP BY title
) AS tmp
SET response.status = 3
WHERE status = 1 AND response.title = tmp.title AND count >= 20;
Is using GROUP BY really that slow ? The solution you tried to implement looks like requesting again and again on the same table and should be way slower than using GROUP BY if it worked.
This is a funny peculiarity with MySQL - I can't think of a way to do it in a single statement (GROUP BY or no GROUP BY).
You could select the appropriate response rows into a temporary table first then do the update by selecting from that temp table.
you'll have to use a temporary table:
create temporary table r_update (title varchar(10));
insert r_update
select title
from response
group
by title
having count(*) < 20;
update response r
left outer
join r_update ru
on ru.title = r.title
set status = case when ru.title is null then 3 else 1;