🍀用命令列批次執行 SQL 腳本失敗怎麼辦?快速看懂「物件已存在」錯誤(以 SQL Server 為例)

 

前言:為什麼我用「批次執行」SQL 會突然失敗?

很多團隊會把資料庫的更新流程自動化:
例如每天匯入資料、建立報表用的暫存表、或部署新版本時跑一包 SQL 腳本。這類作法常會用「命令列工具」去執行 .sql 檔,因為它可以被排程器呼叫、可以輸出 log、也更容易追蹤成功或失敗。

但新手最常遇到的狀況之一就是:

明明昨天還跑得好好的,今天一跑就報錯,整包流程停住。


其中一個超典型錯誤就是:

「資料庫中已經有一個名為 'XXX' 的物件。」


你看到的錯誤訊息,其實在說什麼?

當 SQL Server 回傳類似這樣的訊息:

  • 已切換到某個資料庫(代表連線成功、也確實在你指定的 DB 內執行)

  • 接著出現「層級 16」的錯誤(代表這是會造成語句失敗的錯誤)

  • 最關鍵句:「已經有一個名為 'FT_DATALOG_10963' 的物件」

白話翻譯就是:

你的腳本裡正在「建立」一個叫 FT_DATALOG_10963 的東西(可能是資料表/檢視/程序),

但資料庫裡早就有同名物件了,所以 SQL Server 不允許你再建立一次。



這個錯誤最常發生在哪些情境?

你可以把它想成「你想新增一個檔案,但資料夾裡已經有同名檔案」。

以下情境很常見:

  1. 你昨天已經跑過一次腳本
    物件已存在,今天再跑一次就撞名。

  2. 腳本設計成只能跑一次(One-time script)
    例如部署用 SQL,作者假設只會在全新環境執行。

  3. 你的自動化流程設定成失敗就停止
    很多批次工具會在遇到錯誤時直接終止,導致後面 SQL 都不會跑。


為什麼這個錯誤會讓整包流程停止?

在自動化場景中,常見做法是「只要有錯就當作整包失敗」,因為:

  • 資料庫更新通常是連續步驟

  • 中間任何一步失敗,後面可能都會依賴前面的結果

  • 所以寧可停下來,也不要讓資料進入「半成功半失敗」的危險狀態

這就是為什麼你會覺得:

只是撞到一個名字而已,為什麼整包都不跑了?


因為對部署與資料品質來說,「不中斷」反而更危險。


解法總整理:遇到「物件已存在」你有 3 條路

解法 1:重跑前先刪掉舊的(適合每天重建)

如果你的流程是「每天都重新生成」,那最佳策略是:

  • 先確認舊的存在

  • 存在就刪掉

  • 再重新建立

這樣同一包腳本就能重複執行,不會撞名。

示意:


IF EXISTS (SELECT 1 FROM sys.tables WHERE name = N'REPORT_STAGE_20260302')
BEGIN
DROP TABLE dbo.REPORT_STAGE_20260302;
END;

CREATE TABLE dbo.REPORT_STAGE_20260302
(
Id INT NOT NULL,
CreatedAt DATETIME2 NOT NULL
);

解法 2:存在就跳過(適合部署一次、不要動既有資料)

如果你是「只要有就不要改」,例如:

  • 正式環境已經建好了

  • 你只想確保它存在,不想刪

那你要改成:不存在才建立

示意:


IF NOT EXISTS (SELECT 1 FROM sys.objects WHERE object_id = OBJECT_ID(N'dbo.CONFIG_CACHE') AND type = 'U')
BEGIN
CREATE TABLE dbo.CONFIG_CACHE
(
CacheKey NVARCHAR(100) NOT NULL,
CacheValue NVARCHAR(4000) NULL
);
END;

解法 3:不要再建立結構,只做資料寫入(適合 ETL / 匯入資料)

有些腳本的目的是「匯入資料」,其實表結構早就該固定存在。
如果你每次匯入都 CREATE TABLE,那重跑必撞。

這時候要把腳本分成兩類:

  • 結構腳本(Schema):只在初始化或版本更新跑

  • 資料腳本(Data Load):每天跑、只做 INSERT/更新/清空再灌


工程師建議:如何快速判斷「它到底是什麼物件」?

很多人卡住是因為不知道它是:

  • 資料表?

  • 檢視(View)?

  • 儲存程序(Stored Procedure)?

工程上最有效的方法是:
先查物件類型,再決定要 DROP 哪一種。

你可以用「資料庫系統目錄」查出類型與建立時間,避免刪錯。

SELECT
o.name,
o.type_desc,
o.create_date
FROM sys.objects o
WHERE o.name = N'REPORT_STAGE_20260302';

常見 QA(新手最常問)

Q1:我明明是匯入資料,為什麼會跑到建立物件?

因為 .sql 檔裡可能同時包含「建表 + 匯入」。
腳本作者常把流程寫在同一包,導致你重跑匯入時也會重跑建表。

Q2:直接把舊的刪掉會不會很危險?

要看你的用途:

  • 暫存/中繼表:通常可以刪

  • 正式主表:通常不建議直接刪

  • View/Proc:若只是重新部署版本,刪了重建通常可行,但要有版控與審核

Q3:為什麼錯誤顯示「行 1」?

批次工具常把 SQL 檔用「分段」方式執行(例如遇到分隔符就切 batch)。
所以「行 1」有時是指 那一段 batch 的第 1 行,不一定是整份檔案的第 1 行。


結語:把資料庫腳本寫成「可重複執行」才是長期解

只要你在做自動化、排程、或 CI/CD 部署,最重要的原則是:

✅ 同一包腳本「重跑不會炸」
✅ 失敗時 log 能快速定位問題
✅ 結構與資料分離,避免互相拖累

遇到「物件已存在」其實不是壞事,它是在提醒你:
你的流程正在進入「需要工程化管理」的階段了。

留言

這個網誌中的熱門文章

🔍Vue.js 專案錯誤排查:解決 numericFields is not defined 與合併儲存格邏輯最佳化

🖥️遠端桌面連線完整新手指南:Windows RDP、Chrome Remote Desktop、AnyDesk、TeamViewer 一次搞懂

🔎EF Core 連 Oracle 出現 ORA-00600 [kpp_concatq:2] 的完整排錯指南(含 EF Core ToString/CultureInfo 錯誤)