Reputation: 3258
I have a SQL like this:
INSERT INTO table1 (column1, column2) (
SELECT column3, column4 FROM table2 WHERE column5 = 'value'
);
table1
has 3,500,000 rows.table2
has 900,000 rows.SELECT column3, column4 FROM table2 WHERE column5 = 'value'
returns NO registers (ZERO) and takes ~0.004 seconds.INSERT INTO table1 (column1, column2) VALUES ('value', 'value')
takes ~0.004 seconds as well.But when I combine both in an INSERT INTO SELECT
statement, as shown above, it takes ~7.7 seconds. Is there an explanation? And is there a solution?
Upvotes: 8
Views: 10910
Reputation: 2813
There are a couple of additional things to try apart from the things already mentioned.
table2
.column5
which you probably already have.DISTINCT
in the select.Upvotes: 0
Reputation: 3258
If I remember well, as a workaround I replaced theSELECT
with a list of VALUES
generated programmatically. Example:
INSERT INTO employees (employee_id, first_name, last_name, job_title)
VALUES
(1, 'John', 'Doe', 'Software Engineer'),
(2, 'Jane', 'Smith', 'Database Administrator'),
(3, 'Bob', 'Johnson', 'Quality Assurance Analyst');
That runs really fast.
Unfortunately, for INSERT INTO SELECT
, I never found a solution.
Upvotes: 0
Reputation: 7476
The reason for slowing down performance is quite simple. It's one of the few cases where InnoDB performs a lock on table t2 to prevent inconsistencies in (statement based) replication.
From the MySQL manual:
INSERT INTO T SELECT ... FROM S WHERE ... sets an exclusive index record lock (without a gap lock) on each row inserted into T. If the transaction isolation level is READ COMMITTED, InnoDB does the search on S as a consistent read (no locks). Otherwise, InnoDB sets shared next-key locks on rows from S. InnoDB has to set locks in the latter case: During roll-forward recovery using a statement-based binary log, every SQL statement must be executed in exactly the same way it was done originally.
Possible workarounds (see also Harrison Fisk's blog, written 14 years ago):
If you're using statement based replication, probably the only workaround would be
SELECT * FROM table2 INTO outfile 'tmp.txt';
LOAD DATA INFILE 'tmp.txt' INTO TABLE table1;
If you are using row based replication, set the transaction isolation level to READ COMMITTED.
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED
Another (insecure) option would be to enable the server variable innodb_locks_unsafe_for_binlog
Upvotes: 6
Reputation: 5131
Can you try with this simple index :
CREATE INDEX SQLpro001
ON table2 (column5, column3, column4);
?
Try and publish the response time please...
Upvotes: 0
Reputation: 54
I have run into a similar problem was doing a complex query which retrieved over a 100k rows , The Select was relatively fast BUT the insert was extremely slow. I narrowed down the problem to the amount of rows being inserted 49k was the max for me that the insert was going fast but 50k rows took 30x the time to insert , I solved by splitting the query into multiple row queries. I would say this is an innodb problem (Some internal buffer probably going to disk instead of memory) slowing the insert considerably.
Upvotes: 2
Reputation: 119
Sometimes the performance is slower when you execute an INSERT with SELECT because the type of fields in the origin table are diferents from de destiny table, so when the INSERT-SELECT is executing there is an implicit cast on the values from the SELECT so them could be persisted in the destiny table. That implicit cast is not made with the best way of performance and is slower sometimes from a direct insert from values.
Upvotes: 1