Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SQL insert slow on 1 million rows

WITH TOP 100000 (100k) this query is finished in about 3 seconds

WITH TOP 1000000 (1mil) this query is finished in about 2 minutes

SELECT TOP 1000000
    db_id = IDENTITY(int, 1, 1), *
INTO dbo.tablename
FROM dbname.dbo.tablename

Actual execution plan is always:

clustered index scan 4% cost
top
top
compute scalar
insert (96% cost)
select into

The table has 1.3 mil rows and has an int primary key on first column

Can I speed it up somehow? I'm using SQL Server 2008 R2.

like image 767
cdbeelala89 Avatar asked Apr 23 '26 16:04

cdbeelala89


2 Answers

The results showed that 100,000 records takes 159 ms, and 1,000,000 records takes 1,435 ms. On a Raid 1 OS, Raid 1 Data, Raid 1 Log, Raid 1 TempDb all separate drives. Our Dev enviroment.

The results showed that 100,000 records takes 113 ms, and 1,000,000 records takes 996 ms. On my laptop with a single SSD (Samsung 840 250GB). SSD's rock!!!

The results showed that 100,000 records takes 188 ms, and 1,000,000 records takes 1,880 ms. On a Raid 1 OS, Raid 10 Data, Raid 10 Log, Raid 1 TempDb all separate drives under a production load.

like image 60
SQLEagle Avatar answered Apr 26 '26 06:04

SQLEagle


Here is a complete script that shows that the 1 million takes less than ten times as long as 100,000. Your situation is likely slightly different, but this shows that the fundamentals are not the issue.

The results show that 100,000 records takes 146 ms, and 1,000,000 records takes 1,315 ms.

These results are from my desktop. If someone else could run the script and post their results, that would be very useful.

Rob

USE master;
GO
-- Drop database SourceDB
IF EXISTS (SELECT * FROM sys.databases WHERE name = 'SourceDB') ALTER DATABASE SourceDB SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
IF EXISTS (SELECT * FROM sys.databases WHERE name = 'SourceDB') DROP DATABASE  SourceDB;
GO
-- Create database SourceDB
CREATE DATABASE SourceDB; 
ALTER DATABASE SourceDB SET RECOVERY SIMPLE;
GO
USE SourceDB;
GO
-- Create table SourceDB.dbo.SourceTable
CREATE TABLE dbo.SourceTable (
    ColID int PRIMARY KEY
);
GO
-- Populate table SourceDB.dbo.SourceTable
DECLARE @i int = 0;
WHILE @i < 1300000
BEGIN
    SET @i += 1;
    INSERT INTO dbo.SourceTable (ColID) VALUES (@i);
END;
GO
-- Drop database Test1
IF EXISTS (SELECT * FROM sys.databases WHERE name = 'Test1') ALTER DATABASE Test1 SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
IF EXISTS (SELECT * FROM sys.databases WHERE name = 'Test1') DROP DATABASE  Test1;
GO
-- Create database Test1
CREATE DATABASE Test1;
ALTER DATABASE Test1 SET RECOVERY SIMPLE;
ALTER DATABASE Test1 MODIFY FILE (NAME = Test1, SIZE = 3000MB, MAXSIZE = 8TB);
ALTER DATABASE Test1 MODIFY FILE (NAME = Test1_log, SIZE = 3000MB, MAXSIZE = 2TB);
GO
USE Test1;
GO
IF EXISTS (SELECT * FROM sys.tables WHERE [OBJECT_ID] = OBJECT_ID('dbo.DestinationTable1')) DROP TABLE dbo.DestinationTable1;
IF EXISTS (SELECT * FROM sys.tables WHERE [OBJECT_ID] = OBJECT_ID('dbo.DestinationTable2')) DROP TABLE dbo.DestinationTable2;
GO
DECLARE @n  int       = 100000;
DECLARE @t1 datetime2 = SYSDATETIME();
SELECT TOP (@n) db_id = IDENTITY(int, 1, 1), *
INTO dbo.DestinationTable1
FROM SourceDB.dbo.SourceTable;
SELECT DATEDIFF(ms, @t1, SYSDATETIME()) AS ElapsedMs;
GO
DECLARE @n  int       = 1000000;
DECLARE @t1 datetime2 = SYSDATETIME();
SELECT TOP (@n) db_id = IDENTITY(int, 1, 1), *
INTO dbo.DestinationTable2
FROM SourceDB.dbo.SourceTable;
SELECT DATEDIFF(ms, @t1, SYSDATETIME()) AS ElapsedMs;
GO
like image 29
Rob Garrison Avatar answered Apr 26 '26 07:04

Rob Garrison



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!