出導(dǎo)入.zip:數(shù)據(jù)庫遷移的腳本化解決方案)
簡介這是一份基于 C# 開發(fā)的 SQL Server 數(shù)據(jù)庫腳本導(dǎo)出/導(dǎo)入工具功能與 SQL Server 2014 Management Studio 的“生成腳本”類似適合需要在 .NET 環(huán)境下批量備份表結(jié)構(gòu)、數(shù)據(jù)或遷移數(shù)據(jù)庫的開發(fā)者使用也可作為 C# WinForm 數(shù)據(jù)庫編程的入門實(shí)例。壓縮包共 66 個文件體積僅 2.16MB主要包含 10 個 C# 源碼文件、8 個 SQL 腳本、4 個可直接運(yùn)行的 EXE 及配套 DLL/PDB 調(diào)試文件另有解決方案、項(xiàng)目工程、界面資源配置和編譯中間文件。其中源碼覆蓋主程序入口、WinForm 界面和數(shù)據(jù)庫輔助操作類SQL 腳本可作為導(dǎo)出結(jié)果的參考或?qū)肽0濉R延?1488 人學(xué)習(xí)/下載。借助該工具用戶可以像 SSMS 一樣選擇庫表并生成 CREATE/INSERT 腳本也可將腳本反向?qū)肽繕?biāo)數(shù)據(jù)庫實(shí)現(xiàn)不同服務(wù)器間的庫結(jié)構(gòu)遷移工程目錄按源碼、編譯輸出與資源分類組織適合研究數(shù)據(jù)庫腳本生成原理或作為內(nèi)部數(shù)據(jù)遷移工具二次開發(fā)的基礎(chǔ)。1. SQLSERVER腳本導(dǎo)出導(dǎo)入.zip數(shù)據(jù)搬遷問題的最短路徑接手一個老庫到新服務(wù)器的數(shù)據(jù)遷移數(shù)據(jù)庫超過 1TBSSMS 的“生成腳本”功能在這個量級下基本是擺設(shè)導(dǎo)出的腳本動輒幾個 GB 文本執(zhí)行起來連日志文件都撐不住。這時候我把目光轉(zhuǎn)向腳本方案。SQLSERVER腳本導(dǎo)出導(dǎo)入.zip 就是這類工具包的常見形態(tài)把一批 bcp 命令、T-SQL 存儲過程、批處理封裝在一起按“導(dǎo)出—傳輸—導(dǎo)入—校驗(yàn)”四步完成數(shù)據(jù)搬遷。它適合三類人被大表遷移折磨的 DBA、需要在無人值守環(huán)境定時備份項(xiàng)目數(shù)據(jù)的運(yùn)維以及想擺脫圖形界面、把數(shù)據(jù)流轉(zhuǎn)做到可復(fù)現(xiàn)和可版本化的開發(fā)者。這篇筆記不聊圖形界面只講腳本方案怎么落地。2. 腳本包背后的三條路徑bcp、BULK INSERT 與動態(tài) INSERT 的選型邏輯腳本包的核心不是某一個命令而是三套互補(bǔ)的導(dǎo)出導(dǎo)入路徑。拿到任何腳本包先看它用的是哪條路徑再判斷適合什么數(shù)據(jù)量。2.1 bcp千萬級大表導(dǎo)出的默認(rèn)答案bcp 是 SQL Server 自帶的命令行工具不依賴 SSMS在 Windows 的 cmd、PowerShell 或者 Linux 的 mssql-tools 里都能調(diào)用。它的典型用法是bcp 查詢語句 queryout 數(shù)據(jù)文件 -S 服務(wù)器 -U 賬號 -P 密碼導(dǎo)出的是二進(jìn)制或文本格式的平面文件。選擇 bcp 的第一個理由是性能。底層走 OLEDB 或 ODBC導(dǎo)出時是流式寫文件不會把整張表拉進(jìn)內(nèi)存配合-b參數(shù)按批提交日志增長可控。第二個理由是增量能力queryout 可以接任意 WHERE 子句把某個時間段、某種狀態(tài)的記錄先導(dǎo)出來腳本包里的“增量備份”功能基本都是靠這個實(shí)現(xiàn)的。bcp SELECT [ID], [Name], [Amount], [CreateTime] FROM [MyDB].[dbo].[Orders] WHERE [CreateTime] 2024-01-01 queryout D:\db_backup\orders_2024.dat -S 192.168.1.10,1433 -U backup_user -P YourPassword -c -t | -r \n -b 5000 -m 100 -e D:\db_backup\orders_2024.err這段命令做了幾件事-c表示用字符模式讀寫-t |指定字段分隔符為豎線-r \n指定行分隔符為換行-b 5000表示每 5000 行提交一次-m 100允許最多 100 條錯誤-e指定錯誤日志文件。這里最容易忽略的是-m如果不寫bcp 遇到第一條錯誤就中斷如果寫太大錯誤記錄會淹沒在文件里腳本包一般建議設(shè) 50 到 100。2.2 BULK INSERT把數(shù)據(jù)文件快速裝進(jìn)目標(biāo)庫bcp 負(fù)責(zé)把數(shù)據(jù)導(dǎo)出去BULK INSERT 負(fù)責(zé)把文件裝回來這是腳本包里最常見的配對。BULK INSERT 是一條 T-SQL 語句可以直接在 SSMS 或腳本里執(zhí)行但它有一個硬約束文件路徑必須是 SQL Server 服務(wù)進(jìn)程能訪問到的路徑不是你客戶端電腦的本地路徑。BULK INSERT [MyDB].[dbo].[Orders] FROM ND:\db_backup\orders_2024.dat WITH ( FIELDTERMINATOR |, ROWTERMINATOR \n, FIRSTROW 1, BATCHSIZE 5000, TABLOCK, CODEPAGE 65001 );FIELDTERMINATOR和ROWTERMINATOR必須和 bcp 導(dǎo)出的參數(shù)一致否則導(dǎo)入時列對不上。FIRSTROW 1表示從文件第一行開始讀如果文件有標(biāo)題行要改成2。TABLOCK是性能關(guān)鍵它允許目標(biāo)表在導(dǎo)入期間使用最小日志記錄速度能提升一倍以上代價是導(dǎo)入期間這張表基本處于排他鎖狀態(tài)不適合在線業(yè)務(wù)。CODEPAGE 65001是把文件當(dāng)作 UTF-8 讀后面避坑章節(jié)會專門講編碼問題。2.3 動態(tài) INSERT小配置表的精細(xì)化處理bcp 和 BULK INSERT 適合大表但對于幾百行、幾千行的配置表、字典表用這兩個工具反而小題大做。腳本包里通常會另放一個生成 INSERT 語句的存儲過程把每行數(shù)據(jù)轉(zhuǎn)成可讀的文本 SQL。這樣做的價值在于可審查遷移前能直接看 SQL 內(nèi)容確認(rèn)數(shù)據(jù)沒被轉(zhuǎn)義、截斷。DECLARE sql NVARCHAR(MAX) N; SELECT sql sql CASE WHEN sql N THEN N ELSE N UNION ALL SELECT END QUOTENAME(COLUMN_NAME) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME NConfigItem AND TABLE_SCHEMA Ndbo ORDER BY ORDINAL_POSITION; SET sql NSELECT * INTO #tmp FROM ( sql N) AS src;; PRINT sql;這段代碼演示的是動態(tài)拼列名真正落地時還會把每一行用 FOR XML PATH 拼成INSERT INTO ... VALUES (...);。這里不想展開太多因?yàn)閯討B(tài) INSERT 的坑非常明顯SQL 文本長度有上限數(shù)據(jù)里帶單引號、換行符都會破壞語句結(jié)構(gòu)所以它只適合小表。腳本包里常見做法是把配置表單獨(dú)放進(jìn)一個sql\init_data.sql由維護(hù)者手工維護(hù)不參與大表流程。2.4 選型組合一次遷移任務(wù)里三種方案怎么配合一套成熟的腳本包不會只用一種方案。我一般會這樣分?jǐn)?shù)據(jù)量在百萬級以下的維度表用動態(tài) INSERT 生成可讀腳本千萬級以上的事實(shí)表、流水表用 bcp 導(dǎo)出加 BULK INSERT 導(dǎo)入特別大的表超過 200GB則拆分成按月、按天導(dǎo)出多個文件再并行導(dǎo)。三者配合還有一個好處動態(tài) INSERT 出錯時可以人工看 SQLbcp 出錯時可以看錯誤文件排查路徑清晰。方案適合規(guī)模優(yōu)點(diǎn)缺點(diǎn)bcp queryout百萬級以上流式導(dǎo)出、支持 WHERE 增量、不占內(nèi)存需要命令行環(huán)境、參數(shù)多BULK INSERT百萬級以上T-SQL 內(nèi)執(zhí)行、可配批大小和事務(wù)文件必須放在服務(wù)端可訪問路徑動態(tài) INSERT萬級以下可讀可審查、適合配置表SQL 長度有限、大表性能差3. 從解壓到跑通一套可復(fù)現(xiàn)的導(dǎo)出導(dǎo)入腳本包這一章把完整流程走一遍。拿到 SQLSERVER腳本導(dǎo)出導(dǎo)入.zip 后按目錄結(jié)構(gòu)拆開改配置跑導(dǎo)出再跑導(dǎo)入最后校驗(yàn)。3.1 目錄結(jié)構(gòu)與統(tǒng)一配置先定規(guī)矩再動手腳本包解壓后通常長這樣這也是我組織腳本的習(xí)慣。數(shù)據(jù)文件、日志、SQL 腳本、批處理各歸各的目錄避免一個文件夾里堆滿幾百個文件。SQLSERVER腳本導(dǎo)出導(dǎo)入/ export_all.bat import_all.bat config.ini check_count.sql sql/ import_tables.sql truncate_tables.sql rebuild_index.sql logs/ data/config.ini 是最先要改的東西。把服務(wù)器地址、數(shù)據(jù)庫名、賬號、密碼、目標(biāo)路徑、分隔符全部抽到配置里腳本本身不做任何硬編碼。[SERVER] SRC_SERVER192.168.1.10,1433 DST_SERVER192.168.1.20,1433 DB_NAMEMyDB [ACCOUNT] DB_USERbackup_user DB_PASSYourPassword [CONFIG] EXPORT_PATHD:\db_backup FIELD_SEPARATOR| ROW_SEPARATOR\n BATCH_SIZE5000注意密碼寫在明文 ini 里在內(nèi)部工具包中沒問題但如果這份腳本要交付給外部環(huán)境建議改成環(huán)境變量讀取。bat 文件里用%DB_USER%、%DB_PASS%引用配置值的方式不同腳本包各有差異但思路一致默認(rèn)值放進(jìn) ini命令行參數(shù)優(yōu)先級最高。3.2 批處理導(dǎo)出腳本循環(huán)調(diào)用 bcp 并收集錯誤碼導(dǎo)出的批處理核心是一個 for 循環(huán)遍歷表清單逐表調(diào)用 bcp。表清單可以是一個文本文件也可以直接寫在數(shù)組變量里。echo off setlocal enabledelayedexpansion set EXPORT_PATHD:\db_backup set SERVER192.168.1.10,1433 set USERbackup_user set PASSYourPassword set DBMyDB if not exist %EXPORT_PATH% mkdir %EXPORT_PATH% for %%T in (Orders, Customers, Products, ConfigItem) do ( echo [%date% %time%] exporting %%T ... bcp SELECT * FROM [%DB%].[dbo].[%%T] queryout %EXPORT_PATH%\%%T.dat -S %SERVER% -U %USER% -P %PASS% -c -t | -r \n -b 5000 -m 100 -e %EXPORT_PATH%\%%T.err if !errorlevel! neq 0 ( echo [%date% %time%] ERROR - %%T export failed with code !errorlevel! %EXPORT_PATH%\export.log ) else ( echo [%date% %time%] OK - %%T %EXPORT_PATH%\export.log ) ) endlocal這段腳本有幾個關(guān)鍵點(diǎn)。setlocal enabledelayedexpansion必須打開因?yàn)閑rrorlevel在循環(huán)里要用!errorlevel!而不是%errorlevel%讀取否則每次取到的都是循環(huán)開始前的舊值。if not exist負(fù)責(zé)建目錄忘了建目錄會導(dǎo)致 bcp 報“無法打開文件”。每個表錯誤時只記錄日志而不中斷整個循環(huán)這樣一張表失敗不會拖垮整批任務(wù)。3.3 配合導(dǎo)入建表、清表、BULK INSERT 的順序不能亂導(dǎo)入前先確認(rèn)目標(biāo)庫表結(jié)構(gòu)已經(jīng)存在。腳本包通常會在導(dǎo)出階段順手生成一份建表腳本或者用 bcp format 命令生成格式文件作為建表參考。導(dǎo)入的順序是先 TRUNCATE 舊數(shù)據(jù)再執(zhí)行 BULK INSERT最后重建索引。echo off setlocal set SERVER192.168.1.20,1433 set USERbackup_user set PASSYourPassword set DBMyDB set DATA_PATHD:\db_backup sqlcmd -S %SERVER% -U %USER% -P %PASS% -d %DB% -i sql\truncate_tables.sql if errorlevel 1 goto :end sqlcmd -S %SERVER% -U %USER% -P %PASS% -d %DB% -i sql\import_tables.sql if errorlevel 1 goto :end sqlcmd -S %SERVER% -U %USER% -P %PASS% -d %DB% -i sql\rebuild_index.sql if errorlevel 1 goto :end echo [%date% %time%] import success goto :end :end endlocaltruncate_tables.sql里寫的是對目標(biāo)表執(zhí)行TRUNCATE TABLE目的是讓導(dǎo)入可重復(fù)執(zhí)行。這里有一個順序問題必須先清表再導(dǎo)入否則 BULK INSERT 會往已有數(shù)據(jù)后面追加第二次運(yùn)行時數(shù)據(jù)翻倍。import_tables.sql的內(nèi)容就是上一章展示的 BULK INSERT 語句每張表一段建議把TABLOCK打開等全部導(dǎo)入完成后再重建索引比導(dǎo)入前保留索引快很多。3.4 校驗(yàn)環(huán)節(jié)行數(shù)、聚合值與樣本比對數(shù)據(jù)導(dǎo)完不等于遷完必須驗(yàn)證。最基礎(chǔ)的是行數(shù)比對源庫和目標(biāo)庫各跑一次 COUNT_BIG人工或腳本比對結(jié)果。更穩(wěn)的校驗(yàn)是同時比對主鍵的聚合校驗(yàn)和與行數(shù)這樣能發(fā)現(xiàn)同構(gòu)但數(shù)據(jù)錯位的極端情況。-- 源庫執(zhí)行 SELECT COUNT_BIG(1) AS RowCnt, SUM(CAST(CHECKSUM([ID], [Name], [Amount], [CreateTime]) AS BIGINT)) AS AggKey FROM [MyDB].[dbo].[Orders]; -- 目標(biāo)庫執(zhí)行結(jié)果應(yīng)與源庫完全一致 SELECT COUNT_BIG(1) AS RowCnt, SUM(CAST(CHECKSUM([ID], [Name], [Amount], [CreateTime]) AS BIGINT)) AS AggKey FROM [MyDB].[dbo].[Orders];CHECKSUM聚合校驗(yàn)的核心是把每一行變成一個整數(shù)再對全表求和。只要有一行數(shù)據(jù)的某個字段不一致總和幾乎必然變化。要注意CHECKSUM的結(jié)果范圍是 int 有符號整數(shù)直接求和可能溢出所以要包一層CAST(... AS BIGINT)。如果兩張表的這兩條結(jié)果完全一致基本可以斷定數(shù)據(jù)遷移成功。4. 三個必調(diào)參數(shù)組規(guī)模、編碼、權(quán)限腳本能不能跑通一半看參數(shù)調(diào)得對不對。這一章講腳本包里最值得花時間的三個參數(shù)組。4.1 批大小與錯誤上限把大事務(wù)拆小把失敗控制在文件里bcp 的-b和 BULK INSERT 的BATCHSIZE是同一個概念每 N 行提交一個事務(wù)。批越小單次事務(wù)占用的日志越少失敗后回滾的成本越低批越大導(dǎo)入吞吐越高但一旦中間失敗回滾范圍也越大。我的實(shí)踐經(jīng)驗(yàn)是1GB 以下的表用 1000 到 500010GB 以上的表用 10000 到 20000再往上收益就不明顯了。-m錯誤上限要跟批大小聯(lián)動-m 100配合-b 5000意味著最多只能容忍 100 行壞數(shù)據(jù)超過立刻終止防止錯誤記錄把文件撐爆。如果你導(dǎo)出的數(shù)據(jù)來自業(yè)務(wù)庫建議把-m設(shè)為 0也就是一個錯誤都不允許因?yàn)闃I(yè)務(wù)庫的數(shù)據(jù)理論上不該有壞行出現(xiàn)任何一條都說明上游有問題。e錯誤文件會記錄行號、列號和錯誤原因格式如下這是排查時最直接的入口。第 100 行列 3無法將值 abc-123 轉(zhuǎn)換為 bigint 第 105 行列 7字符串或二進(jìn)制數(shù)據(jù)將被截斷4.2 字符模式、Unicode 與格式文件亂碼和錯列的根源bcp 的-c是字符模式數(shù)據(jù)按數(shù)據(jù)庫排序規(guī)則轉(zhuǎn)成字符串輸出-w是 Unicode 模式輸出 UTF-16 文件-C 65001指定使用 UTF-8 代碼頁。三者選錯輕則中文變問號重則直接導(dǎo)入失敗。導(dǎo)出參數(shù)文件編碼適用場景-c隨系統(tǒng)代碼頁純 ASCII 或數(shù)字內(nèi)容-wUTF-16含 nvarchar / nchar 字段-c -C 65001UTF-8跨平臺交換、現(xiàn)代應(yīng)用如果表里含nvarchar字段我一般直接用-w不要貪圖-c的緊湊。字符模式下非 Unicode 類型轉(zhuǎn)成 varchar中文內(nèi)容取決于目標(biāo)庫排序規(guī)則一旦目標(biāo)庫的代碼頁和源庫不一致必亂。-w雖然文件體積大一倍但能完全避開編碼問題。格式文件是另一個層面當(dāng)字段類型需要精確指定、列順序需要調(diào)整時用bcp ... format nul -f schema.fmt -c先生成格式文件再做修改。4.3 權(quán)限模型與連接方式讓每個賬號只做一件事腳本包在正式環(huán)境跑權(quán)限不足是最常見的“第一夜翻車點(diǎn)”。bcp 導(dǎo)出需要源庫的 SELECT 權(quán)限BULK INSERT 需要目標(biāo)庫的 INSERT 權(quán)限同時要求服務(wù)器級別的ADMINISTER BULK OPERATIONS權(quán)限sqlcmd 執(zhí)行腳本需要對應(yīng)庫的 EXECUTE 權(quán)限。我建議拆分兩個賬號導(dǎo)出賬號只給db_datareader導(dǎo)入賬號單獨(dú)建只給目標(biāo)庫的db_datawriter加ADMINISTER BULK OPERATIONS。不要復(fù)用 sa 賬號否則腳本一旦出錯影響面會擴(kuò)大到整個實(shí)例。連接方式上-S參數(shù)里顯式寫端口號-U -P走 SQL Server 身份驗(yàn)證如果走 Windows 身份驗(yàn)證批處理里要用-E但任務(wù)計劃程序運(yùn)行時注意賬號上下文否則會報“用戶 NT AUTHORITY\ANONYMOUS 登錄失敗”。5. 腳本導(dǎo)出導(dǎo)入避坑指南五個現(xiàn)場翻車記錄腳本方案最大的風(fēng)險不在命令本身而在環(huán)境差異。這一章記錄五個我實(shí)際踩過的坑現(xiàn)象、原因、解決辦法按順序列出。5.1 文件找不到路徑職責(zé)與客戶端/服務(wù)端位置的混淆現(xiàn)象是 bcp 導(dǎo)出正常但 BULK INSERT 報“無法大容量加載文件 D:\db_backup\orders.dat 不存在”。原因在于路徑職責(zé)不同bcp 是客戶端工具讀取客戶端文件系統(tǒng)的路徑BULK INSERT 是服務(wù)端 T-SQL 語句讀取 SQL Server 服務(wù)進(jìn)程所在機(jī)器的路徑。如果你在本地執(zhí)行 BULK INSERT而 SQL Server 跑在另一臺服務(wù)器上D 盤路徑自然不存在。解決方法是把數(shù)據(jù)文件上傳到 SQL Server 所在機(jī)器或者用 UNC 共享路徑\\server\share\orders.dat并保證 SQL Server 服務(wù)賬號有共享目錄的讀取權(quán)限。反過來bcp in 模式則是從客戶端讀取文件推送給服務(wù)端邏輯正好相反。5.2 導(dǎo)入錯列數(shù)據(jù)里的分隔符、引號與行終止符現(xiàn)象是導(dǎo)入后某些行多了一列或者某個字段變成 NULL。最常見的原因是數(shù)據(jù)內(nèi)容里本身包含豎線|導(dǎo)出的字段分隔符和內(nèi)容撞車。比如備注字段“說明A|B|C”被拆成了三個字段后面的列全部錯位。解決方法是換用數(shù)據(jù)中極少出現(xiàn)的控制字符比如\x01十六進(jìn)制 0x01。bcp 的-t |改成-t \x01BULK INSERT 的FIELDTERMINATOR 0x01?;蛘吒纱嘤酶袷轿募槊恳涣酗@式定義類型和終止符格式文件可以完全避開“讀出來再猜列”的問題。5.3 中文變問號代碼頁和排序規(guī)則的拉鋸戰(zhàn)現(xiàn)象是導(dǎo)出文件用記事本打開正常導(dǎo)入后數(shù)據(jù)庫里的中文全部變成問號。原因通常是字符模式-c下源庫的排序規(guī)則是 Chinese_PRC_CI_AS導(dǎo)出時按系統(tǒng) ANSI 代碼頁轉(zhuǎn)出目標(biāo)庫導(dǎo)入時又按自己的代碼頁解析中間至少做了一層有損轉(zhuǎn)換。解決方法是含中文或任何非 ASCII 字符的表導(dǎo)出時改用-wBULK INSERT 里對應(yīng)改成ROWTERMINATOR不變但不要寫CODEPAGE。如果你堅持用 UTF-8導(dǎo)出加-C 65001導(dǎo)入側(cè)寫CODEPAGE 65001兩邊缺一不可。5.4 身份列沖突IDENTITY_INSERT 與 seed 重置現(xiàn)象是導(dǎo)入后目標(biāo)表的自增列值跟源表完全對不上或者導(dǎo)入時報“不能為表插入顯式值”。原因很直接源表有 IDENTITY 列bcp 導(dǎo)出時把自增值也導(dǎo)出來了但目標(biāo)表默認(rèn)不允許對 IDENTITY 列做顯式插入。解決方法是導(dǎo)入前先SET IDENTITY_INSERT [表名] ON導(dǎo)入完成后執(zhí)行DBCC CHECKIDENT (表名, RESEED)重新校準(zhǔn)自增種子。腳本包的import_tables.sql里每張含 IDENTITY 列的表BULK INSERT 前后必須包這兩句否則二次導(dǎo)入時自增列會從錯誤起點(diǎn)繼續(xù)跑。5.5 超時和中斷網(wǎng)絡(luò)包大小、防火墻與重跑策略現(xiàn)象是導(dǎo)出 1 小時左右任務(wù)卡住bcp 進(jìn)程還在但文件大小長時間不變沒有任何報錯。原因大多是網(wǎng)絡(luò)傳輸問題或防火墻探測長連接后靜默斷連。bcp 本身不提供精確的查詢超時參數(shù)但可以通過調(diào)節(jié)網(wǎng)絡(luò)包大小降低斷連概率同時讓腳本具備斷點(diǎn)重跑能力。bcp SELECT * FROM [MyDB].[dbo].[Orders] WHERE [CreateTime] 2024-01-01 queryout D:\db_backup\orders_2024.dat -S 192.168.1.10,1433 -U backup_user -P YourPassword -c -t | -r \n -b 5000 -a 4096-a 4096是網(wǎng)絡(luò)包大小默認(rèn)值偏保守調(diào)大后長傳場景更穩(wěn)。同時給腳本加上“目標(biāo)文件已存在則跳過”或“按時間分片導(dǎo)出”的邏輯中斷后不用從頭跑。比如這個例子里的 WHERE 條件已經(jīng)按日期過濾重跑時把文件改成orders_2024_0901.dat只補(bǔ)跑缺失的區(qū)間即可。6. 把腳本包升級成無人值守的定時任務(wù)腳本跑通只是第一步真正讓這套方案有價值的是接進(jìn)任務(wù)計劃讓它每天安靜地完成導(dǎo)出導(dǎo)入。這里分享兩個我常用的技巧。6.1 日期變量與日志文件批處理里直接用%DATE%取日期很玄學(xué)不同系統(tǒng)區(qū)域設(shè)置會給出不同格式%DATE:~0,4%這種切割方式換個環(huán)境就翻車。我在生產(chǎn)環(huán)境的腳本包里統(tǒng)一改用 PowerShell 生成日期字符串再傳給批處理。$date Get-Date -Format yyyyMMdd $logDir D:\db_backup\logs if (-not (Test-Path $logDir)) { New-Item -ItemType Directory -Path $logDir | Out-Null } cmd /c export_all.bat | Out-File $logDir\export_$date.log -Encoding utf8 $exitCode $LASTEXITCODE exit $exitCode這段腳本把每天的導(dǎo)出日志按日期歸檔同時把批處理的退出碼原樣傳給任務(wù)計劃方便計劃程序把失敗任務(wù)標(biāo)注出來。日志文件只要保留最近 30 天的老文件用一個簡單循環(huán)清理就行。6.2 任務(wù)計劃程序的調(diào)用與退出碼任務(wù)計劃程序里執(zhí)行 PowerShell 腳本時注意“起始于”目錄要設(shè)成腳本包所在目錄否則相對路徑全部失效。批處理里所有路徑都寫成%~dp0開頭是更穩(wěn)的做法這是指腳本自身所在目錄。set BASE_PATH%~dp0 set EXPORT_PATH%BASE_PATH%data set LOG_PATH%BASE_PATH%logs我吃過一次虧腳本在命令行手動跑正常放到任務(wù)計劃里就報“系統(tǒng)找不到指定的路徑”后來發(fā)現(xiàn)是任務(wù)計劃的“起始于”留空了。從那以后我所有腳本包統(tǒng)一改成%~dp0拼絕對路徑再不依賴工作目錄。最后收個尾腳本導(dǎo)出導(dǎo)入這套玩法技術(shù)上并不復(fù)雜真正的價值在于把遷移過程變成可重復(fù)、可檢查、可自動化的流水線。如果你最近也在為數(shù)據(jù)庫搬遷頭疼先拿一張小表跑通全流程再逐步放大希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取