商務系統需限制同一帳號同時在線。資料表旗標法:登入寫 1、登出寫 0。但異常斷電、瀏覽器關閉、Process Kill 導致旗標殘留 1,用戶無法再次登入,需管理員手動重置。
##GlobalTemp 於最後一個參考工作階段結束時自動刪除OBJECT_ID() 判定為不存在CREATE PROCEDURE dbo.CheckUserLogin
@UserID VARCHAR(20),
@IsLoggedIn BIT OUTPUT
AS
BEGIN
SET NOCOUNT ON
DECLARE @TableName NVARCHAR(100) = N'##Login_' + @UserID
DECLARE @SQL NVARCHAR(MAX)
-- OBJECT_ID 判斷:無活動工作階段引用時回傳 NULL
IF OBJECT_ID('tempdb..' + @TableName) IS NULL
BEGIN
-- 動態建表:避免編譯期解析錯誤
SET @SQL = N'CREATE TABLE ' + @TableName + N' (UserID VARCHAR(20) PRIMARY KEY)'
EXEC sp_executesql @SQL
-- 記錄登入資訊(可擴充:登入時間、IP、SessionID)
EXEC sp_executesql N'INSERT ' + @TableName + N' VALUES (@uid)',
N'@uid VARCHAR(20)', @UserID
SET @IsLoggedIn = 0 -- 允許登入
END
ELSE
BEGIN
SET @IsLoggedIn = 1 -- 已在線,拒絕
END
END
// C# 範例
bool isLoggedIn;
using (var cmd = new SqlCommand("dbo.CheckUserLogin", conn) { CommandType = CommandType.StoredProcedure })
{
cmd.Parameters.AddWithValue("@UserID", userId);
var outParam = cmd.Parameters.Add("@IsLoggedIn", SqlDbType.Bit);
outParam.Direction = ParameterDirection.Output;
cmd.ExecuteNonQuery();
isLoggedIn = (bool)outParam.Value;
}
if (isLoggedIn)
throw new Exception("此帳號已在其他地方登入,請先登出。");
CREATE PROCEDURE dbo.ReleaseUserLogin @UserID VARCHAR(20)
AS
BEGIN
DECLARE @TableName NVARCHAR(100) = N'##Login_' + @UserID
IF OBJECT_ID('tempdb..' + @TableName) IS NOT NULL
EXEC(N'DROP TABLE ' + @TableName)
END
CREATE TABLE ##Login_ (
UserID VARCHAR(20),
SessionID VARCHAR(50), -- 應用層產生的 Session ID
LoginTime DATETIME DEFAULT GETDATE(),
ClientIP VARCHAR(45),
UserAgent VARCHAR(500),
PRIMARY KEY (UserID, SessionID)
)
-- 允許同一用戶多裝置:檢查 SessionID 是否存在
-- 強制踢人:管理員刪除對應 SessionID 列
| 優點 | 缺點/限制 |
|---|---|
| 零資料表旗標維護 | 僅適用單一 SQL Server 實例 |
| 異常斷線自動恢復 | Always On 可用性群組次要複本不可寫入暫存表 |
| 無需排程清理殘留旗標 | 大量併發登入可能產生大量全域暫存表 |
| 實作簡單、效能極佳 | tempdb 壓力大時需監控 |