I have read that A JOIN statement can connect two tables for update and I'm having a hard time warping my brain around it..
I have 7 tables and growing all different languages all different charsets.
I need to update and insert 3 columns..
sudo naming
table1EN
table1DE
table1ZH
table1PT
table1FR
table1ES
table1RU
The tables are 100% the same structure..
To do an update now I use this query
UPDATE `Table1EN` SET `Details`= 'String Value', `Name` = 'String Name',
`Info`= 1 WHERE `ID` = 1;
and then repeat 5000 times and change table1EN to table1** and re-run that
Is there a way to simplify this to?
UPDATE `Table1EN`,`Table1ZH`,`Table1DE`,`Table1FR`,`Table1RU`,`Table1ES`,`Table1PT`
SET `Details`= 'String Value', `Name` = 'String Name', `Info`= 1 WHERE `ID` = 1;
Running this query returns ambiguous name details, name and info.
using a join statement seems to lock up the server..
My questions are how can I run a multi-table update query where all values are the same nothing changes? Not lock up the server? Not have an ambiguous name error? Finally not have to run the query in chucks of 5000 after renaming the table?
Update1:
As Arth has pointed below, I did not include the actual JOIN Query here.
UPDATE table1EN
INNER JOIN table1ZH USING (ID)
INNER JOIN table1DE USING (ID)
INNER JOIN table1FR USING (ID)
INNER JOIN table1PT USING (ID)
INNER JOIN table1ES USING (ID)
INNER JOIN table1RU USING (ID)
SET table1EN.Info = 1, table1EN.Details ='String Value',
table1ZH.Info = 1, table1ZH.Details ='String Value',
table1DE.Info = 1, table1DE.Details ='String Value',
table1FR.Info = 1, table1FR.Details ='String Value',
table1ES.Info = 1, table1ES.Details ='String Value',
table1RU.Info = 1, table1RU.Details ='String Value',
table1PT.Info = 1, table1PT.Details ='String Value'
WHERE table1EN.ID = 1;
I'm posting it now in hopes to simplify it to stop the server from crashing anytime I try to run it with 5000 different query's at one time.
I have tried reducing this based on something I read
to
set table1EN.Info = 1, table1EN.Details ='String Value',
table1ZH.Info=table1EN.Info,
table1DE.Info=table1EN.Info,
table1FR.Info=table1EN.Info
etc ........
However this seems to cause even more server lag and crashes witch I would expect..
First off when you do the UPDATE JOIN, you haven't included any JOIN conditions.. so that will try and do a cartesian product of each table:
#rows = #rows_table1EN * #rows_table1ZH * ... * #rows_table1PT
You should JOIN on table1EN.id = table1xx.id for each table.
The next problem is that you will have to reference each column you want to change. This will result in:
SET table1EN.detail = 'String Value',
table1ZH.detail = 'String Value'
...
table1PT.detail = 'String Value'
This could be done with dynamically building the statement, but that is fairly hideous.
This all leads me to question your database structure. Did you consider using one table with an extra language column; either the two letter identifier
(OKish) or a foreign key to a languages table (better)?
I wonder if....
Update (
Select ID, details, name, info, 'table1EN' src FROM table1EN UNION ALL
Select ID, details, name, info, 'table1ZH' FROM table1ZH UNION ALL
Select ID, details, name, info, 'table1DE' FROM table1DE UNION ALL
Select ID, details, name, info, 'table1RU' FROM table1RU UNION ALL
Select ID, details, name, info, 'table1FR' FROM table1FR UNION ALL
Select ID, details, name, info, 'table1ES' FROM table1ES UNION ALL
Select ID, details, name, info, 'table1PT' FROM table1PT) b
set `details` = 'String value',
`Name` = 'String Name',
`Info` = 1
where ID=1
would work... based on https://dev.mysql.com/worklog/task/?id=3701
ID and src would be a composite key.
Related
This must be fairly straight forward, as I tend to use ORMs I don't have to get my hands dirty often and am therefore struggling!
I have a database and want to get several fields from a table, that bit is easy..
SELECT main_table.registration_number, main_table.registered_name FROM main_table;
I want to filter the results based on another table, which is also easy..
SELECT second_table.registration_number FROM second_table WHERE this_field = '' AND that_field = '0';
Now the problem is I want to run the first query based on the second queries result set, I was thinking something like this:
SELECT main_table.registration_number, main_table.registered_name FROM main_table WHERE main_table.registration_number IN (SELECT * FROM second_table WHERE this_field = '' AND that_field = '0');
This gives me: Error Code: 1241. Operand should contain 1 column(s)
Am I handling this completely wrong?
Your subquery should do something like below,
(select * from table) in subquery is not what you really need to do your
so the subquery should return one column
(SELECT registration_number FROM second_table WHERE this_field = '' AND that_field = '0');
You cannot have multiple columns being returned in a subquery like
that, doing so it will result in such error
You have to select a column
SELECT main_table.registration_number, main_table.registered_name FROM
main_table WHERE main_table.registration_number IN (SELECT
registration_number FROM second_table WHERE this_field = '' AND
that_field = '0');
I tried to come up with a query that updates records in a MySQL table using other records in the same table, but I had mixed results between local testing and production. I don't know much about subqueries, so I want to bring this question here. In local development with MySQL InnoDB 5.6.23, a query on a dataset of about 180k records take 25 to 30 seconds. On a staging server with MySQL InnoDB 5.5.32 and a dataset of 254k records, the query seems to stall for hours until it's stopped, taking 100% of a CPU core.
This is the query I came up with:
UPDATE
`product_lang` AS `pl1`
SET
pl1.`name` = (
SELECT pl2.`name` FROM (SELECT `name`, `id_product`, `id_lang` FROM `product_lang`) AS `pl2`
WHERE pl1.`id_product` = pl2.`id_product`
AND pl2.`id_lang` = 1
)
WHERE
pl1.`id_lang` != 1
The objective is to replace the value of name in product records where id_lang is not 1 (default language for the sake of explaining) with the value of name of records value with the default id_lang of 1.
I know that subqueries are inefficient, but I really don't know how to solve this problem, and it would be a great plus to leave this in SQL-land instead of using the app layer to do the heavy lifting.
If you write the query like this:
UPDATE product_lang pl1
SET pl1.name = (SELECT pl2.`name`
FROM (SELECT `name`, `id_product`, `id_lang`
FROM `product_lang`
) `pl2`
WHERE pl1.`id_product` = pl2.`id_product` AND pl2.`id_lang` = 1
)
WHERE pl1.`id_lang` <> 1
Then you have a problem. The only index that can help is on product_lang(id_lang).
I would recommend writing this as a join:
UPDATE product_lang pl1 join
(select id_product, pl.name
from product_lang
where id_lang = 1
) pl2
on pl1.id_lang <> 1 and pl2.id_product = pl1.id_product
SET pl1.name = pl2.name
WHERE pl1.id_lang <> 1
The index that you want for this query is product_lang(id_lang, id_product) and product_lang(id_product). However, this seems like a strange update, because it will set all the names to the name from language 1.
UPDATE product_lang AS pl1
JOIN product_lang AS pl2
ON pl1.`id_product` =
pl2.`id_product`
SET pl1.name = pl2.name
WHERE pl2.`id_lang` = 1
AND pl1.`id_lang` != 1;
And have INDEX(id_lang, id_product).
Make sure that there is an index specifying columns id _ product and id_lang.
update pl1
set pl1.name=pl2.name
from product_lang pl1
,product_lang pl2
where pl1.id_product = pl2.id_product AND pl2.id_lang = 1
and pl1.id_lang <> 1
The composit index that will be required will be id_product and id_lang for
I'm trying to write a query like
if (select count(*) from Users where fkId=5000 and status='r') =
(select count(*) from Users where fkId=5000) then ..
in just one query.
What this means is, if all the rows that have fkId=5000 also have status=r, then do something.
There can be any number of rows with fkId=5000, and any fraction of those rows could have status=r, status=k, status=l, status=a etc. I'm interested in the case where ALL the rows that have fkId=5000 also have status=r (and not any other status).
The way I'm doing it now is
how many rows with id=5000 and status = 'r'?
how many rows with id=5000?
are those numbers equal? then ..
I'm trying to figure out how to rewrite this query using only 1 query, instead of 2. Keyword ALL didn't seem to be able to write such a query (<> ALL is equivalent to NOT IN). I tried a couple of GROUP BY formulations but could not get the correct result to appear.
The most efficient way to do this is:
if not exists (select 1
from users
where fkid = 5000 and (status <> 'r' or status is null)
)
This will stop the query at the first non-matching row.
I suggest you to check for any rows with status not equal to 'r'
SELECT count(*)>0 FROM Users WHERE fkId = 5000 AND status != 'r'
In the following case, if the number 1 is "true" (which it is) then you'll get Yes back, and if not you'll get No back:
SELECT IF(1, 'Yes', 'No') AS yesorno
(Go ahead -- try it!)
In your case however, the following would be more appropriate:
SELECT IF (
(SELECT COUNT(*) FROM Users WHERE fkId=5000 AND status IN('r') AND status NOT IN('1', 'a', 'k')) = (SELECT COUNT(*) FROM Users WHERE fkId=5000),
'They are equal.',
'They are not equal.'
)
AS are_they_equal
By adding AS, you can manipulate the name of the "column" that's returned to you.
Hope that helps... Also, see this page if you'd like more info.
:)
EASY!
Simply join back to the same table. Here is the complete code for testing:
CREATE TABLE Users(id int NOT NULL AUTO_INCREMENT, fkID int NOT NULL, status char(1), PRIMARY KEY (id));
INSERT Users (fkID, status) VALUES (5000, 'r');
INSERT Users (fkID, status) VALUES (5000, 'r');
INSERT Users (fkID, status) VALUES (5000, 'r');
-- The next query produces "0" to indicate no miss-matches
SELECT COUNT(*) FROM Users u1 LEFT JOIN Users u2 ON u1.id=u2.id AND u2.status='r' WHERE u1.fkID=5000 AND u2.id IS NULL;
-- now change one record to create a miss-match
UPDATE Users SET status='l' WHERE id=3 ;
-- The next query produces "1" to indicate 1 miss-match
SELECT COUNT(*) FROM Users u1 LEFT JOIN Users u2 ON u1.id=u2.id AND u2.status='r' WHERE u1.fkID=5000 AND u2.id IS NULL;
DROP TABLE Users;
So all you need to test for in the result is that it's 0 (zero) meaning everything has fkID=5000 also has status='r'
If you properly index your table then joining back to the same table is not an issue and certainly beats having to do a 2nd query.
Besides the NOT EXISTS version - which should be the most efficient as it does no counting at all and exits as soon as it finds a value that doesn't match the conditions, there is one more way, that will work if status is not nullable and will be efficient if there is an index on (fkId, status):
IF EXISTS
( SELECT 1
FROM Users
WHERE fkId = 5000
HAVING MIN(status) = 'r'
AND MAX(status) = 'r'
)
There is one difference though. The above will show false if there are no rows at all with fkId=5000, while the NOT EXISTS version will show true - which is probably what you want anyway.
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;
Is there a way to update multiple rows with different values for each row using a single SQL query? I have to update one colum in many rows with different data. Using individual update queries for each row seems excessive so if it's possible I would like to consolidate this process into a single SQL statement or at least reduce the number of queries required.
I am using PHP with the Zend framework and MySql.
Create a temporary table and fill it with:
CREATE TEMPORARY TABLE temptable (id INTEGER, VALUE VARCHAR(200))
INSERT
INTO temptable
VALUES
('1', 'val1'),
('2', 'val2'),
('3', 'val3'),
('4', 'val4')
Then issue:
UPDATE
mytable m, temptable t
SET m.value = t.value
WHERE m.id = t.id
Don't know about MySQL specifically, but to update multiple rows based on a SELECT, or a UNION of multiple SELECTs, I would do
UPDATE U
SET MyColumn = T.OtherColumn
FROM MyUpdateTable AS U
JOIN
(
SELECT [OtherColumn] = OtherColumn1
FROM MyOtherTable1
WHERE ...
UNION
SELECT OtherColumn2
FROM MyOtherTable2
WHERE ...
) AS T
ON T.ID = U.ID
Update 10/28/2014, converted to work for MySQL:
UPDATE MyUpdateTable AS U
JOIN
(
SELECT [OtherColumn] = OtherColumn1
FROM MyOtherTable1
WHERE ...
UNION
SELECT OtherColumn2
FROM MyOtherTable2
WHERE ...
) AS T
ON T.ID = U.ID
SET MyColumn = T.OtherColumn
I know this works for SQL Server, so it's worth a try in MySQL.
update xtable
set a =
Case
when a = "a"
then z
when a = "b"
then y
End
where ...
You can construct the case statement based on your different rows.