Domanda

I have the need to frequently transfer databases between servers using a backup/restore procedure. I am running into the problem where the login information is not maintained when restoring and would like to prevent this from happening.

Here are the details:

  • There are 3 servers each running an instance of SQL Server 2008 Express
  • I transfer the database content between these 3 servers.
  • I transfer database content by using the "backup" option on the source database and the "restore" option on the destination.
  • When restoring I use the option to Overwrite the existing database (WITH REPLACE).
  • The database can be named the same on each server (although currently the name does slightly differ on one of the servers. They are named xxx.mydatabase and local.mydatabase).
  • Each instance of SQL Express has a Security Login "MyDatabaseUser".
  • Each database has a User with the same name "MyDatabaseUser".

After moving the database with this method, the correct usernames still appear where expected but the User Mappings associated with the Security Login have been deleted. The Security Login for the Server and the User for the database are no longer connected and applications fail when attempting to log in.

This problem is easy enough to solve manually by deleting the User from the database and re-adding the User Mappings to the Security Login however when doing this frequently, it is quite annoying. Beyond being annoying, needing to manually remap this information causes a brief amount of downtime. I can get it done pretty quick, but there must be a better way that can further minimize this.

I would like to use the backup/restore options and have the user mappings preserved. I am not familiar with all of the backup/restore options and what they do. Are there settings I can use to accomplish this?

I have full access to the servers and logins and can change things if needed to make this happen. Although I would ideally like to use the backup/restore option, I am also willing to run a package, queries, scripts, create/use and external program, or whatever to accomplish this task. The main goal is to minimize the steps needed and possibility of error during the database transfer.

È stato utile?

Soluzione

Put all SQL-users (i mean logins) into the sysadmin role.
Alternatively, you need to remap the logins to the users.

EXEC sp_change_users_login 'Auto_Fix', 'user'

where the syntax is this:

EXEC sp_change_users_login 'UserName', 'LoginName'

Here's how I do it:

        public override void RemapDbUserLogins(string DbName)
        {
            string strSQL = @"
USE " + EscapeDbName(DbName) + @";

DECLARE @__DatabasePrincipal sysname --nvarchar(128) 
DECLARE @__ServerPrincipal sysname --nvarchar(128) 

DECLARE @__CurPrincipals CURSOR

SET @__CurPrincipals = CURSOR FOR
(
    -- sysname: nvarchar(128)
    -- Get Login for user 
    SELECT 
         dp.name AS user_name 
        --,sp.name AS login_name 

        ,
        CASE 
            WHEN sp.name IS NULL 
                THEN
                    CASE 
                        WHEN 1 = (SELECT COUNT(*) FROM sys.server_principals  AS spSubSel WHERE spSubSel.name = dp.name COLLATE Latin1_General_CI_AS)
                            THEN dp.name 
                        WHEN dp.name LIKE '%[_]DE' AND 1 = (SELECT COUNT(*) FROM sys.server_principals  AS spSubSel WHERE spSubSel.name = SUBSTRING(dp.name, 1, LEN(dp.name) - 3) COLLATE Latin1_General_CI_AS)
                            THEN SUBSTRING(dp.name, 1, LEN(dp.name) - 3) 
                        WHEN dp.name LIKE '%DE' AND 1 = (SELECT COUNT(*) FROM sys.server_principals  AS spSubSel WHERE spSubSel.name = SUBSTRING(dp.name, 1, LEN(dp.name) - 2) COLLATE Latin1_General_CI_AS)
                            THEN SUBSTRING(dp.name, 1, LEN(dp.name) - 2) 

                        WHEN dp.name LIKE '%[_]EN' AND 1 = (SELECT COUNT(*) FROM sys.server_principals  AS spSubSel WHERE spSubSel.name = SUBSTRING(dp.name, 1, LEN(dp.name) - 3) COLLATE Latin1_General_CI_AS)
                            THEN SUBSTRING(dp.name, 1, LEN(dp.name) - 3) 
                        WHEN dp.name LIKE '%EN' AND 1 = (SELECT COUNT(*) FROM sys.server_principals  AS spSubSel WHERE spSubSel.name = SUBSTRING(dp.name, 1, LEN(dp.name) - 2) COLLATE Latin1_General_CI_AS) 
                            THEN SUBSTRING(dp.name, 1, LEN(dp.name) - 2) 

                        WHEN dp.name LIKE '%[_]FR' AND 1 = (SELECT COUNT(*) FROM sys.server_principals  AS spSubSel WHERE spSubSel.name = SUBSTRING(dp.name, 1, LEN(dp.name) - 3) COLLATE Latin1_General_CI_AS)
                            THEN SUBSTRING(dp.name, 1, LEN(dp.name) - 3) 
                        WHEN dp.name LIKE '%FR' AND 1 = (SELECT COUNT(*) FROM sys.server_principals  AS spSubSel WHERE spSubSel.name = SUBSTRING(dp.name, 1, LEN(dp.name) - 2) COLLATE Latin1_General_CI_AS) 
                            THEN SUBSTRING(dp.name, 1, LEN(dp.name) - 2) 

                        WHEN dp.name LIKE '%[_]IT' AND 1 = (SELECT COUNT(*) FROM sys.server_principals  AS spSubSel WHERE spSubSel.name = SUBSTRING(dp.name, 1, LEN(dp.name) - 3) COLLATE Latin1_General_CI_AS)
                            THEN SUBSTRING(dp.name, 1, LEN(dp.name) - 3) 
                        WHEN dp.name LIKE '%IT' AND 1 = (SELECT COUNT(*) FROM sys.server_principals  AS spSubSel WHERE spSubSel.name = SUBSTRING(dp.name, 1, LEN(dp.name) - 2) COLLATE Latin1_General_CI_AS) 
                            THEN SUBSTRING(dp.name, 1, LEN(dp.name) - 2) 

                        --ELSE 'foo' + SUBSTRING(dp.name, 1, LEN(dp.name) - 3)
                        --ELSE N'ApertureWebServices' 
                        ELSE NULL --COLLATE Latin1_General_CI_AS
                    END 
            ELSE sp.name COLLATE Latin1_General_CI_AS
        END AS CorrespondingUser 

    FROM sys.database_principals AS dp 

    LEFT JOIN sys.server_principals  AS sp 
        ON sp.sid = dp.sid 

    WHERE dp.type_desc = 'SQL_USER' 
    AND dp.sid IS NOT NULL 
    AND dp.sid != 0 

    AND sp.name IS NULL -- WHERE sids don't match 

)
OPEN @__CurPrincipals
    FETCH NEXT FROM @__CurPrincipals INTO @__DatabasePrincipal, @__ServerPrincipal 
    WHILE @@FETCH_STATUS = 0
    BEGIN
        PRINT @__DatabasePrincipal + N' = ' + @__ServerPrincipal 

        --EXEC sp_helptext 'sp_change_users_login' 

        DECLARE @Action varchar(10) -- REPORT / UPDATE_ONE / AUTO_FIX 
        DECLARE @UserNamePattern sysname  = NULL 
        DECLARE @LoginName sysname  = NULL 
        DECLARE @Password sysname  = NULL 


        SET @Action = 'Update_One' 
        SET @UserNamePattern = @__DatabasePrincipal -- N'ApertureWebServicesDE' 
        SET @LoginName = @__ServerPrincipal -- N'ApertureWebServices' 

        IF @LoginName IS NOT NULL
        BEGIN
            PRINT 'EXECUTE sp_change_users_login @Action = ''' + @Action + N''', @UserNamePattern = ''' + @UserNamePattern + N''', @LoginName = ''' + @LoginName + N''', @Password = ' + ISNULL(N'''' + @Password + N'''', N'NULL') 
            EXECUTE sp_change_users_login @Action, @UserNamePattern, @LoginName, @Password 
        END
        ELSE 
        BEGIN 
            PRINT N'Ignored user ' + @UserNamePattern
        END 
    FETCH NEXT FROM @__CurPrincipals INTO @__DatabasePrincipal, @__ServerPrincipal 
    END
CLOSE @__CurPrincipals
DEALLOCATE @__CurPrincipals
";

            ExecuteWithoutTransaction(strSQL);
        } // End Sub RemapDbUserLogins
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top