首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> SQL Server 資料庫 >> 關於SqlServer

SQL Server 孤立用戶完整解決方案:sp_change_users_login 與含資料庫用戶遷移最佳實踐

     編輯:關於SqlServer

前言:備份還原後的「隱形殺手」

跨伺服器備份還原資料庫時,資料庫內的用戶與伺服器層級的登入因 SID 不匹配而斷裂,形成「孤立用戶」。應用連線報錯 `Login failed` 或 `User already exists`,實為 SID 不符。本文提供從偵測、修復、預防的完整閉環方案,涵蓋 `sp_change_users_login`、Contained Database User、遷移腳本自動化。

一、核心概念:登入 vs 用戶 vs SID

孤立用戶定義:資料庫用戶存在,但 `sys.database_principals.sid` 在 `sys.server_principals` 中找不到對應 SID (SQL Login);或 Windows 登入 SID 在新伺服器不存在。

二、偵測孤立用戶

1. 經典方法 (相容所有版本)

USE YourDB;
EXEC sp_change_users_login 'Report';
-- 回傳:UserName, UserSID

2. 現代查詢 (SQL 2005+、資訊更豐富)

SELECT
    dp.name AS DatabaseUser,
    dp.type_desc AS UserType,
    dp.sid AS UserSID,
    sp.name AS ServerLogin,
    sp.type_desc AS LoginType,
    sp.sid AS LoginSID,
    CASE WHEN sp.sid IS NULL THEN 'ORPHANED' ELSE 'MAPPED' END AS Status
FROM sys.database_principals dp
LEFT JOIN sys.server_principals sp ON dp.sid = sp.sid
WHERE dp.type IN ('S', 'U', 'G')  -- SQL_USER, WINDOWS_USER, WINDOWS_GROUP
  AND dp.principal_id > 4;        -- 排除系統用戶

三、修復策略對比

層級物件識別碼用途
伺服器層級Login (登入)SID (Security Identifier, 二進位)身分驗證、連線權限、伺服器角色
資料庫層級User (用戶)SID (同登入 SID 建立關聯)資料庫權限、結構擁有者、角色成員
方法適用場景優點缺點/風險指令範例
sp_change_users_login 'Auto_Fix'大量孤立用戶、名稱完全一致一鍵修復、無需逐一指定名稱不一致時會錯誤映射、可能授予錯誤權限、不建議生產使用`EXEC sp_change_users_login 'Auto_Fix', 'user1', NULL, 'pwd';`
sp_change_users_login 'Update_One'精確控制、名稱不同、需指定密碼明確映射、可重設密碼、安全可控逐一操作、繁瑣`EXEC sp_change_users_login 'Update_One', 'db_user', 'server_login';`
ALTER USER ... WITH LOGIN (SQL 2005+ 推薦)現代標準語法、支援 Windows 登入語意清晰、不需密碼 (Windows 登入)、支援含資料庫用戶需 SQL 2005+`ALTER USER db_user WITH LOGIN = server_login;`
重建用戶 (Drop/Create)用戶權限簡單、可接受權限重授徹底乾淨、無歷史包袱需腳本化權限重授、可能破壞結構擁有權`DROP USER u; CREATE USER u FOR LOGIN l;`

四、含資料庫用戶遷移最佳實踐 (SQL 2012+)

根治孤立用戶的終極方案:Contained Database User (包含資料庫用戶)。用戶認證資訊 (密碼雜湊) 直接儲存在資料庫內,無需伺服器層級登入,跨伺服器還原即可直接連線,零孤立風險。

1. 啟用含資料庫認證

-- 伺服器層級
EXEC sp_configure 'contained database authentication', 1; RECONFIGURE;

-- 資料庫層級
ALTER DATABASE YourDB SET CONTAINMENT = PARTIAL;

2. 建立/遷移為含資料庫用戶

-- 新建
CREATE USER AppUser WITH PASSWORD = 'ComplexP@ssw0rd!', DEFAULT_SCHEMA = dbo;

-- 從 SQL Login 遷移 (SQL 2012+)
-- 步驟 1: 建立同名含資料庫用戶 (密碼同步)
DECLARE @sql NVARCHAR(MAX) = N'';
SELECT @sql += 'CREATE USER ' + QUOTENAME(name) + ' WITH PASSWORD = ''Migrate_' + name + '_' + CONVERT(NVARCHAR(36), NEWID()) + ''', DEFAULT_SCHEMA = ' + QUOTENAME(default_schema_name) + ';'
FROM sys.database_principals
WHERE type = 'S' AND principal_id > 4; -- SQL 用戶
EXEC sp_executesql @sql;

-- 步驟 2: 移除舊用戶 (權限已由含資料庫用戶繼承)
-- 注意:需先轉移結構擁有權
EXEC sp_change_users_login 'Report'; -- 確認無孤立
-- 批次轉移結構擁有權 (若有)
-- 最後 DROP 舊用戶

3. 連線字串調整

-- 原來
Server=OldServer;Database=YourDB;User Id=AppUser;Password=...;

-- 含資料庫用戶 (同樣語法,但驗證發生在資料庫層級)
Server=NewServer;Database=YourDB;User Id=AppUser;Password=...;
-- 無需在 NewServer 建立 Login!

五、自動化遷移腳本 (跨伺服器還原後執行)

-- usp_FixOrphanedUsers_PostRestore
CREATE PROCEDURE dbo.usp_FixOrphanedUsers_PostRestore
    @TargetLoginPrefix NVARCHAR(50) = N''  -- 若登入名稱有前綴差異
AS
BEGIN
    SET NOCOUNT, XACT_ABORT ON;

    -- 1. 偵測孤立 SQL 用戶
    DECLARE @Orphans TABLE (DbUser SYSNAME, DbSID VARBINARY(85), LoginName SYSNAME);
    INSERT INTO @Orphans
    SELECT dp.name, dp.sid, sp.name
    FROM sys.database_principals dp
    LEFT JOIN sys.server_principals sp ON dp.sid = sp.sid
    WHERE dp.type = 'S' AND dp.principal_id > 4 AND sp.sid IS NULL;

    -- 2. 嘗試按名稱匹配現有登入 (含前綴)
    UPDATE o SET o.LoginName = sp.name
    FROM @Orphans o
    JOIN sys.server_principals sp ON sp.name = @TargetLoginPrefix + o.DbUser
    WHERE sp.type = 'S';

    -- 3. 逐一修復 (Update_One)
    DECLARE @DbUser SYSNAME, @LoginName SYSNAME;
    DECLARE cur CURSOR LOCAL FAST_FORWARD FOR
        SELECT DbUser, LoginName FROM @Orphans WHERE LoginName IS NOT NULL;
    OPEN cur;
    FETCH NEXT FROM cur INTO @DbUser, @LoginName;
    WHILE @@FETCH_STATUS = 0
    BEGIN
        BEGIN TRY
            EXEC sp_change_users_login 'Update_One', @DbUser, @LoginName;
            PRINT 'Fixed: ' + @DbUser + ' -> ' + @LoginName;
        END TRY
        BEGIN CATCH
            PRINT 'FAILED: ' + @DbUser + ' - ' + ERROR_MESSAGE();
        END CATCH
        FETCH NEXT FROM cur INTO @DbUser, @LoginName;
    END
    CLOSE cur; DEALLOCATE cur;

    -- 4. 報告剩餘無法自動修復的
    SELECT * FROM @Orphans WHERE LoginName IS NULL;
END;

六、Windows 登入孤立處理

  • Windows 登入 SID 由 AD 決定,跨網域遷移 SID 必變
  • 無法用 `sp_change_users_login` 修復 (僅限 SQL Login)
  • 解決:`ALTER USER db_user WITH LOGIN = [NEWDOMAIN\user];` 或重建用戶映射新登入
  • 最佳:遷移至含資料庫用戶 (密碼認證) 或統一使用 SQL Login 遷移

七、預防清單:遷移前必做

動作工具/腳本驗收
匯出登入腳本 (含 SID、密碼雜湊)sp_help_revlogin (Microsoft 官方腳本) / dbatools `Export-DbaLogin`目標伺服器建立同 SID 登入
啟用含資料庫認證`sp_configure 'contained database authentication', 1`目標伺服器支援
資料庫設定 PARTIAL CONTAINMENT`ALTER DATABASE ... SET CONTAINMENT = PARTIAL`還原後保持設定
遷移用戶為含資料庫用戶上述遷移腳本零孤立用戶
驗證應用連線測試連線字串、權限矩陣全功能正常

八、總結

孤立用戶本質是 SID 身分識別斷裂。短期用 `ALTER USER ... WITH LOGIN` 精確修復;長期必須推行 Contained Database User 徹底消除跨伺服器身分依賴。遷移自動化腳本 + `sp_help_revlogin` 匯出登入 + 含資料庫認證,構成「零孤立」鐵三角。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved