You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

向SQL Server批量插入百万条数据报错的解决方案咨询

解决SQL Server插入100万条记录时"嵌套太深"的问题

这个错误的根源很明确:你把100万条IF NOT EXISTS...INSERT的单条语句堆在了同一个SQL脚本里,SQL Server的查询解析器处理不了这么多连续的嵌套逻辑块(每条IF都是一个嵌套层级)。下面给你几个实用的解决方案,按推荐优先级排序:

1. 改用批量插入+存在性检查(最优方案)

单条循环插入100万条数据不仅会触发嵌套错误,性能也会极差(每条都要单独编译、执行)。更好的方式是先把所有待插入数据导入临时表,再通过一次批量操作完成插入+去重。

步骤:

第一步:创建临时表存储待插入数据

CREATE TABLE #TempAccounts (
    AccountID INT,
    Name NVARCHAR(100),
    Comment NVARCHAR(MAX),
    IsMachine BIT,
    UserID NVARCHAR(50),
    Prefix INT,
    Action NVARCHAR(50),
    Initials NVARCHAR(10),
    TimeStamp DATETIME,
    Reason NVARCHAR(50),
    Iscal NVARCHAR(10)
);

注意:你原来的INSERT语句里重复写了[Name]列,这是笔误,一定要修正,不然会报错。

第二步:批量导入待插入数据

你可以把原脚本里所有VALUES后的内容提取出来,批量插入到临时表:

-- 示例:批量插入多条数据,你可以把100万条VALUES都放到这里
INSERT INTO #TempAccounts 
VALUES 
(117242, 'blabla', 'The users project)', 1, 'val', 39, 'val', 'blabla', CAST(N'2013-01-16 05:53:50.490' AS DateTime), 'NORMAL', '0'),
(117243, 'anotherName', 'Another comment', 0, 'user2', 40, 'action2', 'init2', CAST(N'2013-01-17 06:00:00.000' AS DateTime), 'NORMAL', '0'),
-- ... 剩下的999998条数据

如果数据量太大,用BULK INSERT导入CSV文件会更快:

BULK INSERT #TempAccounts
FROM 'C:\Users\name\Desktop\OutPut\AccountsData.csv'
WITH (
    FIELDTERMINATOR = ',',
    ROWTERMINATOR = '\n',
    FIRSTROW = 2 -- 如果CSV有表头的话
);

第三步:批量插入到目标表并去重

用MERGE语句或者INSERT...SELECT来完成存在性检查+插入:

方法A:使用MERGE(推荐,逻辑更清晰)
MERGE INTO [dbo].[tblAccount] AS Target
USING #TempAccounts AS Source
ON Target.AccountID = Source.AccountID AND Target.TimeStamp = Source.TimeStamp
WHEN NOT MATCHED THEN
    INSERT ([AccountID], [Name], [Comment],[IsMachine], [UserID], [Prefix], [Action], [Initials], [TimeStamp], [Reason], [Iscal])
    VALUES (Source.AccountID, Source.Name, Source.Comment, Source.IsMachine, Source.UserID, Source.Prefix, Source.Action, Source.Initials, Source.TimeStamp, Source.Reason, Source.Iscal);
方法B:使用INSERT...SELECT
INSERT INTO [dbo].[tblAccount] ([AccountID], [Name], [Comment],[IsMachine], [UserID], [Prefix], [Action], [Initials], [TimeStamp], [Reason], [Iscal])
SELECT 
    Source.AccountID, Source.Name, Source.Comment, Source.IsMachine, 
    Source.UserID, Source.Prefix, Source.Action, Source.Initials, 
    Source.TimeStamp, Source.Reason, Source.Iscal
FROM #TempAccounts AS Source
WHERE NOT EXISTS (
    SELECT 1 
    FROM [dbo].[tblAccount] AS Target
    WHERE Target.AccountID = Source.AccountID AND Target.TimeStamp = Source.TimeStamp
);

最后记得清理临时表:

DROP TABLE #TempAccounts;

2. 拆分脚本为多个小文件(备选方案)

如果你坚持要用原来的单条IF NOT EXISTS逻辑,可以把大脚本拆分成多个小文件(比如每个文件1000条语句),然后逐个执行。

用PowerShell拆分脚本:

$sourceFile = "C:\Users\name\Desktop\OutPut\Result tblAccount.sql"
$outputDir = "C:\Users\name\Desktop\OutPut\Batches\"
$batchSize = 1000 # 每个文件包含1000条语句

# 创建输出目录
if (-not (Test-Path $outputDir)) {
    New-Item -ItemType Directory -Path $outputDir | Out-Null
}

$content = Get-Content $sourceFile
$totalBatches = [math]::Ceiling($content.Count / $batchSize)

for ($i=0; $i -lt $totalBatches; $i++) {
    $startIndex = $i * $batchSize
    $endIndex = [math]::Min(($i+1)*$batchSize -1, $content.Count -1)
    $batchContent = $content[$startIndex..$endIndex]
    $batchFile = Join-Path $outputDir "Batch_$i.sql"
    $batchContent | Out-File $batchFile -Encoding utf8
}

修改bat脚本循环执行小文件:

@echo off
setlocal enabledelayedexpansion
set "sqlServer=SQLSERVER"
set "batchDir=C:\Users\name\Desktop\OutPut\Batches\"

for %%f in ("%batchDir%*.sql") do (
    echo !TIME! Starting execution of %%~nf.sql
    sqlcmd -S "!sqlServer!" -i "%%f"
    echo !TIME! Finished %%~nf.sql
)

pause

3. 额外注意事项

  • 你原来的INSERT语句里重复了[Name]列(列列表和VALUES里都有两个),这是语法错误,必须修正,否则即使解决了嵌套问题,插入也会失败。
  • 批量操作前建议关闭目标表的非聚集索引,插入完成后再重建,能大幅提升插入速度。

内容的提问来源于stack exchange,提问作者Mindan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:17:39