Combine 2 update querys in a single query - performance - mysql

Im programming a favourite function.
For example we have multiple adresses and can choose one as favourite.
At the moment i got 2 querys to do this job:
UPDATE userdata
SET maindata = 0
WHERE
cid = :id;
UPDATE userdata
SET maindata = 1
WHERE
cid = :id AND id = :id2
LIMIT 1
In the first query i make all adresses as "no favourite" and in the second one i make the new choosen adress the favourite one.
Is there any way to imrpove this query or rewrite both into 1 ? Or even a better solution ?

If you want a single query you could use a case when (or an if)
update userdata
set maindate = case when id = :id2 then 1
else 0 end
where cid = :id;
for performance Be sure you have a proper index on userdata columns (cid, id)
and the number of rows scanned should be the same for the first wuary ..but in this way you avoid the second ..
eventually try create a composite index
create index myidx1 on userdata(cid, id)

UPDATE userdata SET maindata = (case when cid = id AND id = id2 then 1 else 0 end);
This will help. I am not sure of your query but this will help. let me know if you looking something different...

Related

Update column based on value in another table without joins

Up front, I'm in a DB class and could use a hint to get closer to the correct answer.
In the ticket_old table there is the first and last name of technicians. Only two unique names.
In the new ticket table, I've got a tech_id column which needs the int matching the last_name of the tech found in the ticket_old table.
I've been trying to do this using the code below, which executes successfully and updates 0 rows.
UPDATE ticket,ticket_old
SET tech_id = (CASE WHEN ticket_old.techLast = 'name1' THEN 1
WHEN ticket_old.techLast = 'name2' THEN 2
END)
;
-edit, I also tried the following which runs and updates 0 rows.
UPDATE ticket,
(SELECT techLast FROM ticket_old WHERE techLast = 'name1') as src
SET ticket.tech_id = 1;
When Comparing two values,
Always use Double Equal marks:
SET tech_id = (CASE WHEN ticket_old.techLast == 'name1' THEN 1
WHEN ticket_old.techLast == 'name2' THEN 2
END)
I'm not sure if it is 'name' or "name". Try it.

Writing a correlated sub-query in my select query

I have a funky query that works fine with static data but I need my data to be dynamic. So the static data is like this
SELECT c.my_name, c.my_id, (SELECT count(d.friendship_id) FROM another_table d WHERE d.my_id = 1 AND d.my_friends_id = 2) as count FROM myprofile c WHERE c.my_id = 1;
This returns the data I want like this:
my_name my_id count
parijat 123 1 (OR 0 if the row doesn't exist)
For reference, both another_table.my_id (foreign key), another_table.my_friends_id references myprofile.my_id (primary key). another_table.friendship_id is the primary key here and is auto incremented.
Now the actual question:
I want my subquery to be something like this:
(SELECT count(d.friendship_id) FROM another_table d WHERE d.my_id = 1 AND d.my_friends_id = CURRENT_ROW_ID)
where CURRENT_ROW_MY_ID is the c.my_id that is being selected upon in the main query.
Is this possible and if not, what should my approach be to get the results I need ?
You can do a subquery to get the current auto_increment value for that table:
select auto_increment from information_schema.tables where table_schema = 'you_db_name' and table_name = 'your_table_name'
HTH
Francisco
Sometimes I ask before I have completely explored the option. Just found out that a correlated subquery works fine even in select statements. Here is what I did to get it working:
SELECT c.my_name, c.my_id, (SELECT count(d.friendship_id) FROM another_table d WHERE d.my_id = 1 AND d.my_friends_id = c.my_id) as count FROM myprofile c WHERE c.my_id = 1;
my_id is slightly ambiguous. A better word for it would be profile_id, however dealing with a legacy database ain't fun for sure.

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

mysql update multiple rows, each with its own values, with a CASE statement

I'm trying to update two fields of several rows at once but I can't determine the right syntax to do so, except for doing so with one field update.
Each row is identified by an id, and therefore I'm using a CASE statement.
I have this table:
tbl_accounts(id_account, nation_id,
group_id)
Now, the following query works for updating only one field:
UPDATE tbl_accounts SET nation_id = CASE id_account
WHEN 3 THEN 333
WHEN 5 THEN 555
ELSE nation_id END
The above will update the nation_id field of each corresponding row identified by its id_account.
And the following query doesn't work for updating two fields - please suggest a fix to the syntax. I'm trying to avoid using any SELECT/JOIN/etc':
UPDATE tbl_accounts SET nation_id = CASE id_account, group_id = CASE id_account
WHEN 3 THEN 3331, 3332
WHEN 5 THEN 5551, 5552
ELSE nation_id, group_id END
I could run this as two separate statements but I'm sure there's a way to combine the two into one.
Any help is highly appriciated!
It sounds like you are looking for something like this:
UPDATE tbl_accounts
SET nation_id =
CASE id_account
WHEN 3 THEN 3331
WHEN 5 THEN 5551
ELSE nation_id
END,
group_id =
CASE id_account
WHEN 3 THEN 3332
WHEN 5 THEN 5552
ELSE group_id
END
But doing separate updates is a sensible solution in this situation. The above query will require checking every row in the table to see if it matches the condition. If you have an index on id_account (and presumably you do as it appears to be the primary key) then it will be very fast to update a single row.
UPDATE tbl_accounts SET nation_id = 3331, groupid = 3332 WHERE id_account = 3
UPDATE tbl_accounts SET nation_id = 5551, groupid = 5552 WHERE id_account = 5

Update with SELECT and group without GROUP BY

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;