Since nobody has answered it up to now like this, I dare to fill the gap.
"batch insert", not "bulk insert"
First of all, "bulk insert" might better be replaced by "batch insert". In TSQL, "bulk insert" is the command to import from a file, see BULK INSERT in MYSQL.
mysqldump cannot be an example for how insert works inside a db
I dare to say that the question has nothing to do with for example a db that was dumped and gets loaded since reading from a file is always without the risks of locks and can therefore be done in another way than a query inside the db, there can never be any case of duplicates, everything can be done without locks in many processes at a time.
To make this clearer: if you have db locks so that one operation does not allow another before its ending although you would normally not need to wait for it to end since you already know that there are no duplicates or FK checks, you can get around the locks by dumping a query at first in a file and afterwards by reloading from that file again. Then you have the same power that a mysqldump and load has. Might be good as a trick for other things as well, not just for the question MySQL Create Table as SELECT, but for the question here, it is clear that you need a trick, and if you need a trick for it outside of the db, the db itself does not have this trick at hand. I did not read any other trick to get this done from inside a db, for example to save the data to another temp table (instead of the file trick) and then make a full table select from that temp table to get around the locks. Never seen or heard.
This long text shall say that the accepted answer might not hit the point.
Faster
This shortly answers the question. As far as I can remember, a "manual batch insert" was always slower than a "full table select insert". I remember this from a large migration of an old db into a newly structured db that was done without mysqldump.
Batch is not always batch
And it is clear that speed will differ between selecting a full table and selecting batches of it, even if the full table gets selected in batches as well in the end.
Python example as a hint
This happens in other programming languages as well. Taken from When writing csv from CF to bucket: 'with open(filepath, "w") as MY_CSV:' leads to "FileNotFoundError: [Errno 2] No such file or directory:", in Python:
instead of the pd.to_csv()
with a chunksize parameter of 5000 which needed 62s for 700k rows to be loaded and stored into a csv, the csv module's writer that used batches as well took more than the 9 minutes (stopped then, it might have lasted much longer).
Logs overhead and fragmentation risks
The question is about speed and not about logs or fragmentation risks, and I even doubt that logs and fragmentation risks are worse off with a "full table insert" than with other ways.
Indirect hints from row-wise steps
There is another point: INSERT INTO TABLE2 SELECT * FROM TABLE1
cannot be run together with ON DUPLICATE DELETE
since that would slow it down to a row-wise workflow - it just cannot do it.
To get this done, you have to use VALUES()
instead:
INSERT INTO {tbl_name}({attributes})
VALUES {insert_placeholders of the batch}
ON DUPLICATE KEY UPDATE {update_placeholders}
This shows that the query that the OP calls "bulk insert" (and is a "batch insert" I guess) can do row-wise workflows. Only to be able to do it shows that the workflow is coded in another way. If you instead delete all of the rows that need to be replaced or to be deleted at first and only afterwards insert anything new with a "full table insert", you get around any row-wise workflow. It is then like
replace into some_table
select somecolumn from othertable
Taken from MySQL insert on duplicate key; delete?, just without any row-wise workflow.
Wrap up
I am guessing here a lot, do not trust this answer. I just dare to say that INSERT INTO TABLE2 SELECT * FROM TABLE1
has the minimum overhead, and is always the fastest choice, and is often even by far the fastest, and this regardless of the memory, since it uses the best batch sizes without any need to tell it. And I just guess that it blocks other queries around that table until its task is done, which might sometimes lead to a locked table alert but will be the shortest way to go.
WHERE
clause and see how it pans out - I thought that the MVCC architecture meant that it doesn't matter if it's 1 record being updated or 1 billion! But see @RolandoMYSQLDBA's answer!