You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过C#的Microsoft.SqlServer.Management.Smo.Server高效获取SQL Server数据库用户?

嘿,我之前也碰到过Smo默认遍历数据库用户/所有者时性能拉胯的情况,给你几个亲测有效的优化思路和替代方案:

优化Smo本身的加载行为

Smo默认会采用延迟加载,而且会加载对象的所有属性,这在数据库数量多的时候会导致大量的数据库往返请求,拖慢性能。你可以通过SetDefaultInitFields方法指定只加载你需要的属性,避免不必要的数据传输。

比如获取数据库所有者的示例:

// 提前设置只加载Database对象的Name和Owner属性
m_server.ConnectionContext.SetDefaultInitFields(typeof(Database), "Name", "Owner");

foreach (Database db in m_server.Databases)
{
    Console.WriteLine($"数据库: {db.Name}, 所有者: {db.Owner}");
}

如果要获取每个数据库的用户列表,同样可以限制User对象的加载字段:

// 先限制Database只加载Name属性
m_server.ConnectionContext.SetDefaultInitFields(typeof(Database), "Name");
// 再限制User只加载Name和Type属性
m_server.ConnectionContext.SetDefaultInitFields(typeof(User), "Name", "Type");

foreach (Database db in m_server.Databases)
{
    Console.WriteLine($"=== 数据库: {db.Name} ===");
    foreach (User user in db.Users)
    {
        Console.WriteLine($"用户: {user.Name} ({user.Type})");
    }
}
直接执行T-SQL查询(性能最优)

如果追求极致性能,直接绕过Smo的封装,通过ConnectionContext执行原生T-SQL查询系统视图是最好的选择——毕竟Smo底层也是通过查询系统视图实现的,但它会做很多额外的封装工作。

获取所有数据库及所有者

using (var cmd = m_server.ConnectionContext.CreateCommand())
{
    cmd.CommandText = @"
        SELECT 
            d.name AS DatabaseName,
            sp.name AS OwnerName
        FROM sys.databases d
        INNER JOIN sys.server_principals sp 
            ON d.owner_sid = sp.sid
        WHERE d.state = 0; -- 只筛选在线状态的数据库";
    
    using (var reader = cmd.ExecuteReader())
    {
        while (reader.Read())
        {
            string dbName = reader["DatabaseName"].ToString();
            string ownerName = reader["OwnerName"].ToString();
            Console.WriteLine($"数据库: {dbName}, 所有者: {ownerName}");
        }
    }
}

获取所有数据库的用户列表

using (var cmd = m_server.ConnectionContext.CreateCommand())
{
    cmd.CommandText = @"
        DECLARE @dbName NVARCHAR(128);
        DECLARE dbCursor CURSOR FOR 
            SELECT name FROM sys.databases WHERE state = 0;
        
        OPEN dbCursor;
        FETCH NEXT FROM dbCursor INTO @dbName;
        
        WHILE @@FETCH_STATUS = 0
        BEGIN
            -- 动态查询每个数据库的用户(排除系统内置的主体)
            EXECUTE('
                SELECT 
                    ''' + @dbName + ''' AS DatabaseName,
                    dp.name AS UserName,
                    dp.type_desc AS UserType
                FROM ' + QUOTENAME(@dbName) + '.sys.database_principals dp
                WHERE dp.type NOT IN (''S'', ''C'', ''K'', ''R'') -- 排除系统用户、证书、密钥、角色
                AND dp.name NOT LIKE ''##%##''; -- 排除特殊系统账户
            ');
            
            FETCH NEXT FROM dbCursor INTO @dbName;
        END;
        
        CLOSE dbCursor;
        DEALLOCATE dbCursor;";
    
    using (var reader = cmd.ExecuteReader())
    {
        while (reader.Read())
        {
            string dbName = reader["DatabaseName"].ToString();
            string userName = reader["UserName"].ToString();
            string userType = reader["UserType"].ToString();
            Console.WriteLine($"数据库: {dbName}, 用户: {userName} ({userType})");
        }
    }
}
使用Smo的枚举方法

Smo提供了EnumDatabases这类枚举方法,它们比直接遍历Databases集合更轻量,因为只会返回你指定的属性,不会创建完整的Database对象实例。

示例:

// 枚举数据库,只获取Name和Owner列
DataTable dbTable = m_server.EnumDatabases("Name", "Owner");

foreach (DataRow row in dbTable.Rows)
{
    Console.WriteLine($"数据库: {row["Name"]}, 所有者: {row["Owner"]}");
}

内容的提问来源于stack exchange,提问作者Mare Milojkovic

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 08:05:19