前言
Access (Jet/ACE 引擎) 與 SQL Server (T-SQL) 在 SQL 方言上存在顯著差異。遷移專案、跨資料庫報表、雙資料庫支援產品常需處理這些差異。本文整理 8 大類常見差異,提供對照表、最佳實踐寫法與遷移檢查清單。
一、NULL 判斷
| Access | SQL Server | 建議統一寫法 |
NOT ISNULL(col) | IS NOT NULL(col) / col IS NOT NULL | col IS NOT NULL (ANSI 標準,兩者皆支援) |
ISNULL(col) | IS NULL(col) / col IS NULL | col IS NULL |
二、字串大小寫轉換
| 功能 | Access | SQL Server | 備註 |
| 轉小寫 | LCASE(str) | LOWER(str) | |
| 轉大寫 | UCASE(str) | UPPER(str) | |
三、DELETE 語法
| Access | SQL Server | 建議統一寫法 |
DELETE * FROM Table | DELETE FROM Table | DELETE FROM Table (兩者皆支援) |
四、DATEDIFF 間隔單位
| 單位 | Access | SQL Server |
| 天 | 'd' | day / dd / d |
| 小時 | 'h' | hour / hh |
| 分 | 'n' | minute / mi / n |
| 秒 | 's' | second / ss / s |
| 月 | 'm' | month / mm / m |
| 年 | 'yyyy' | year / yy / yyyy |
建議:SQL Server 使用完整單位名稱 (day, hour) 可讀性最佳;Access 僅接受引號縮寫。
五、布林/True-False 處理
- Access:
True/False, Yes/No, -1/0 可互換。
- SQL Server:無 Boolean 型別,使用
BIT (1/0)。查詢條件必須寫 WHERE Flag = 1 或 WHERE Flag = 0。
- 遷移時:Access Yes/No 欄位 → SQL Server
BIT NOT NULL DEFAULT 0。
六、日期時間函數
| 功能 | Access | SQL Server |
| 目前日期時間 | NOW(), DATE(), TIME() | GETDATE() (精度 3.33ms), SYSDATETIME() (高精度), CAST(GETDATE() AS DATE), CAST(GETDATE() AS TIME) |
| 日期加減 | DATEADD('d', n, dt) | DATEADD(day, n, dt) |
| 日期差異 | DATEDIFF('d', dt1, dt2) | DATEDIFF(day, dt1, dt2) |
| 日期部分 | DAY(dt), MONTH(dt), YEAR(dt) | 同名函數 DAY(), MONTH(), YEAR() |
關鍵差異:Access 允許在 SQL 中直接呼叫 VBA 函數 (如 CStr(), Format()),SQL Server **嚴禁**,需改用 CAST/CONVERT/FORMAT。
七、Access 遷移至 SQL Server 後必檢項目
| 項目 | 現象 | 處理 |
| 自動編號 | 遷移後變為普通 INT,失去 Identity | ALTER TABLE ... ALTER COLUMN Id INT IDENTITY(1,1) (需重建表或新增欄位) |
| 數值型別長度 | Access 長整數/雙精度無長度概念 | 統一對應 BIGINT/DECIMAL(18,4),避免 FLOAT 精度問題 |
| 預設值遺失 | 遷移工具常漏掉 DEFAULT | 逐表補齊 DEFAULT 約束 |
| 日期函數 | NOW()/DATE()/TIME() 失效 | 全域取代為 GETDATE() / CAST(... AS DATE/TIME) |
| 日期型別 | 預設轉為 SMALLDATETIME (範圍 1900-2079) | 改為 DATETIME 或 DATETIME2 以支援更廣範圍 |
八、快速轉換腳本範例 (Python/regex 概念)
import re
def access_to_tsql(sql):
# NULL 判斷
sql = re.sub(r'NOT\s+ISNULL\s*\(', 'IS NOT NULL(', sql, flags=re.IGNORECASE)
sql = re.sub(r'ISNULL\s*\(', 'IS NULL(', sql, flags=re.IGNORECASE)
# 大小寫
sql = re.sub(r'\bLCASE\s*\(', 'LOWER(', sql, flags=re.IGNORECASE)
sql = re.sub(r'\bUCASE\s*\(', 'UPPER(', sql, flags=re.IGNORECASE)
# DELETE
sql = re.sub(r'DELETE\s+\*\s+FROM', 'DELETE FROM', sql, flags=re.IGNORECASE)
# DATEDIFF 間隔單位
interval_map = {"'d'": 'day', "'h'": 'hour', "'n'": 'minute', "'s'": 'second', "'m'": 'month', "'yyyy'": 'year'}
for acc, tsql in interval_map.items():
sql = sql.replace(acc, tsql)
# 日期函數
sql = re.sub(r'\bNOW\s*\(\s*\)', 'GETDATE()', sql, flags=re.IGNORECASE)
sql = re.sub(r'\bDATE\s*\(\s*\)', 'CAST(GETDATE() AS DATE)', sql, flags=re.IGNORECASE)
sql = re.sub(r'\bTIME\s*\(\s*\)', 'CAST(GETDATE() AS TIME)', sql, flags=re.IGNORECASE)
return sql
結語
掌握上述 8 大類差異與遷移檢查清單,配合自動化轉換腳本與人工複核,可大幅降低 Access → SQL Server 遷移風險。長期專案建議在資料存取層 (DAL/ORM) 封裝方言差異,應用程式碼層面保持資料庫中立。