前置準備:啟用 Ad Hoc Distributed Queries
-- 僅需一次,開啟後建議用畢關閉以減少攻擊面
EXEC sp_configure 'show advanced options', 1; RECONFIGURE;
EXEC sp_configure 'Ad Hoc Distributed Queries', 1; RECONFIGURE;
-- 用畢關閉
EXEC sp_configure 'Ad Hoc Distributed Queries', 0; RECONFIGURE;
EXEC sp_configure 'show advanced options', 0; RECONFIGURE;
四種存取方式對比
| 方式 | 適用場景 | 效能/快取 | 安全性 | 語法複雜度 |
| Linked Server | 頻繁、固定遠端存取;支援分散式交易 | 最佳 (計畫快取、統計資料同步) | 集中管理登入對應 | 初建較繁,後續最簡 |
| OPENQUERY | 需在遠端執行複雜邏輯、利用遠端索引/統計 | 良好 (遠端編譯) | 透過 Linked Server 登入 | 中 |
| OPENROWSET | 臨時、一次性存取;不想建立永久連結 | 一般 (本地編譯) | 連線字串含密碼風險 | 高 (字串參數多) |
| OPENDATASOURCE | 類似 OPENROWSET,語法更簡潔 | 一般 | 同 OPENROWSET | 中 |
1. Linked Server 建立與使用
-- 建立
EXEC sp_addlinkedserver
@server = 'LNK_PROD',
@srvproduct = '',
@provider = 'SQLNCLI', -- SQL 2005+ 建議 SQLNCLI / MSOLEDBSQL
@datasrc = '192.168.10.50'; -- IP 或主機名
-- 登入對應 (本地登入 → 遠端登入)
EXEC sp_addlinkedsrvlogin
@rmtsrvname = 'LNK_PROD',
@useself = 'FALSE',
@locallogin = NULL, -- NULL 表所有本地登入
@rmtuser = 'remote_user',
@rmtpassword = 'StrongPassword';
-- 查詢 (四部分命名)
SELECT * FROM [LNK_PROD].[SalesDB].[dbo].[Orders];
-- 匯入
SELECT * INTO LocalOrders FROM [LNK_PROD].[SalesDB].[dbo].[Orders];
-- 更新遠端
UPDATE r SET r.Status = l.Status
FROM [LNK_PROD].[SalesDB].[dbo].[Orders] r
JOIN LocalOrders l ON r.OrderID = l.OrderID;
-- 移除
EXEC sp_dropserver 'LNK_PROD', 'droplogins';
2. OPENROWSET 即時查詢 (免建立 Linked Server)
-- SQLNCLI (SQL 2005+ 推薦)
SELECT * FROM OPENROWSET(
'SQLNCLI',
'Server=192.168.10.50;UID=remote_user;PWD=StrongPassword;',
'SELECT OrderID, CustID, Amount FROM SalesDB.dbo.Orders WHERE OrderDate > ''2024-01-01'''
) AS t;
-- 信任連線 (Windows 驗證)
SELECT * FROM OPENROWSET(
'SQLNCLI',
'Server=192.168.10.50;Trusted_Connection=yes;',
'SalesDB.dbo.Orders'
) AS t;
-- 匯入/更新/刪除 (需資料來源支援)
INSERT OPENROWSET('SQLNCLI', 'Server=...;Trusted_Connection=yes;', 'SELECT col FROM DB.dbo.T')
SELECT col FROM LocalT;
UPDATE OPENROWSET('SQLNCLI', '...', 'SELECT col FROM DB.dbo.T WHERE id=1')
SET col = 'new';
DELETE FROM OPENROWSET('SQLNCLI', '...', 'SELECT col FROM DB.dbo.T WHERE id=1');
3. OPENQUERY (需先有 Linked Server,遠端執行)
-- 查詢
SELECT * FROM OPENQUERY(LNK_PROD, 'SELECT * FROM SalesDB.dbo.Orders WHERE Amount > 1000');
-- 匯入遠端
INSERT OPENQUERY(LNK_PROD, 'SELECT col FROM SalesDB.dbo.T')
SELECT col FROM LocalT;
-- 更新遠端
UPDATE OPENQUERY(LNK_PROD, 'SELECT col FROM SalesDB.dbo.T WHERE id=1')
SET col = 'new';
4. OPENDATASOURCE (單次連線字串)
SELECT * FROM OPENDATASOURCE(
'SQLNCLI',
'Data Source=192.168.10.50;User ID=remote_user;Password=StrongPassword'
).SalesDB.dbo.Orders;
安全性最佳實務
- **最小權限**:遠端登入布號僅授予必要資料庫的 `SELECT/INSERT/UPDATE/DELETE`,嚴禁 `sysadmin`/`db_owner`。
- **避免明文密碼**:優先使用 Windows 驗證 (`Trusted_Connection=yes`) 或 Linked Server 登入對應 (`sp_addlinkedsrvlogin`),勿在程式碼/腳本中硬編碼密碼。
- **防火牆限制**:遠端 SQL 連接埠 (預設 1433) 僅允許已知應用伺服器 IP 存取。
- **稽核**:啟用 `LOGON`、`EXECUTE` 稽核追蹤跨伺服器存取。
常見錯誤排查
| 錯誤 | 原因 | 解決 |
| SQL Server 阻止了對組件 'Ad Hoc Distributed Queries' 的存取 | 未啟用選項 | 執行上述 `sp_configure` 啟用 |
| 登入失敗 / 解密過程中出錯 (15466, 15185) | 服務主金鑰不匹配 / 登入對應遺失 | `ALTER SERVICE MASTER KEY FORCE REGENERATE;` 重新產生主金鑰,重建登入對應 |
| 分散式交易 (MSDTC) 錯誤 | MSDTC 服務未啟動/防火牆阻擋 | 雙邊啟用 MSDTC、開放 135/動態埠、設定「不需要驗證」/「允許遠端用戶端」 |