﻿# SQL Server 安全性概論與無法刪除資料庫使用者的解決辦法

經常有人來問我特定 SQL Server 資料庫裡的使用者無法刪除的問題，這問題其實跟 SQL Server 的安全性架構有很大關係，解決這個問題當然還是瞭解觀念的重要性大於知道如何解決問題。除了講解觀念外，本篇文章也會列出一些出問題時的情境，方便快速解決問題。

我先假設各位已經知道 **驗證** (Authentication) 與 **授權** (Authorization) 的差別，簡單的來說 **驗證**負責辨識登入者的身份，而**授權**在於提供特定特定身份授與特定的操作權限。

在 SQL Server 裡提供了兩種驗證模式，分別是 **Windows 驗證** (Windows Authentication) 與 **混合模式驗證** (Mixed Modes authentication)，藉以控制應用程式連接 SQL Server 的方式。另外，SQL Server 又區分了兩種登入的類型，分別是 **Windows 登入** 與 **SQL Server 登入**，在從應用程式連接資料庫時，使用 Windows 登入可以不用輸入密碼在連線參數上，這種登入方式是比較建議的登入方法。

在設定**登入帳號**時，該帳號可以隸屬於一個以上的 **伺服器角色** (Server Role)，這些在 SQL Server 中存在的伺服器角色數量是固定的，無法新增、也無法刪除，預設所有新增的「登入帳號」都會自動歸類在 **public** 這個**伺服器角色**下，這個 public 角色只有允許連接 SQL Server 的權力而已，並沒有任何資料庫的的存取權限。

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/0e5ae009cefa5f7e639d-image_1017.png)

所以我們從 SSMS ( Management Studio ) 中所看到的「安全性」節點，其實是為於「伺服器節點」之下，並不隸屬於任何資料庫，這是應用程式連接 SQL Server 時的第一個安全性關卡，應該妥為設定才行，不應該給予登入帳號有過大的權限，例如將該登入帳號加入到 sysadmin 伺服器角色之類的。

因此，這裡所儲存的是 **登入帳號** (Login)，而非 **資料庫使用者** (Database User)，這點非常重要。

當我們在任意一個資料庫中建立**資料庫使用者**時，所建立的其實是一個所謂的 **安全性主體** (security principal)，他代表的是一個能夠被賦予安全性設定的對象，也就是一個可授權的對象。

這裡的 **資料庫使用者** 在建立時必須對應到一個 **登入帳戶**，而在建立時可以設定任意的**使用者名稱**，方便好記即可（一般我們會習慣設定使用者名稱與登入名稱一致），並且挑選出一個登入帳戶，在設定好之後我們就可以針對這個 **資料庫使用者** 設定各種權限相關的設定，例如：資料庫角色、安全性實體、…等等。

如下圖是透過 SSMS 新增 **資料庫使用者** 的畫面，我看過許多人在建立資料庫使用者時，會勾選如下圖藍色框框的勾選項目，也就是設定該使用者「擁有特定的結構描述」，勾選這一項就會導致日後無法刪除**資料庫使用者**的問題，解決辦法在本文稍後會提及。

[![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/da638374052881b4ee76-image_thumb_68.png)](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/3c6cbe3d0da429c20a52-image_1018.png)

除了設定 SQL Server 預設的 **資料庫角色** 外，你也可以自行新增**資料庫角色**，並設定該角色應該有哪些權限能用，基於資安的[最小權限原則](http://zh.wikipedia.org/wiki/%E6%9C%80%E5%B0%8F%E6%9D%83%E9%99%90%E5%8E%9F%E5%88%99) ([Principle of least privilege](http://en.wikipedia.org/wiki/Principle_of_least_privilege))，我們應該授與資料庫使用者最小的使用權限，以加強資料庫的存取安全性，這時我們就可以透過自訂的資料庫角色來設定適當的權限，最後再將**資料庫使用者**加入到該**資料庫角色**即可將權限套用完成。

若要設定 **資料庫使用者** 或 **資料庫角色** 的細部權限，我們可以切換至 **安全性實體** (Securables) 頁籤。所謂的 **安全性實體** (Securable) 與 **安全性主體** (Security Principle) 不太一樣，但中文卻被翻譯的非常相似，讀者必須特別小心。這裡的 **安全性實體** (Securables) 指的是可以指派權限給特定 **資料庫使用者** 或 **資料庫角色** 的資料庫物件。

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/0324c39a95228e86808f-image_1019.png)

在你搜尋出 **安全性實體** (或 **資料庫物件**) 後，就可以針對這些物件設定更細部的權限，如下圖示，我選擇了一個預存程序，下方的權限部分就又分為 **改變**、**取得擁有權**、**執行**、**控制**、**檢視定義** 等權限，如果你只需給特定使用者「執行」的權限，就只要在「執行」權限的「授與」這一欄勾選即可。

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/ae07d9667de43a455bfb-image_1020.png)

接下來，在資料庫層級的安全性中，還有一個所謂的 **結構描述** (Schema) 的觀念，這是從 SQL Server 2005 開始才有的概念，在 SQL Server 2000 以前，使用者名稱本身就是資料庫物件的一部分，但新的版本不再有這種限制，而改用 **結構描述** (Schema) 來「擁有」這些資料庫物件。

如果由 **結構描述** (Schema) 來「擁有」這些資料庫物件，那麼應該由誰來「擁有」**結構描述** 呢？當然是透過 **資料庫使用者** 或 **資料庫角色** 囉！

第 1 種無法刪除的情境：資料庫預設 **結構描述** (Schema) 的擁有者被指定了想刪除的**資料庫使用者**

也就是說，當有特定 **結構描述** 被特定 **資料庫使用者** 所擁有時，該 **資料庫使用者** 就會因為被鎖定導致無法刪除，所以在刪除資料庫使用者經常會遇到一個很常見的錯誤訊息如下：

Msg 15138, Level 16, State 1, Line 2  
資料庫主體在資料庫中擁有 **結構描述** 且無法卸除。

那麼我們要怎樣才能一次瀏覽所有 **結構描述** (Schema) 的擁有者是誰呢？以下這行 T-SQL 可以幫你列出

```
SELECT * FROM INFORMATION_SCHEMA.SCHEMATA
```

如果你發現你想刪除的**資料庫使用者**擁有了特定 **結構描述** (Schema) 的話，你可以修改特定 **結構描述** 的擁有者給其他人，例如：dbo

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/cebeb9b14cc8db840ec9-image_1021.png) 

移轉擁有權的 T-SQL 語法如下：

```
ALTER AUTHORIZATION ON SCHEMA::[db_owner] TO [dbo]
```

第 2 種無法刪除的情境：無法刪除多餘的 **結構描述**，所以也連帶無法刪除**資料庫使用者**

這問題並不常見，除非開發人員真的亂新增**結構描述**才會這樣，否則**結構描述**應該是在資料庫設計階段所設計過的，況且我們只要把該**結構描述**的擁有者改成其他**資料庫使用者**或**資料庫角色**，就可以刪除該資料庫使用者了。

但如果因為設計有所改變而需要刪除特定**結構描述**卻無法刪除的話，其錯誤訊息如下：

訊息 3729，層級 16，狀態 1，行 2  
無法 drop schema TESTUser，因為物件 'PK\_Table\_1' 正在參考它。

為了要刪除該結構描述，你必須要先轉移這些被參考到的資料庫物件的結構描述到另一個結構描述裡，以下是搜尋出所有該結構描述所用到的物件清單，這一行 T-SQL 指令可以幫你快速找出使用該結構描述的物件：（備註：如下範例請將 **MySchema** 修改成你的**結構描述名稱**）

```
SELECT schema_name(uid) as SCHEMA_NAME, * FROM sysobjects WHERE schema_name(uid) = 'MySchema'
```

假設我們要將 **MySchema.View\_1** 物件轉移到 **dbo** 結構描述，其物件名稱會變成 **dbo.View\_1**，那麼我們可以使用以下 T-SQL 執行轉移動作：

```
ALTER SCHEMA dbo TRANSFER MySchema.View_1
```

 第 3 種無法刪除的情境：有**資料庫角色**的擁有者被指定了想刪除的**資料庫使用者**

這種問題的錯誤訊息如下：

Msg 15421, Level 16, State 1, Line 2  
資料庫主體擁有**資料庫角色**且無法卸除。

一樣只要轉移擁有者即可，以下是方便查出所有**資料庫角色**擁有者的 T-SQL 語法：

```
SELECT user_name(owning_principal_id) as OWNER_NAME, * FROM [sys].[database_principals]
```

以下是轉移資料庫角色擁有者的語法：

```
ALTER AUTHORIZATION ON ROLE::[testrole] TO [dbo]
```

第 4 種無法刪除的情境：有 **服務** 的擁有者被指定了想刪除的**資料庫使用者**

這種問題的錯誤訊息如下：

Msg 15138, Level 16, State 1, Line 2  
資料庫主體在資料庫中擁有 **服務** 且無法卸除。

以下是方便查出所有**服務**擁有者的 T-SQL 語法：

```
SELECT user_name(principal_id) as OWNER_NAME, * FROM [sys].[services]
```

**結語**

資料庫中還有許多其他物件都有可能因為參考到資料庫使用者而無法刪除，但熟悉了基本觀念後，應該就會懂得融會貫通，相信日後遇到無法刪除使用者的問題應該也能輕鬆的自行解決。

**相關連結**

-   [MS-SQL 資料庫還原到另一台主機無法登入](https://web.archive.org/web/20230606221613/https://blog.xuite.net/tolarku/blog/39283410)
-   [最小許可權原則 - 维基百科，自由的百科全书](http://zh.wikipedia.org/wiki/%E6%9C%80%E5%B0%8F%E6%9D%83%E9%99%90%E5%8E%9F%E5%88%99)
-   [SQL 資料庫還原到到另一台後無法登入要怎麼解決](/post/2007/11/18/SQL-Backup-and-Restore-cause-ASPNET-Can-not-Login.aspx)
-   [解決【規劃的新資料庫擁有者已經是資料庫的使用者】問題](/post/2008/08/29/The-proposed-new-database-owner-is-already-a-user-in-the-database.aspx)
-   [開發SQL相關應用程式時要注意登入帳號的預設結構描述](/post/2008/09/21/SQL-Server-User-should-set-the-right-Default-Schema.aspx)
