Please is there any way how to optimize this update query in MySql?
UPDATE table1 t1
SET t1.column =
(SELECT MIN(t2.column)
FROM table2 t2 WHERE t1.id = t2.id
);
Both tables have around 250 000 records.
Table structure:
CREATE TABLE `table1` (
`id` int(11) NOT NULL,
`column` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
CREATE TABLE `table2` (
`code` int(11) NOT NULL,
`id` int(11) NOT NULL,
`column` datetime NOT NULL,
PRIMARY KEY (`code, `id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
ALTER TABLE table2 ADD CONSTRAINT FK_id
FOREIGN KEY (id) REFERENCES table1 (id)
;
Thank you for help.
this is how I would do it :
create a temporary table to hold aggregated values
CREATE TEMPORARY TABLE tmp_operation
SELECT id, MIN(`column`) as cln FROM table2 GROUP BY id;
add index to temporary table for faster join to table 1 (can omit this step depending on data size)
ALTER TABLE tmp_operation ADD UNIQUE INDEX (id);
update with simple join. you can use left or inner join depending if you want to update columns to nulls)
UPDATE table1
SET table1.`column` = tmp_operation.cln
INNER JOIN tmp_operation ON table1.id = tmp_operation.id;
drop temporary table after done
DROP TABLE tmp_operation;
You could do it by first grouping table t2 and then use JOIN (this is similar to #frail's answer but without the temporary table):
UPDATE
table1 t1
JOIN
( SELECT id
, MIN(column) AS min_column
FROM table2
GROUP BY id
) AS t2
ON t2.id = t1.id
SET t1.column = t2.min_column ;
An index at table2, on (id, column) would help performance.
Add forign key in table2 of table1 primary key
UPDATE table1 t1
INNER JOIN table2 t2
ON t1.id = t2.id
SET t1.column = t2.column
having MIN(t2.column)
examples
Related
I have a table that looks like this:
id int primary key
uniqueID string --not uniquely indexed
foreignKeyID int --foreignKey to another table
I want to find all the uniqueIds in this table that exist for foreign key 1 that do not exist for foreign key 2
I thought I could do something like this:
SELECT * FROM table t1
LEFT JOIN table t2
ON t1.uniqueID = t2.uniqueID
WHERE
t1.foreignKeyID = 1
AND t2.uniqueID IS NULL
However this is never giving me results. I can make it work with a NOT IN subquery but this is a very large table so I suspect a solution using joins will be faster.
Looking for the best way to structure this query.
Here's an sample data set and SQL Fiddle with an example of the working NOT IN query I am trying to convert to a LEFT JOIN:
CREATE TABLE `table` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`uniqueID` varchar(255),
`foreignKeyID` int(5) unsigned NOT NULL DEFAULT 0,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
INSERT INTO `table` (uniqueID, foreignKeyID) VALUES ('aaa', 1), ('bbb', 1);
http://sqlfiddle.com/#!9/48a3f3/4 and a non-working LEFT JOIN I thought would be equivalent.
Thanks!
Try this, seems to be working if understood the question properly:
SELECT *
FROM `table` t
LEFT JOIN `table` tt ON tt.uniqueID = t.uniqueID AND tt.foreignKeyID <> 1
WHERE t.foreignKeyID = 1 AND tt.id IS NULL;
I have a MySQL database with three relevant tables, t1 with 6598 rows, t2 with 1713 rows and t3 with 10023 rows.
Details:
TABLE t1 (
`id` SERIAL,
`t2_id` BIGINT UNSIGNED NOT NULL,
`t3_id` BIGINT UNSIGNED,
PRIMARY KEY (`id`),
FOREIGN KEY (t2_id) REFERENCES t2(id),
FOREIGN KEY (t3_id) REFERENCES t3(id)
);
TABLE t2(
`id` SERIAL,
`name` VARCHAR(128) NOT NULL,
PRIMARY KEY (`id`)
);
TABLE t3 (
`id` SERIAL,
`name` VARCHAR(128),
PRIMARY KEY (`id`)
);
I want perform the following query but it does not finish (takes forever basically):
SELECT *
FROM t1
INNER JOIN t3
ON t1.t3_id = t3.id
INNER JOIN t2
ON t1.t2_id = t2.id
WHERE (t3.name NOT NULL)
ORDER BY t3.name ASC , t1.id ASC
LIMIT 25
When i remove the order clause it works very fast (0.17 sec).
How could i change the query to make it work?
I can suggest the following indices:
CREATE INDEX idx_t1 ON t1 (t2_id, t3_id, id);
CREATE INDEX idx_t3 ON t3 (id, name);
These indices at the very least should substantially speed up the joins. If used, MySQL would most likely be taking the following joining strategy:
SELECT *
FROM t2
INNER JOIN t1
ON t2.id = t1.t2_id
INNER JOIN t3
ON t1.t3_id = t3.id
WHERE
t3.name IS NOT NULL
ORDER BY
t3.name,
t1.id
LIMIT 25;
The idea here is that we do a full table scan on t2, which is by far the smallest table. There are no restrictions on the records in t2 anyway, so we might as well scan this first. Then, for each of the joins to t1 and t3 we try to use the indices setup. Note that because your tables have relatively few columns, the two indices defined can easily cover all columns, thereby increasing the likelihood that MySQL will choose to use the indices.
t1 is a many:many mapping table. It does not need a surrogate id for the PK. Instead it needs a composite PK of the two other columns, Plus an index the other direction.
See http://mysql.rjweb.org/doc.php/index_cookbook_mysql#many_to_many_mapping_table for discussion of those and several other tips.
I use this query (insert into select 3 tables) to insert my rows in the mysql table.
"INSERT INTO test (catid_1, descat_1, catid_2, descat_2, id_user, user)
SELECT '$_POST[cat_1]',t1.desc AS descat_1, '$_POST[cat_2]', t2.desc AS descat_2,
$_POST[id_user]',t3.user FROM t1, t2, t3
WHERE t1.idcat_1='$_POST[cat_1]' and t2.idcat_2='$_POST[cat_2]'
and t3.id_user='$_POST[id_user]'";
Now I'd like to use the same logic to UPDATE my rows (update into select 3 table) in the mysql table.
tables structure
t1
`idcat_1` int(11) NOT NULL AUTO_INCREMENT,
`desc` varchar(100) NOT NULL,
PRIMARY KEY (`idcat_1`)
t2
`idcat_2` int(11) NOT NULL AUTO_INCREMENT,
`idcat_1` int(11) NOT NULL,
`desc` varchar(100) NOT NULL,
PRIMARY KEY (`idcat_2`)
t3
`id_user` int(11) NOT NULL AUTO_INCREMENT,
`user` varchar(40) NOT NULL,
PRIMARY KEY (`id_user`)
Is it possible to do?
Thanks
Like this:
UPDATE test AS t
INNER JOIN t1 ON -- join conditon
INNER JOIN t2 ON ...
INNER JOIN t3 ON ...
SET t.catid_1 = '$_POST[cat_1]',
t.descat_1 = t1.desc,
....
WHERE t1.idcat_1='$_POST[cat_1]'
and t2.idcat_2='$_POST[cat_2]'
and t3.id_user='$_POST[id_user]'
It is not clear how the four tables are joined, you will need to supply the join condition for each JOIN.
Update 1
From the tables' structures that you just posted in your updated question, it seems like neither of the three tables test, t1 nor t2 related to the table t3 by any keys. In this key you didn't need to join with this table t3 and only join the tables test, t1 and t2. Assuming that:
test relates to t1 with test.catid_1 = t1.idcat_1.
t1 relates to t2 with t1.idcat_1 = t2.idcat_1.
Like this:
UPDATE test AS t
INNER JOIN t1 ON t.catid_1 = t1.idcat_1
INNER JOIN t2 ON t1.idcat_1 = t2.idcat_1
SET t.catid_1 = '$_POST[cat_1]',
t.descat_1 = t1.desc,
....
WHERE t1.idcat_1='$_POST[cat_1]'
and t2.idcat_2='$_POST[cat_2]'
I have 2 table t1 -> t2 (common one to many relationship) with 140.000 records on table t2 reffering 50.000 records on t1, foreing key some times is null (no parent).
CREATE TABLE `t1` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL,
PRIMARY KEY (`id`),
KEY `name_idx` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=125666 DEFAULT CHARSET=utf8
CREATE TABLE `t2` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL,
`t1_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `t1_id_idx` (`t1_id`)
CONSTRAINT `t1_fk` FOREIGN KEY (`t1_id`) REFERENCES `t1` (`id`),
) ENGINE=InnoDB AUTO_INCREMENT=125666 DEFAULT CHARSET=utf8
This query running on 15 seconds:
SELECT * FROM t2
LEFT JOIN t1 ON t2.t1_id = t1.id
ORDER BY t1.name ASC
LIMIT 10;
This query running on 0.5 seconds:
SELECT * FROM t2
LEFT JOIN t1 ON t2.t1_id = t1.id
WHERE t1.name <> 'any not found value'
ORDER BY t1.name ASC
LIMIT 10;
Can any body explain to me why this occurs?
Obs: Edited.
News:
This query running on 0.06 seconds: (WHAT'S CHANGE? inner join!!)
SELECT * FROM t2
INNER JOIN t1 ON t2.t1_id = t1.id
ORDER BY t1.name ASC
LIMIT 10;
but above query does not is a solution for my, in my case t2.t1_id can be null some times.
Any Idea??
News:
Running explain with left and inner join:
Mysql show: Using temporary; Using filesort; Rows: 140.000
With Inner Join:
Mysql show: Using Where; Rows: 8
Solved!
The Problem is on order by, when using order by mysql create a temporaty file (Explain...Using Temporary), this temporary file is too big causing the lag.
Tips:
Avoid Using Tempoaray
When Using temporary don't load much data.
I suspect you already have an index on t1.name but it is descending not ascending. That explains why the second query is so much faster.
The other explanation is the first query was not cached but the second query found data in the cache and ran faster.
I'm trying to use SQL to delete multiple rows from multiple tables that are
joined together.
Table A is joined to Table B
Table B is joined to Table C
I want to delete all rows in table B & C that correspond to a row in Table A
CREATE TABLE `boards` (
`boardid` int(2) NOT NULL AUTO_INCREMENT,
`boardname` varchar(255) NOT NULL DEFAULT '',
PRIMARY KEY (`boardid`)
);
-- --------------------------------------------------------
--
-- Table structure for table `messages`
--
CREATE TABLE `messages` (
`messageid` int(6) NOT NULL AUTO_INCREMENT,
`boardid` int(2) NOT NULL DEFAULT '0',
`topicid` int(4) NOT NULL DEFAULT '0',
`message` text NOT NULL,
`author` varchar(255) NOT NULL DEFAULT '',
`date` datetime DEFAULT NULL,
PRIMARY KEY (`messageid`)
);
-- --------------------------------------------------------
--
-- Table structure for table `topics`
--
CREATE TABLE `topics` (
`topicid` int(4) NOT NULL AUTO_INCREMENT,
`boardid` int(2) NOT NULL DEFAULT '0',
`topicname` varchar(255) NOT NULL DEFAULT '',
`author` varchar(255) NOT NULL DEFAULT '',
PRIMARY KEY (`topicid`)
);
Well, if you had used InnoDB tables, you could set up a cascading delete with foreign keys that would do it all automatically. But if you have some reason for using MyISAM, You just use a multiple-table DELETE:
DELETE FROM boards, topics, messages
USING boards INNER JOIN topics INNER JOIN messages
WHERE boards.boardid = $boardid
AND topics.boardid = boards.boardid
AND messages.boardid = boards.boardid;
this can be done by your db-system if you are using foreign keys with "on delete cascade".
Take a look here:
http://dev.mysql.com/doc/refman/5.1/en/innodb-foreign-key-constraints.html
You could either just check for presence
delete from topics where boardid in (select boardid from boards)
delete from messages where boardid in (select boardid from boards)
but this would only make sense if this behaviour should not always apply. When the behaviour should always apply, implement foreign keys with delete on cascade
explained on a zillion sites, in your helpfiles and here
Deleting rows from multiple tables can be done in two ways :
Delete rows from one table, determining which rows to delete by referring to another
table
Delete rows from multiple tables with a single statement
Multiple-table DELETE statements can be written in two formats. The following example demonstrates one syntax, for a query that deletes rows from a table t1 where the id values match those in a table t2:
DELETE t1 FROM t1, t2 WHERE t1.id = t2.id;
The second syntax is slightly different:
DELETE FROM t1 USING t1, t2 WHERE t1.id = t2.id;
To delete the matching records from both tables, the statements are:
DELETE t1, t2 FROM t1, t2 WHERE t1.id = t2.id;
DELETE FROM t1, t2 USING t1, t2 WHERE t1.id = t2.id;
The ORDER BY and LIMIT clauses normally supported by UPDATE and DELETE aren’t allowed when these statements are used for multiple-table operations.