為什麼 SESSION_USER 返回 dbo 而不是 SQL 登錄?
我在 GP 表上有一個觸發器,它記錄關鍵欄位何時更改以及誰更改了它。下面是觸發器範例。
CREATE TRIGGER [dbo].[TRACKAPP_UPDATE] ON [dbo].[Table] AFTER UPDATE AS DECLARE @MDFUSRID char(15) DECLARE @SOPNUMBE char(32) DECLARE @DOCSTATUS INT BEGIN BEGIN IF UPDATE(DOCSTATUS) BEGIN SELECT @MDFUSRID = SESSION_USER SELECT @SOPNUMBE = SOPNUMBE FROM inserted SELECT @DOCSTATUS = DOCSTATUS FROM INSERTED
在 99% 的情況下,這會返回更改欄位的使用者的正確名稱。如果任何 ERP 人員更改了一個欄位(包括我自己),則使用者會顯示一個 dbo。我一直認為這是一個訪問問題。但是,我們發現一些使用者也以 dbo 而不是他們的名字返回。
你對我應該從哪裡開始尋找有什麼建議嗎?
您將使用者與登錄混淆。使用者是數據庫級別的“主體”,而登錄是伺服器級別(也稱為實例級別)的主體。
每個數據庫都有一個
dbo
映射到登錄名的使用者。*當“目前”數據庫是有問題的數據庫時,*將dbo
顯示映射到使用者的登錄。dbo``SESSION_USER
並且,
sysadmin
固定伺服器角色中的登錄名(包括通過作為固定伺服器角色成員的 Windows 組連接的 Windows 登錄名sysadmin
)將顯示dbo
為SESSION_USER
. 即使登錄名映射到該數據庫中具有除預設模式以外的使用者dbo
(因為此問題與模式無關),也會出現這種情況。下面的測試程式碼中的“測試 3”中顯示了一個範例。
SESSION_USER
當數據庫中沒有要映射的使用者時,可以返回登錄名。例如,如果登錄名具有CONTROL SERVER
伺服器權限,以便他們可以連接到任何數據庫但不屬於sysadmin
固定伺服器角色,SESSION_USER
則將僅返回那些沒有使用者映射的數據庫的登錄名。下面的測試程式碼中的“測試 5”中顯示了一個範例。您要使用的是
ORIGINAL_LOGIN()
. 此函式將返回用於連接實例的實際登錄名,即使已使用模擬將目前安全上下文更改為另一個登錄名的安全上下文。以下測試說明並證明了上述行為:
設置
USE [master]; CREATE LOGIN [GazooLogin] WITH PASSWORD = 'NevrCrack'; CREATE DATABASE [GazooDB] COLLATE Latin1_General_100_CI_AS; ALTER AUTHORIZATION ON DATABASE::[GazooDB] TO [sa]; GO CREATE USER [GazooUser1] FROM LOGIN [GazooLogin] WITH DEFAULT_SCHEMA = [GazooSchema1]; GO CREATE SCHEMA [GazooSchema1] AUTHORIZATION [GazooUser1]; GO USE [GazooDB]; CREATE USER [GazooUser2] FROM LOGIN [GazooLogin] WITH DEFAULT_SCHEMA = [GazooSchema2]; GO CREATE SCHEMA [GazooSchema2] AUTHORIZATION [GazooUser2]; GO
測試
-------------------------------------------------- -- Test 1: USE [GazooDB]; EXECUTE AS LOGIN = N'GazooLogin'; SELECT * FROM sys.fn_my_permissions(NULL, N'database') -- database CONNECT SELECT SESSION_USER AS [SESSION_USER], ORIGINAL_LOGIN() AS [ORIGINAL_LOGIN], SUSER_SNAME() AS [SUSER_SNAME], SUSER_NAME() AS [SUSER_NAME]; -- GazooUser2 Dali\Solomon GazooLogin GazooLogin USE [master]; SELECT * FROM sys.fn_my_permissions(NULL, N'database') -- database CONNECT SELECT SESSION_USER AS [SESSION_USER], ORIGINAL_LOGIN() AS [ORIGINAL_LOGIN], SUSER_SNAME() AS [SUSER_SNAME], SUSER_NAME() AS [SUSER_NAME]; -- GazooUser1 Dali\Solomon GazooLogin GazooLogin USE [GazooDB]; -- can only revert from DB where EXECUTE AS was run REVERT; -------------------------------------------------- -- Test 2: USE [master]; EXEC sys.sp_addrolemember N'db_owner', N'GazooUser1'; EXECUTE AS LOGIN = 'GazooLogin'; SELECT * FROM sys.fn_my_permissions(NULL, N'database') -- lots of stuff :) SELECT SESSION_USER AS [SESSION_USER], ORIGINAL_LOGIN() AS [ORIGINAL_LOGIN], SUSER_SNAME() AS [SUSER_SNAME], SUSER_NAME() AS [SUSER_NAME]; -- GazooUser1 Dali\Solomon GazooLogin GazooLogin USE [GazooDB]; SELECT * FROM sys.fn_my_permissions(NULL, N'database') -- database CONNECT SELECT SESSION_USER AS [SESSION_USER], ORIGINAL_LOGIN() AS [ORIGINAL_LOGIN], SUSER_SNAME() AS [SUSER_SNAME], SUSER_NAME() AS [SUSER_NAME]; -- GazooUser2 Dali\Solomon GazooLogin GazooLogin USE [master]; -- can only revert from DB where EXECUTE AS was run REVERT; -------------------------------------------------- -- Test 3: EXEC sys.sp_droprolemember N'db_owner', N'GazooUser1'; EXEC sys.sp_addsrvrolemember N'GazooLogin', N'sysadmin'; EXECUTE AS LOGIN = 'GazooLogin'; SELECT * FROM sys.fn_my_permissions(NULL, N'database') -- lots of stuff :) SELECT SESSION_USER AS [SESSION_USER], ORIGINAL_LOGIN() AS [ORIGINAL_LOGIN], SUSER_SNAME() AS [SUSER_SNAME], SUSER_NAME() AS [SUSER_NAME]; -- dbo Dali\Solomon GazooLogin GazooLogin USE [GazooDB]; SELECT * FROM sys.fn_my_permissions(NULL, N'database') -- lots of stuff :) SELECT SESSION_USER AS [SESSION_USER], ORIGINAL_LOGIN() AS [ORIGINAL_LOGIN], SUSER_SNAME() AS [SUSER_SNAME], SUSER_NAME() AS [SUSER_NAME]; -- dbo Dali\Solomon GazooLogin GazooLogin USE [master]; -- can only revert from DB where EXECUTE AS was run REVERT; -------------------------------------------------- -- Test 4: CREATE LOGIN [SuperDuperDatabaseOwner] WITH PASSWORD = 'NevaCrack'; ALTER AUTHORIZATION ON DATABASE::[GazooDB] TO [SuperDuperDatabaseOwner]; USE [GazooDB]; EXECUTE AS LOGIN = N'GazooLogin'; SELECT * FROM sys.fn_my_permissions(NULL, N'database') -- lots of stuff :) SELECT SESSION_USER AS [SESSION_USER], ORIGINAL_LOGIN() AS [ORIGINAL_LOGIN], SUSER_SNAME() AS [SUSER_SNAME], SUSER_NAME() AS [SUSER_NAME]; -- dbo Dali\Solomon GazooLogin GazooLogin REVERT; SELECT * FROM sys.database_principals; SELECT * FROM sys.server_principals; -------------------------------------------------- -- Test 5: DROP SCHEMA [GazooSchema2]; DROP USER [GazooUser2]; USE [master]; EXEC sp_dropsrvrolemember N'GazooLogin', N'sysadmin'; GRANT CONTROL SERVER TO [GazooLogin]; EXECUTE AS LOGIN = N'GazooLogin'; SELECT SESSION_USER AS [SESSION_USER], ORIGINAL_LOGIN() AS [ORIGINAL_LOGIN], SUSER_SNAME() AS [SUSER_SNAME], SUSER_NAME() AS [SUSER_NAME]; -- GazooUser1 Dali\Solomon GazooLogin GazooLogin USE [GazooDB]; SELECT * FROM sys.fn_my_permissions(NULL, N'database') -- lots of stuff :) SELECT SESSION_USER AS [SESSION_USER], ORIGINAL_LOGIN() AS [ORIGINAL_LOGIN], SUSER_SNAME() AS [SUSER_SNAME], SUSER_NAME() AS [SUSER_NAME]; -- GazooLogin Dali\Solomon GazooLogin GazooLogin USE [master]; -- can only revert from DB where EXECUTE AS was run REVERT; -------------------------------------------------- -- Test 6: USE [GazooDB]; EXECUTE AS LOGIN = N'SuperDuperDatabaseOwner'; SELECT SESSION_USER AS [SESSION_USER], ORIGINAL_LOGIN() AS [ORIGINAL_LOGIN], SUSER_SNAME() AS [SUSER_SNAME], SUSER_NAME() AS [SUSER_NAME]; -- dbo Dali\Solomon SuperDuperDatabaseOwner SuperDuperDatabaseOwner REVERT;
清理
-------------------------------------------------- -- CLEAN UP: USE [master]; DROP DATABASE [GazooDB]; DROP SCHEMA [GazooSchema1]; DROP USER [GazooUser1]; DROP LOGIN [GazooLogin]; DROP LOGIN [SuperDuperDatabaseOwner];
**!!!此外,**在 SQL Server 中使用觸發器時,在執行以下操作時需要非常小心:
SELECT @SOPNUMBE = SOPNUMBE FROM inserted SELECT @DOCSTATUS = DOCSTATUS FROM INSERTED
問題是
INSERTED
偽表中可能有多行,具體取決於受 DML 操作影響的行數。如果更新的行不止一行,則這兩個變數中的值將是最後一行的值INSERTED
(並且它們沒有特定的順序)。如果列的值在所有行中都相同,那麼我認為這並不重要,但最好不要以這種方式構造觸發器程式碼。