简体   繁体   中英

SQL - Iterate through a list of databases to return single result for orphaned users

I'm using the below code to give me a list of orphaned users for a database which works great. I'm now trying to build this into something that will iterate through the list of databases and return the results in a single table.

Here is the code I am using to obtain the orphaned users:

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

I can get a list of databases using the below but I want to combine with the above to go through each database in turn.

SELECT name FROM sys.databases 
WHERE database_id > 4

Any help or pointers with this would be much appreciated!

Thanks :-)

The only way I know of running queries in different databases is to use a cursor to iterate through the database names and executing a dynamic query (with the desired database name) in each iteration.

To run away from the difficulties (at least to me) in using table variables with dynamic queries, I used a local temporary table (the single # prefix) in which to store the results.

For this to work, I had to prefix each schema.table in your query with a placeholder that is replaced by the database name in each iteration. That way, each table will be fully qualified with db.schema.table .

Also be aware that using EXEC like this puts you vulnerable to sql injection.

I hope this helps you:

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

You can use the system stored procedure sp_msforeachdb to run a set of SQL statements against each database. Each execution of that procedure, in this case, will return a separate result set so we are using a temporary table to gather all the results up into a single table. This single table you then can manipulate using normal SQL to filter and sort your final results.

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

A few things to note is that the "?" character in the SQL template will be filled in with the database name each time the template is executed in a new database. Also I changed your column aliases to use the "[" characters instead of the single quotes.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM