I have a script like this:
declare #username nvarchar(255)
declare #Alterstatement nvarchar(2000)
declare #userloginname nvarchar(255)
declare #InsertIntoHistory nvarchar(2000)
declare getusername cursor
for
select name from [SysAdmin].[dbo].[DisabledAccount]
where name in (select name from sys.server_principals)
open getusername
Fetch next from getusername into #username
while ##FETCH_STATUS=0
begin
set #userloginname = '[' + #username + ']'`
set #Alterstatement = 'Alter Login' +#userloginname +'Disable'
set #InsertIntoHistory = 'Insert into DisabledAccountHistory (DisabledName,ServerName) values('''
+ #username + ''','''+ ##servername +''')'
exec(#alterstatement)
exec(#InsertIntoHistory)
Fetch next from getusername
into #username
end
close getusername
deallocate getusername
I'm using this script to disable some users and insert into a history table. But when I run this, some problem shows up. For example when I have only one user that I need to disable, it will run 3 times and insert 3 rows into the history table. How can I ask it just run 1 time for each user?
I would get rid of the cursor completely as it isn't needed here. You can still disable all the logins and insert the data into your history table.
declare #SQL nvarchar(max) = ''
select #SQL = #SQL + 'ALTER LOGIN ' + QUOTENAME(name) + ' DISABLE;'
from [SysAdmin].[dbo].[DisabledAccount]
where name in (select name from sys.server_principals)
group by name
exec sp_executesql #SQL
Insert into DisabledAccountHistory
(
DisabledName
, ServerName
)
select name
, ##servername
from [SysAdmin].[dbo].[DisabledAccount]
where name in (select name from sys.server_principals)
group by name
Related
I am trying to write a sql statement to control a report header selection of multiple database by user groups a user is assigned to. This is to limit the database selection security when running an SSRS report so that they can' only select their branch or group of branches they have access to. So far I can return the group results for a single user. I am trying to get a list of all active AD users and groups that are like GRP-XP%. Here is my script so far, that only works for a single username. Eventually this table will be passed to PowerBI, so I'd need username, usergroup table of listing complete.
Declare #username varchar(max) = 'ssmith'
DECLARE #Query NVARCHAR(1024), #Path NVARCHAR(1024)
SET #Query = '
SELECT #Path = distinguishedName
FROM OPENQUERY(CSAD, ''
SELECT distinguishedName, SAMAccountName
FROM ''''LDAP://DC=Domain,DC=co, dc=uk''''
WHERE
objectClass = ''''user''''
AND sAMAccountName = ''''' + Replace(#Username, 'domain\', '') + '''''
'')
'
EXEC SP_EXECUTESQL #Query, N'#Path NVARCHAR(1024) OUTPUT', #Path = #Path OUTPUT
SET #Query = '
SELECT
Replace(Right(cn, Len(cn)-7), '' '', '' '')
FROM OPENQUERY (CSAD, ''<LDAP://DC=Domain,DC=co,DC=uk>;(&(objectClass=group)(member:1.2.840.113556.1.4.1941:=' + #Path +'));cn, adspath;subtree'')
where CN like ''GRP-XP%''
Order By cn'
Declare #table Table (Name varchar(100))
Insert into #table
EXEC SP_EXECUTESQL #Query
select * from #table
So the results would look like;
Thank you
I found some code I wrote many years ago. I can't test it as we no longer use a local AD domain controller but it definitely worked at some point.
It basically returns a list of all users and associated groups so it should be easy enough to modify for your needs.
ALTER PROC [AD].[Get_AD_AllUsersWithGroups]
AS
DECLARE #Query NVARCHAR(1024), #Path NVARCHAR(1024)
DECLARE #distinguishedName nvarchar(256)
DECLARE #SAMAccountName nvarchar(256)
CREATE TABLE #users (distinguishedName nvarchar(1000), SAMAccountName nvarchar(100))
CREATE TABLE #results(SAMAccountName nvarchar(100), DistinguishedName nvarchar(1000), GroupName nvarchar(1000), ActiveDirectoryPath nvarchar(1000))
-- Get all the users from AD
SET #Query = '
SELECT distinguishedName, SAMAccountName
FROM OPENQUERY(ADSI, ''
SELECT distinguishedName , SAMAccountName
FROM ''''LDAP://DC=MyDomain,DC=local''''
WHERE
objectClass = ''''user''''
'')
'
INSERT INTO #users
EXEC SP_EXECUTESQL #Query
-- For each user in #users, get a list of groups they belong to
DECLARE cUsers CURSOR FOR
SELECT distinguishedName, SAMAccountName from dbo.#users u
order by u.distinguishedName
OPEN cUsers
FETCH NEXT FROM cUsers
INTO #distinguishedName, #SAMAccountName
WHILE ##FETCH_STATUS = 0
BEGIN
SET #distinguishedName = REPLACE(#distinguishedName, '''', '''''')
SET #SAMAccountName = REPLACE(#SAMAccountName, '''', '''''')
SET #Query = '
INSERT INTO #results
SELECT ''' + #SAMAccountName + ''', ''' + #distinguishedName + ''', cn as GroupName, AdsPath AS ActiveDirectoryPath
FROM OPENQUERY (ADSI, ''<LDAP://DC=MyDomain,DC=local>;(&(objectClass=group)(member:1.2.840.113556.1.4.1941:='
+ #distinguishedName +'));cn, adspath;subtree'')'
EXEC SP_EXECUTESQL #Query
FETCH NEXT FROM cUsers
INTO #distinguishedName, #SAMAccountName
END
CLOSE cUsers
DEALLOCATE cUsers
SELECT * FROM dbo.#results r
GO
I'm exploring options to setup a SQL 2016 database server to support teaching students database fundamentals. There will be close to 200 students on the course.
I'd like to automate the creation of all the user accounts and associated database via a script - this means the database for each user needs to be created at the same time. Basically each student's user login name and database will be named as the student's ID. Login authentication will be SQL authentication, not Windows as the lab PCs are not on AD.
If anyone could point me in the direction of any scripts I could modify or use it would be extremely helpful - basically if I go down this route I'll need to do it every year so scripting would be optimal.
Thanks
D.
Using Dynamic Sql and using while loop we can loop the student names and create database and logins.Hope it helps you
IF OBJECT_ID('Tempdb..#USerTable') IS NOT NULL
DROP TABLE #USerTable
IF OBJECT_ID('Tempdb..#USerNameINSERT') IS NOT NULL
DROP TABLE #USerNameINSERT
IF OBJECT_ID('Tempdb..#PasswordsINSERT') IS NOT NULL
DROP TABLE #PasswordsINSERT
--ADD Your student Names Passwords in a single line as below
DECLARE #Usernames NVARCHAR(max) = 'Stud1,Stud2,Stud3,Stud4'
,#Password NVARCHAR(max) = 'Password1,Password2,Password3,Password4'
,#Usernameselect NVARCHAR(max)
,#DatabaseNameselect NVARCHAR(max)
,#PasswordSelect NVARCHAR(max)
,#Usernameselect1 NVARCHAR(max)
,#DatabaseNameselect1 NVARCHAR(max)
,#PasswordSelect1 NVARCHAR(max)
DECLARE #USerName TABLE (
ID INT IDENTITY
,Usernames NVARCHAR(max)
,Passwords NVARCHAR(max)
)
INSERT INTO #USerName (
UserNAmes
,Passwords
)
SELECT #Usernames
,#Password
SELECT ID,UserNAmes INTO #USerNameINSERT
FRom (
SELECT Row_number()Over(Order by (SELECT NULL)) AS ID,Split.a.value('.', 'VARCHAR(1000)') AS UserNAmes
FROM (
SELECT ID, CAST('<S>' + REPLACE(UserNAmes, ',', '</S><S>') + '</S>' AS XML) AS UserNAmes
FROM #USerName
) AS A
CROSS APPLY UserNAmes.nodes('/S') AS Split(a)
)DT
SELECT ID,Passwords INTO #PasswordsINSERT
FRom (
SELECT Row_number()Over(Order by (SELECT NULL)) AS ID,Split.a.value('.', 'VARCHAR(1000)') AS Passwords
FROM (
SELECT CAST('<S>' + REPLACE(Passwords, ',', '</S><S>') + '</S>' AS XML) AS Passwords
FROM #USerName
) AS A
CROSS APPLY Passwords.nodes('/S') AS Split(a)
)DT
SELECT u.ID
,u.UserNAmes
,u.UserNAmes+'Db' AS DatabaseName
,p.Passwords
INTO #USerTable
FROM #USerNameINSERT u
INNER JOIN #PasswordsINSERT p ON p.ID = u.ID
DECLARE #minId INT
,#maxId INT
,#SqlQuery NVARCHAR(max)
,#SqlQuery1 NVARCHAR(max)
,#SqlQuery2 NVARCHAR(max)
SELECT #minId = MIN(ID)
,#maxId = Max(Id)
FROM #USerTable
WHILE (#minId <= #maxId)
BEGIN
SELECT #Usernameselect = UserNAmes
,#DatabaseNameselect = DatabaseName
,#PasswordSelect = Passwords
FROM #USerTable
WHERE ID = #minId
SET #SqlQuery = 'CREATE DATABASE ' + #DatabaseNameselect
SET #SqlQuery1 = 'USE [' + #DatabaseNameselect + ']'
SET #SqlQuery2 = 'CREATE LOGIN ' + #Usernameselect + ' WITH PASSWORD=N''' + #PasswordSelect + ''' MUST_CHANGE, CHECK_EXPIRATION=ON, CHECK_POLICY=ON'+ CHAR(13) + CHAR(10) + 'GO'
SET #SqlQuery = ISNULL('', 'GO') + CHAR(13) + CHAR(10) + #SqlQuery
+ CHAR(13) + CHAR(10) + 'GO' + CHAR(13) + CHAR(10) + #SqlQuery1
+ CHAR(13) + CHAR(10) + 'Go' + + CHAR(13) + CHAR(10) + #SqlQuery2
--EXEC (#SqlQuery)
PRINT #SqlQuery
SET #minId = #minId + 1
END
Use CREATE LOGIN command.
Following example will create user myDBUser.
USE [master]
GO
CREATE LOGIN [myDBUser] WITH PASSWORD=N'myPassword' MUST_CHANGE, DEFAULT_DATABASE=[myDB], CHECK_EXPIRATION=ON, CHECK_POLICY=ON
GO
And of course create DB user for such login
USE [myDB]
GO
CREATE USER myDBUser FOR LOGIN myDBUser;
I think something like this would work, you might need to alter the dynamic SQL bit around the login as its a bit thrown together.
--Create table for bulk insert
Create Table UserNameCSV (UserID int, UserName Varchar(100))
--Insert login names from C:\csvtest.txt
BULK
INSERT UserNameCSV
FROM 'c:\csvtest.txt'
WITH
(FIELDTERMINATOR = ',', ROWTERMINATOR = '\n')
GO
-- Run Cursor
Declare #UserName Varchar(100)
Declare db_Cursor CURSOR FAST_FORWARD FOR
Select UserName from UserNameCSV
OPEN db_Cursor
FETCH NEXT FROM db_Cursor INTO #UserName
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #Sql varchar(max) ='CREATE DATABASE ' + #UserName
DECLARE #Sql1 varchar(max) = 'USE [' + #UserName + ']'
DECLARE #Sql2 varchar(max) = 'CREATE LOGIN ' + #UserName + ' WITH PASSWORD=N''myPassword123!'' MUST_CHANGE, CHECK_EXPIRATION=ON, CHECK_POLICY=ON'
EXECUTE(#Sql)
EXECUTE (#Sql1)
EXECUTE (#Sql2)
FETCH NEXT FROM db_Cursor into #UserName
END
CLOSE db_Cursor
DEALLOCATE db_Cursor
I'm using SQL Server Management Studio and I have multiple server connections and I need to know where a specific stored procedure exists in every server which contains multiple databases!
Looking for a query to run on each server
Here is a little script that you could run on each server.
I know that it uses a while loop, which is generally not preferred, someone might be able to improve on it.
Run the script from the master database.
SQL Query:
set nocount on
declare #procname varchar(150) = '<SPROCNAME_TO_FIND>' -- SET THIS TO THE STORED PROCEDURE NAME YOU ARE LOOKING FOR
declare #alldbonserver table
( databasename varchar(150),
doneflag bit )
declare #foundtbl table
( countcol int,
dbname varchar(150) )
declare #errortbl table
( dbname varchar(150) )
insert #alldbonserver
( databasename,
doneflag )
select sdb.name,
0
from sys.databases sdb
declare #curdbname varchar(150),
#sqlcmd varchar(max)
while exists (select 1
from #alldbonserver
where doneflag = 0)
begin
select top 1
#curdbname = databasename
from #alldbonserver
where doneflag = 0
select #sqlcmd = 'select distinct 1, ''' + #curdbname + ''' as dbname from ' + #curdbname + '.sys.objects where type = ''P'' and name = ''' + #procname + ''''
begin try
insert #foundtbl
( countcol, dbname )
exec(#sqlcmd)
end try
begin catch
insert #errortbl
values ( #curdbname )
end catch
update #alldbonserver
set doneflag = 1
where databasename = #curdbname
end
select dbname as 'Databases with stored procedure'
from #foundtbl
select dbname as 'Unable to access databases'
from #errortbl
set nocount off
Sample output:
Databases with stored procedure
-----------------------------------------
MainDatabase
SomeOtherDatabase
Unable to access databases
-----------------------------------------
model
ReportServer$SQLTemp
VeryPrivateDatabase
If you are using SSMS, you could create a server registration group that contains all of the servers you connect to, right click on the group and run this query against the group: exec sp_MSforeachdb 'select * from [?].sys.procedures where name=''<your_name_here>'''
This query will enumerate each of the databases on each of the servers in the group (note: the single quotes are like that for a reason...)
I just put this together on SQL2012. It should return all Stored Procs on a Server for a given name. If you leave #SPName blank, you will get all of them.
The Query utilizes sys.all_objects so you could easily change it to work the same for tables, functions, any or all db objects.
DECLARE #SPName VARCHAR(256)
SET #SPName = 'My_SP_Name'
DECLARE #DBName VARCHAR(256)
DECLARE #varSQL VARCHAR(512)
DECLARE #getDBName CURSOR
SET #getDBName = CURSOR FOR
SELECT name
FROM sys.databases
CREATE TABLE #TmpTable (DBName VARCHAR(256),
SchemaName VARCHAR(256),
SPName VARCHAR(256))
OPEN #getDBName
FETCH NEXT
FROM #getDBName INTO #DBName
WHILE ##FETCH_STATUS = 0
BEGIN
SET #varSQL = 'USE [' + #DBName + '];
INSERT INTO #TmpTable
SELECT '''+ #DBName + ''' AS DBName,
SCHEMA_NAME(schema_id) AS SchemaName,
name AS SPName
FROM sys.all_objects
WHERE [type] = ''P'' AND name LIKE ''%' + #SPName + '%'''
EXEC (#varSQL)
FETCH NEXT
FROM #getDBName INTO #DBName
END
CLOSE #getDBName
DEALLOCATE #getDBName
SELECT *
FROM #TmpTable
DROP TABLE #TmpTable
BackDrop: We are researching why a number of accounts were missed in a process. We have went back to as far as we have data. We now have a rather large list of accounts that for whatever reason were missed. Now this process without going into too much detail is VERY VERY complex and we need to know why these accounts and only these accounts were missed. As any DataBase we have many many automated procedures that run all the time, so there is really at this point no telling what on earth happened to cause these accounts to get missed. My only bet I think at solving this is to find similarities between these accounts. Obviously we have tried looking at the more common places and have since found nothing.
Issue: I want to use SQL to return all the tablenames and columnnames in our database Where these list of accounts have the same value in a column or columns of a table. I have created a query to find tablenames, columns, and so forth but dont know how to bring it all together to create one query that will give me all the results I want. I am certain that a cursor will need to be used and lots of inner joining but I am just not sure how this should be done.
Again:
Lets say I have account numbers 123456 and 654321 and I know our DataBase has 3,000 tables with a column reference to account number with a name of either AccountNumber, AccountNum, or Account. I want to search and find all tables that have a column with the name AccountNumber, AccountNum, or Account that has a value of 123456 or 654321. Then with these tables, for each table I want to take the rows Where the column whether the name be AccountNumber, AccountNum, or Account has a value of either 123456 and 654321 and then for each of those rows I want to check each column of each row to see if the columns on a row for account number 123456 is eqaul to a column on a row for account number 654321 , if so then I want it to return the column name and the tablename. This way I can see what these accounts have in common.
ADVANCED PORTION:
IF some poor soul is able to do the above then I'd also like to create a query that will return
The tablename and when it was updated. I would get the updated value by checking each column in each table and if the column has a type of "timestamp" or a default value of "GetDate()" then that column would be used as updated. In final result set that shows were all changes have happened for those account nubmers it will order by updated.
A first approach, rustic (I'm not that used to T-SQL, I did more PL/SQL), but which may help you going further, AND TESTED IN SQL SERVER 2008. Hope it works in 2005...)
So, we create two procedures, one calling the other
The provided code can only check, in one time
- for 2 differents IDs
- for all concerned fields (Account, AccountNum, AccountNumber)
The idea (checking for AccountNumber column)
Find the tables (in table INFORMATION_SCHEMA.columns, which lists your database tables) which have a column with one of the 3 names provided
For every table found :
create a dynamic query :
select count(*) from <THE_TABLE> where <Account_column_name> IN (123456 654321);
If we have 2 results (mean that our Ids are both present in table), we launch the second procedure, with parameters : #TableName = <THE_TABLE>, #FieldName = <Account_column_name>, #FirstId = 123456, #SecondId = 654321
We find the column names for <THE_TABLE> (again in INFORMATION_SCHEMA.columns).
For every column found :
create a dynamic query
select count(*) from <THE_TABLE> as T1
inner join <THE_TABLE> as T2 on T1.<COLUMN_NAME> = T2.<COLUMN_NAME>
where T1.<Account_column_name>= 123456
and T2.<Account_column_name>= 654321
if count(*) = 1, it means that the same value exists in the same column of the same table for the given ids.
In that case, we print <THE_TABLE> and <THE_COLUMN>
To launch search, in sql management studio, just make
EXEC GetSimilarValuesForFieldAndValue 123456, 654321
and in the "Messages" part, you should have a list of "results".
CREATE procedure [dbo].[GetSimilarValuesForFieldAndValue](#FirstId int, #SecondId int)
AS
DECLARE #sql nvarchar(MAX);
DECLARE #params NVARCHAR(MAX);
DECLARE #Count int;
DECLARE #Name NVARCHAR(MAX);
DECLARE #FieldName NVARCHAR(MAX);
DECLARE db_cursor CURSOR for
select TABLE_NAME, COLUMN_NAME FROM INFORMATION_SCHEMA.columns
where COLUMN_NAME IN('Account', 'AccountNumber', 'AccountNum');
OPEN db_cursor
FETCH next from db_cursor into #Name, #FieldName
while ##FETCH_STATUS = 0
begin
select #sql =
N' SELECT #Count=Count(*) FROM ' + #Name +
N' WHERE ' +#FieldName+' IN (#FirstId,#SecondId)'
SELECT #params = N'#FieldName NVARCHAR(MAX), #FirstId int, #SecondId int, #Count int out'
EXEC sp_executesql #sql, #params, #FieldName, #FirstId, #SecondId, #Count OUT
if (#Count = 2)
begin
exec dbo.CompareFields #Name, #FieldName, #FirstId, #SecondId
end
FETCH NEXT FROM db_cursor INTO #Name, #FieldName
end
close db_cursor
DEALLOCATE db_cursor
GO
The second one :
CREATE procedure [dbo].[CompareFields](#TableName NVARCHAR(MAX), #FieldName NVARCHAR(MAX), #FirstId int, #SecondId int)
as
DECLARE #ColumnName NVARCHAR(MAX)
DECLARE #Sql NVARCHAR(MAX)
DECLARE #Params NVARCHAR(MAX)
DECLARE #Count int
DECLARE cfCursor CURSOR FOR
SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS
where TABLE_NAME = ''+#TableName+' '
AND COLUMN_NAME <> ' '+#FieldName+''
OPEN cfCursor
FETCH next from cfCursor into #ColumnName
while ##FETCH_STATUS = 0
begin
select #Sql =
N' SELECT #Count = count(*) from ' +#TableName + ' T1 '+
N' INNER JOIN ' + #TableName + ' T2 ON T1.' +#ColumnName + ' = T2.' + #ColumnName +
N' WHERE T1.' +#FieldName + ' = '+ CAST(#FirstId as varchar) +
N' AND T2.' + #FieldName + ' = '+CAST(#SecondId as varchar)
SELECT #Params =
N'#TableName VARCHAR(MAX), #ColumnName VARCHAR(MAX), '+
N'#FieldName VARCHAR(MAX), #FirstId int, #SecondId int, #Count int out'
exec sp_executesql #sql, #Params, #TableName, #ColumnName, #FieldName, #FirstId, #SecondId, #Count OUT
if #Count = 1
begin
--print tableName and column Name if value is identic
print 'Table : ' + #TableName + ' : same value for ' + #ColumnName
end
FETCH NEXT FROM cfCursor INTO #ColumnName
end
close cfCursor
DEALLOCATE cfCursor
GO
I actually had to do this for Guids at one point. Here is the script for doing with Guids. One sec and I'll work on modifying it to suit your needs:
DECLARE #table VARCHAR(100)
DECLARE #column VARCHAR(100)
DECLARE #value INT
SET #value = '06B8BD6C-A8EC-4EB3-9562-6666EE86952D'
DECLARE table_cursor CURSOR
FOR select tbl.Name, cols.name as TableName FROM sys.columns cols JOIN
sys.tables tbl on cols.object_id = tbl.object_id
where system_type_id = 36
OPEN table_cursor
FETCH NEXT FROM table_cursor
INTO #table, #column;
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #SQL NVARCHAR(1000)
SET #SQL = 'SELECT ''' + #Table + ''' AS TBL,''' +
#column + ''' AS COL FROM [' + #table + ']
WITH(NOLOCK) WHERE ' + #column + ' = ''' + CAST(#value AS VARCHAR(50)) + ''''
print #sql
EXEC sp_executesql #Sql
FETCH NEXT FROM table_cursor
INTO #table, #column;
END
CLOSE table_cursor
DEALLOCATE table_cursor
Updated to handle for searching on a field name:
DECLARE #table VARCHAR(100)
DECLARE #column VARCHAR(100)
DECLARE #value UNIQUEIDENTIFIER
SET #value = --ENTER YOUR ACCOUNT NUMBER HERE
DECLARE table_cursor CURSOR
select tbl.Name, cols.name as TableName FROM sys.columns cols JOIN
sys.tables tbl on cols.object_id = tbl.object_id
where cols.Name = 'AccountNumber'
OR cols.Name = 'AccountNum' OR cols.Name = 'Account'
OPEN table_cursor
FETCH NEXT FROM table_cursor
INTO #table, #column;
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #SQL NVARCHAR(1000)
SET #SQL = 'SELECT ''' + #Table + ''' AS TBL,''' + #column +
''' AS COL FROM [' + #table + '] WITH(NOLOCK)
WHERE ' + #column + ' = ''' + CAST(#value AS VARCHAR(50)) + ''''
print #sql
EXEC sp_executesql #Sql
FETCH NEXT FROM table_cursor
INTO #table, #column;
END
CLOSE table_cursor
DEALLOCATE table_cursor
After performing a database restore, I want to run a dynamic script to fix ophaned users. My script below loops through all users that are displayed after executing sp_change_users_login 'report' and applys an "alter user [username] with login = [username]" statement to fix SID conflicts. I'm getting an "incorrect syntax error on line 15" and can't figure out why...help..
DECLARE #Username varchar(100), #cmd varchar(100)
DECLARE userLogin_cursor CURSOR FAST_FORWARD
FOR
SELECT UserName = name FROM sysusers
WHERE issqluser = 1 and (sid IS NOT NULL AND sid <> 0×0)
AND suser_sname(sid) IS NULL
ORDER BY name
FOR READ ONLY
OPEN userLogin_cursor
FETCH NEXT FROM userLogin_cursor INTO #Username
WHILE ##fetch_status = 0
BEGIN
SET #cmd = ‘ALTER USER ‘+#username+‘ WITH LOGIN ‘+#username
EXECUTE(#cmd)
FETCH NEXT FROM userLogin_cursor INTO #Username
END
CLOSE userLogin_cursor
DEALLOCATE userLogin_cursor
Orphaned users can be fixed by using the [dbo].[sp_change_users_login] stored procedure.
Loop through all your users and execute the procedure
Good Luck
DECLARE #UserCount INT
DECLARE #UserCurr INT
DECLARE #userName VARCHAR(100)
DECLARE #vsql NVARCHAR(4000)
DECLARE #Users TABLE(
id INT IDENTITY(1,1) PRIMARY KEY NOT NULL,
userName VARCHAR(100))
INSERT INTO #Users(UserName)
SELECT [name] FROM
--
master.[dbo].sysUsers -- SQL 2008 & SQL 2005
--master.dbo.sysxlogins -- SQL 2000
SELECT #UserCount = max([id]) FROM #Users
SET #UserCurr = 1
WHILE (#UserCurr <= #UserCount)
BEGIN
SELECT #userName=userName FROM #Users WHERE [id] =#UserCurr
SET #vsql = '[dbo].[sp_change_users_login] ''AUTO_FIX'',''' + #userName + ''''
-- EXEC(#vsql)
PRINT #vsql
SET #UserCurr = #UserCurr + 1
END
DECLARE #Username VARCHAR(100),
#cmd VARCHAR(100)
DECLARE userlogin_cursor CURSOR FAST_FORWARD FOR
SELECT username = name
FROM sysusers
WHERE issqluser = 1
AND (sid IS NOT NULL
AND sid <> 0x01)
AND Suser_sname(sid) IS NULL
ORDER BY name
FOR READ ONLY
OPEN userlogin_cursor
FETCH NEXT FROM userlogin_cursor INTO #Username
WHILE ##FETCH_STATUS = 0
BEGIN
SET #cmd = 'ALTER USER [' + #username + '] WITH LOGIN = [' + #username + ']'
EXECUTE(#cmd)
FETCH NEXT FROM userlogin_cursor INTO #Username
END
CLOSE userlogin_cursor
DEALLOCATE userlogin_cursor
I've used a similar approach, wrapping the code in a stored procedure:
USE [master]
GO
CREATE PROCEDURE [sp_AutoFixAllUsers]
AS
BEGIN
DECLARE #AutoFixCommand NVARCHAR(MAX)
SET #AutoFixCommand = ''
SELECT --dp.[name], dp.[sid] AS [DatabaseSID], sp.[sid] AS [ServerSID],
#AutoFixCommand = #AutoFixCommand + ' '
+ 'EXEC sp_change_users_login ''Auto_Fix'', ''' + dp.[name] + ''';'-- AS [AutoFixCommand]
FROM sys.database_principals dp
INNER JOIN sys.server_principals sp
ON dp.[name] = sp.[name] COLLATE DATABASE_DEFAULT
WHERE dp.[type_desc] IN ('SQL_USER', 'WINDOWS_USER', 'WINDOWS_GROUP')
AND sp.[type_desc] IN ('SQL_LOGIN', 'WINDOWS_LOGIN', 'WINDOWS_GROUP')
AND dp.[sid] <> sp.[sid]
IF (#AutoFixCommand <> '')
BEGIN
PRINT 'Fixing users in database: ' + DB_NAME()
PRINT #AutoFixCommand
EXEC(#AutoFixCommand)
PRINT ''
END
END
GO
I then used the sys.sp_MS_marksystemobject stored procedure to make my stored procedure available in all user databases (allowing it to operate on local objects)
EXEC sys.sp_MS_marksystemobject 'sp_AutoFixAllUsers'
You can then run it as follows:
EXEC [MyDB].[dbo].[sp_AutoFixAllUsers]
Or for every database using sp_msforeachdb:
EXEC sp_msforeachdb '[?].[dbo].[sp_AutoFixAllUsers]'