0

我正在使用下面的代码为我提供一个运行良好的数据库的孤立用户列表。我现在正在尝试将其构建成可以遍历数据库列表并在单个表中返回结果的东西。

这是我用来获取孤立用户的代码:

select DB_NAME() AS [Current Database], u.uid, u.name, u.sid, rm.role_principal_id as 'Role ID', dp1.name as 'Role'
from sys.sysusers u
left join sys.syslogins l on UPPER(u.sid) = UPPER(l.sid)
inner join sys.database_role_members rm on rm.member_principal_id = u.uid
left join sys.database_principals dp on dp.principal_id = rm.member_principal_id
left join sys.database_principals dp1 on dp1.principal_id = rm.role_principal_id 
where u.uid > 4 and u.issqlrole = 0
--and issqluser = 1 --commented out to include orphaned windows logins
and l.name is null
order by 1

我可以使用以下内容获取数据库列表,但我想结合以上内容依次浏览每个数据库。

SELECT name FROM sys.databases 
WHERE database_id > 4

对此的任何帮助或指示将不胜感激!

谢谢 :-)

4

2 回答 2

0

我知道在不同数据库中运行查询的唯一方法是使用游标遍历数据库名称并在每次迭代中执行动态查询(使用所需的数据库名称)。

为了避免在动态查询中使用表变量时遇到困难(至少对我而言),我使用了一个本地临时表(单个#前缀)来存储结果。

为此,我必须在查询中的每个schema.table 前面加上一个占位符,该占位符在每次迭代中替换为数据库名称。这样,每个表都将完全符合db.schema.table

另请注意,EXEC像这样使用会使您容易受到 sql 注入的影响。

我希望这可以帮助你:

CREATE TABLE #results (
    currdb sysname,
    uid int,
    uname sysname,
    usid int,
    rpid int,
    rname sysname)

DECLARE @sqltemplate VARCHAR(4000), @sql VARCHAR(4000)
SET @sqltemplate = 'SELECT
   ''[@db]'', u.uid, u.name, u.sid, rm.role_principal_id, dp1.name
    FROM [@db].sys.sysusers u
        LEFT JOIN [@db].sys.syslogins l ON UPPER(u.sid) = UPPER(l.sid)
        INNER JOIN [@db].sys.database_role_members rm
            ON rm.member_principal_id = u.uid
        LEFT JOIN [@db].sys.database_principals dp
            ON dp.principal_id = rm.member_principal_id
        LEFT JOIN [@db].sys.database_principals dp1
            ON dp1.principal_id = rm.role_principal_id
    WHERE u.uid > 4 AND u.issqlrole = 0 AND l.name IS NULL
    ORDER BY 1'

DECLARE @dbname sysname

DECLARE dbnames CURSOR FOR
    SELECT name FROM sys.databases WHERE database_id > 4

OPEN dbnames

FETCH NEXT FROM dbnames INTO @dbname

WHILE @@FETCH_STATUS = 0
BEGIN
  SET @sql = REPLACE(@sqltemplate, '@db', @dbname)
  INSERT #results
    EXEC (@sql)
  FETCH NEXT FROM dbnames INTO @dbname
END
CLOSE dbnames
DEALLOCATE dbnames

SELECT * FROM #results
DROP TABLE #results
于 2013-10-03T10:20:50.623 回答
0

您可以使用系统存储过程sp_msforeachdb针对每个数据库运行一组 SQL 语句。在这种情况下,该过程的每次执行都将返回一个单独的结果集,因此我们使用一个临时表将所有结果收集到一个表中。然后,您可以使用普通 SQL 操作此单个表来过滤和排序最终结果。

IF OBJECT_ID('tempdb..#Orphans') IS NOT NULL
  DROP TABLE #Orphans

CREATE TABLE #Orphans 
(
  [Current Database] SYSNAME,
  [uid]              SMALLINT,
  [name]             SYSNAME,
  [sid]              VARBINARY(85),
  [Role ID]          SMALLINT,
  [Role]             SYSNAME
)

INSERT #Orphans
EXEC sp_msforeachdb 'use [?];
    select DB_NAME() AS [Current Database]
         , u.uid, u.name
         , u.sid
         , rm.role_principal_id as [Role ID]
         , dp1.name as [Role]
      from sys.sysusers u
             left join sys.syslogins l 
               on UPPER(u.sid) = UPPER(l.sid)
                 inner join sys.database_role_members rm 
                   on rm.member_principal_id = u.uid
                     left join sys.database_principals dp 
                       on dp.principal_id = rm.member_principal_id
                         left join sys.database_principals dp1 
                           on dp1.principal_id = rm.role_principal_id 
     where u.uid > 4 and u.issqlrole = 0
     --and issqluser = 1 --commented out to include orphaned windows logins
       and l.name is null
    order by 1'

SELECT * FROM #Orphans

有几点需要注意的是“?” 每次在新数据库中执行模板时,SQL 模板中的字符都会被填写数据库名称。我还更改了您的列别名以使用“[”字符而不是单引号。

于 2013-10-03T11:06:37.893 回答