如果你的試算表已經胖到載入會喘,多半不是公式太重,是頁籤太多。
每次匯入就多一張暫存、每次月結就多一份報表副本。
與其右鍵一個個刪,不如用 deleteSheet() 規則化清理:用前綴或日期挑目標、白名單保護核心頁、先複製到備份檔,再刪原表,最後把操作寫進 Log。
本篇文章盡量說明內容,也附上自訂選單與排程範例,讓同事不會亂按、半夜也不會把整本清空。
結果是速度回來了、風險降下來,整理變成例行工作,而不是臨時救火。
希望本篇文章的內容能夠幫助到需要的您。
目錄
{tocify} $title={目錄}
為什麼要學 deleteSheet()?
做報表或月結時,工作表越建越多:每月一張報表、每次匯入一張原始資料、每位同事複製一份版本。長期下來,檔案會膨脹、載入變慢,甚至權限管理變亂。deleteSheet() 是 Apps Script 提供的「刪除工作表」方法,用得好可以:
自動清理過期的工作表(例如超過 90 天的暫存)。
交接前統一刪除測試表、殘留樣板。
例行產出新報表前,先清掉上次跑錯留下的重複頁籤。
但刪除也有風險:誤刪、刪到最後一張表、權限不足等。本文會把這些坑一次說透,並給你「先確認、再刪除、可回復」的完整做法。
基礎觀念:deleteSheet() 到底在刪什麼?
在 Apps Script 裡,deleteSheet() 是 Spreadsheet 物件的方法,用法如下:
const ss = SpreadsheetApp.getActiveSpreadsheet();
ss.deleteSheet(sheet); // 參數必須是一個有效的 Sheet 物件
幾個關鍵點:
1. 刪的是「工作表(Sheet)」,不是整個試算表(Spreadsheet)。
2. 你必須先取得要刪的那張 Sheet 物件,例如:ss.getSheetByName('Raw_2024_12')。
3. 若參數是 null/undefined、或該 Sheet 不屬於該 Spreadsheet,會丟錯。
4. 無法刪除「最後一張可見的工作表」——這是官方限制,避免檔案變成空殼。
5. 刪除動作不可撤銷(對當下動作而言),但你可以靠版本紀錄回溯;或先備份到另一份試算表再刪。
手動 vs. 程式:什麼時候用哪一種?
手動刪除(適合零星清理)
1. 開啟試算表 → 對著要刪的工作表分頁(頁籤)按右鍵。
2. 選 「刪除」 → 確認。
3. 完成。
優點:直覺、少量時最快。
缺點:大量頁籤或規則性清理時,容易漏掉、且花時間。
用 Apps Script 刪除(適合規模化與自動化)
批次刪除:例如同名規則、日期規則(刪除超過 90 天的歷史表)。
專案交付前自動清場:把 TMP_ 開頭的表全部清乾淨。
發佈新報表流程:先備份 → 再刪舊版 → 產生新版。
基礎範例 : 刪除單一工作表
function deleteSingleSheet() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheetName = 'TMP_to_delete'; // 你要刪除的表名
const sheet = ss.getSheetByName(sheetName);
// 防呆:確認存在
if (!sheet) {
throw new Error(`找不到工作表:${sheetName}`);
}
// 防呆:避免刪到最後一張
if (ss.getSheets().length <= 1) {
throw new Error('無法刪除:這是檔案中最後一張可見工作表。');
}
ss.deleteSheet(sheet);
}
要點:
1. 先檢查存在性,避免 null 參數。
2. 檢查總數,避免刪到最後一張。
批次刪除:依名稱規則清掉一批
刪除「指定前綴」的暫存表(如 TMP_)
function deleteSheetsByPrefix(prefix = 'TMP_') {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheets = ss.getSheets();
// 防呆:避免全刪
const targets = sheets.filter(s => s.getName().startsWith(prefix));
if (targets.length === 0) return;
// 不要把最後一張刪光
const available = sheets.length - targets.length;
if (available < 1) {
throw new Error('刪除後會變成空檔,已中止。請保留至少一張表。');
}
targets.forEach(s => ss.deleteSheet(s));
}
依「日期規則」刪掉過舊表(表名格式如 Raw_2025-06-15)
function deleteOldSheetsByDate(days = 90, pattern = /^Raw_(\d{4}-\d{2}-\d{2})$/) {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const now = new Date();
const sheets = ss.getSheets();
const toDelete = sheets.filter(s => {
const m = s.getName().match(pattern);
if (!m) return false;
const d = new Date(m[1]); // 由表名解析日期
const diffDays = (now - d) / (1000 * 60 * 60 * 24);
return diffDays > days;
});
if (toDelete.length === 0) return;
if (sheets.length - toDelete.length < 1) {
throw new Error('刪除後將無表可用,請保留至少一張。');
}
toDelete.forEach(s => ss.deleteSheet(s));
}
先備份、再刪除:降低風險的標準流程
做專案時,最佳實務是:「先複製到備份檔 → 再刪除原表」。好處是刪錯也有退路,不必仰賴整份檔案的版本紀錄。
function archiveThenDelete(prefix = 'TMP_', backupFileId = '你的備份試算表ID') {
const src = SpreadsheetApp.getActiveSpreadsheet();
const dst = SpreadsheetApp.openById(backupFileId); // 備份檔
const targets = src.getSheets().filter(s => s.getName().startsWith(prefix));
if (targets.length === 0) return;
// 確保刪完仍有表存在
if (src.getSheets().length - targets.length < 1) {
throw new Error('刪除後檔案會變空,已中止。請保留至少一張表。');
}
// 逐張備份 → 刪除
targets.forEach(sheet => {
const temp = sheet.copyTo(dst); // 複製到備份檔
temp.setName(`[ARCHIVE] ${sheet.getName()} @ ${new Date().toISOString()}`);
src.deleteSheet(sheet); // 再刪原表
});
}
備份策略建議:
1. 建一份專用「Archive」試算表,權限只給管理者。
2. 備份表名加上時間戳,避免重名。
3. 定期清理備份檔(例如保留近 180 天)。
常見錯誤訊息與雷點
1. Exception: The sheet is the last visible sheet
因為試算表不能沒有任何表。
解法:先 insertSheet('KEEP') 新增一張占位表,再刪。
2. Exception: You do not have permission to call deleteSheet / 權限不足
你的帳號沒有編輯權限或該表受保護。
解法:
確認檔案權限、移除保護、改用擁有權限的帳號或部署 Web App 以擁有者身份執行(需謹慎)。
3. Cannot call Spreadsheet.deleteSheet with null.
傳入的 sheet 為 null。
解法:先 getSheetByName() 檢查是否存在,不存在就中止並提示。
4. 誤刪表單回應
刪掉「表單回應 1」會把歷史回應資料一起拿掉。
解法:先 copyTo() 備份到另一份檔案或轉存到資料庫,再刪。
5. 批次刪到關鍵頁籤
用前綴/規則選取時,可能把 Template 或 Config 誤刪。
解法:加入白名單保護、乾跑模式、手動複核名單。
問題集
Q1:刪除後可以復原嗎?
單次動作無法「撤銷」,但你可以到「檔案 → 版本紀錄」回溯到某個時間點;或若有用上文備份招,去備份檔把對應表複製回來。
Q2:為什麼我明明是編輯者,卻刪不了某張表?
該表可能被設「保護工作表」,且沒把你加進允許名單。聯絡擁有者調整保護或請擁有者執行刪除腳本。
Q3:可不可以刪除多個指定名稱?
可以,建立一個名單陣列迭代處理,再套上「最後一張防呆」。
function deleteByNames(names = ['A','B','C']) {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheets = ss.getSheets();
const targets = names
.map(n => ss.getSheetByName(n))
.filter(Boolean);
if (targets.length === 0) return;
if (sheets.length - targets.length < 1) {
throw new Error('刪除後將無表可用。');
}
targets.forEach(s => ss.deleteSheet(s));
}
Q4:怎麼避免別人誤觸批次刪除?
把批次刪除功能放進「管理員選單」,或在 menuDeleteTmp() 前加使用者信箱判斷,只允許特定帳號執行。
function onlyAdminCanRun_() {
const allowed = ['you@company.com'];
const me = Session.getActiveUser().getEmail();
if (!allowed.includes(me)) {
throw new Error('你無權執行此操作,請聯絡管理員。');
}
}
