ユーザーが多い場合、DROP USERに時間がかかりすぎる


11

十分なRAMと高速ディスクを備えたSQL Server 2014インスタンスでは、データベースにアクセスできるユーザーが160人以上います。なんらかの理由で私にはわからないためDROP USER [username]、このデータベースでコマンドを実行すると、ユーザーあたり最大5秒かかります。

ログインへのユーザーの再マッピングと権限の復元は非常に高速です。

本番環境からDEVデータベースを更新するという状況では、すべてのデータベースユーザーを削除して再作成する必要があります。したがって、データベースユーザーを削除して再作成する必要があります。

DROP USERコマンドを高速化するにはどうすればよいですか?

私が書いているインスタンスでは、160回以上実行する必要があることを覚えておいてください。

これは私が使っているSQLです:

DECLARE drop_user_cur CURSOR FOR 
SELECT name FROM #drop_users

OPEN drop_user_cur
FETCH NEXT FROM drop_user_cur INTO @user

WHILE @@FETCH_STATUS = 0
BEGIN
    SET @sql = 'use [' + @db_name + '] DROP USER [' + @user + ']'
    BEGIN TRY
        print @sql
        EXECUTE(@sql)
    END TRY
    BEGIN CATCH
        print 'ERREUR : ' + @sql
    END CATCH
    FETCH NEXT FROM drop_user_cur INTO @user
END

CLOSE drop_user_cur
DEALLOCATE drop_user_cur

問題はカーソルからではありません。DROP USER最大5秒かかるのは実際の時間です。

を使用するsp_whoisactiveと、wait_typeはになりNULLます。

ここに画像の説明を入力してください

期間に注意を払っていない、DROPCREATE USERで実行されたWHILEことをより分も言っている理由であるループ。

プロファイラーは実行する125,000以上の読み取りを示していますDROP USER

Service Brokerが有効になっていません。


1
@CraigEfrein回答の削除を取り消すことができます。私のコメントをあなたの答えに含めてくれてありがとう。ソリューションが機能していてうれしいです!
Kin Shah

回答:


6

この問題の解決策は、データベースでService Brokerを有効にすることでした。

ALTER DATABASE [Database_name] SET NEW_BROKER WITH ROLLBACK IMMEDIATE;

データベースのService Brokerを有効にした後、ドロップユーザーは実質的に瞬時になりました。

Kinは以前のコメントでService Brokerが有効になっているかどうかを尋ねたので、正しい方向に検索できました。


2

これは質問に対する回答ではなく、完全に却下するための議論です。

私があなたのコメントを正しく理解した場合:

本番環境からDEVデータベースを更新するという状況では、すべてのデータベースユーザーを削除して再作成する必要があります。したがって、データベースユーザーを削除して再作成する必要があります。

あなたの目標は、本番データを開発システムにコピーし、同じユーザー名を使用して本番と同じ権限で機能させることです。

最短のパスは、このKB記事の msで説明されている手順を使用して、本番システムから開発システムにSQLログインを複製することです。

msで定義された手順を使用すると、開発サーバーでデータベースを復元でき、権限は変更なしで機能します。

上記の手順を使用して、SQL 2005の本番環境をテスト環境と開発環境のSQL 2012環境の両方にコピーしました。
ユーザーのクローンを作成した後、単純なデータベースの復元により、最新のデータを備えた完全に機能する2つの新しいシステムができました。

このソリューションの大きな利点は、開発データを更新するには、運用データベースを復元するだけです。参照、類義語などを調整する必要がありますが、権限は破損しません。

これは参考のための記事のコードですが、古いSQLバージョンとの互換性、パスワード暗号化の詳細、およびサーバー間でログインを転送する際の頭痛の種となるその他の情報についての備考と詳細がある記事を参照してください。

USE master
GO
IF OBJECT_ID ('sp_hexadecimal') IS NOT NULL
  DROP PROCEDURE sp_hexadecimal
GO
CREATE PROCEDURE sp_hexadecimal
    @binvalue varbinary(256),
    @hexvalue varchar (514) OUTPUT
AS
DECLARE @charvalue varchar (514)
DECLARE @i int
DECLARE @length int
DECLARE @hexstring char(16)
SELECT @charvalue = '0x'
SELECT @i = 1
SELECT @length = DATALENGTH (@binvalue)
SELECT @hexstring = '0123456789ABCDEF'
WHILE (@i <= @length)
BEGIN
  DECLARE @tempint int
  DECLARE @firstint int
  DECLARE @secondint int
  SELECT @tempint = CONVERT(int, SUBSTRING(@binvalue,@i,1))
  SELECT @firstint = FLOOR(@tempint/16)
  SELECT @secondint = @tempint - (@firstint*16)
  SELECT @charvalue = @charvalue +
    SUBSTRING(@hexstring, @firstint+1, 1) +
    SUBSTRING(@hexstring, @secondint+1, 1)
  SELECT @i = @i + 1
END

SELECT @hexvalue = @charvalue
GO

IF OBJECT_ID ('sp_help_revlogin') IS NOT NULL
  DROP PROCEDURE sp_help_revlogin
GO
CREATE PROCEDURE sp_help_revlogin @login_name sysname = NULL AS
DECLARE @name sysname
DECLARE @type varchar (1)
DECLARE @hasaccess int
DECLARE @denylogin int
DECLARE @is_disabled int
DECLARE @PWD_varbinary  varbinary (256)
DECLARE @PWD_string  varchar (514)
DECLARE @SID_varbinary varbinary (85)
DECLARE @SID_string varchar (514)
DECLARE @tmpstr  varchar (1024)
DECLARE @is_policy_checked varchar (3)
DECLARE @is_expiration_checked varchar (3)

DECLARE @defaultdb sysname

IF (@login_name IS NULL)
  DECLARE login_curs CURSOR FOR

      SELECT p.sid, p.name, p.type, p.is_disabled, p.default_database_name, l.hasaccess, l.denylogin FROM 
sys.server_principals p LEFT JOIN sys.syslogins l
      ON ( l.name = p.name ) WHERE p.type IN ( 'S', 'G', 'U' ) AND p.name <> 'sa'
ELSE
  DECLARE login_curs CURSOR FOR


      SELECT p.sid, p.name, p.type, p.is_disabled, p.default_database_name, l.hasaccess, l.denylogin FROM 
sys.server_principals p LEFT JOIN sys.syslogins l
      ON ( l.name = p.name ) WHERE p.type IN ( 'S', 'G', 'U' ) AND p.name = @login_name
OPEN login_curs

FETCH NEXT FROM login_curs INTO @SID_varbinary, @name, @type, @is_disabled, @defaultdb, @hasaccess, @denylogin
IF (@@fetch_status = -1)
BEGIN
  PRINT 'No login(s) found.'
  CLOSE login_curs
  DEALLOCATE login_curs
  RETURN -1
END
SET @tmpstr = '/* sp_help_revlogin script '
PRINT @tmpstr
SET @tmpstr = '** Generated ' + CONVERT (varchar, GETDATE()) + ' on ' + @@SERVERNAME + ' */'
PRINT @tmpstr
PRINT ''
WHILE (@@fetch_status <> -1)
BEGIN
  IF (@@fetch_status <> -2)
  BEGIN
    PRINT ''
    SET @tmpstr = '-- Login: ' + @name
    PRINT @tmpstr
    IF (@type IN ( 'G', 'U'))
    BEGIN -- NT authenticated account/group

      SET @tmpstr = 'CREATE LOGIN ' + QUOTENAME( @name ) + ' FROM WINDOWS WITH DEFAULT_DATABASE = [' + @defaultdb + ']'
    END
    ELSE BEGIN -- SQL Server authentication
        -- obtain password and sid
            SET @PWD_varbinary = CAST( LOGINPROPERTY( @name, 'PasswordHash' ) AS varbinary (256) )
        EXEC sp_hexadecimal @PWD_varbinary, @PWD_string OUT
        EXEC sp_hexadecimal @SID_varbinary,@SID_string OUT

        -- obtain password policy state
        SELECT @is_policy_checked = CASE is_policy_checked WHEN 1 THEN 'ON' WHEN 0 THEN 'OFF' ELSE NULL END FROM sys.sql_logins WHERE name = @name
        SELECT @is_expiration_checked = CASE is_expiration_checked WHEN 1 THEN 'ON' WHEN 0 THEN 'OFF' ELSE NULL END FROM sys.sql_logins WHERE name = @name

            SET @tmpstr = 'CREATE LOGIN ' + QUOTENAME( @name ) + ' WITH PASSWORD = ' + @PWD_string + ' HASHED, SID = ' + @SID_string + ', DEFAULT_DATABASE = [' + @defaultdb + ']'

        IF ( @is_policy_checked IS NOT NULL )
        BEGIN
          SET @tmpstr = @tmpstr + ', CHECK_POLICY = ' + @is_policy_checked
        END
        IF ( @is_expiration_checked IS NOT NULL )
        BEGIN
          SET @tmpstr = @tmpstr + ', CHECK_EXPIRATION = ' + @is_expiration_checked
        END
    END
    IF (@denylogin = 1)
    BEGIN -- login is denied access
      SET @tmpstr = @tmpstr + '; DENY CONNECT SQL TO ' + QUOTENAME( @name )
    END
    ELSE IF (@hasaccess = 0)
    BEGIN -- login exists but does not have access
      SET @tmpstr = @tmpstr + '; REVOKE CONNECT SQL TO ' + QUOTENAME( @name )
    END
    IF (@is_disabled = 1)
    BEGIN -- login is disabled
      SET @tmpstr = @tmpstr + '; ALTER LOGIN ' + QUOTENAME( @name ) + ' DISABLE'
    END
    PRINT @tmpstr
  END

  FETCH NEXT FROM login_curs INTO @SID_varbinary, @name, @type, @is_disabled, @defaultdb, @hasaccess, @denylogin
   END
CLOSE login_curs
DEALLOCATE login_curs
RETURN 0
GO

1
こんにちは。私は本番環境からユーザーの正確なコピーを作成しようとはしていません。DEVは実際には本番の正確なコピーではありません。ログインは同じパスワードを使用せず、devは同じドメイン内にありません。データベースユーザーを削除する必要がある他の制約がありますが、これは退屈しません。
Craig Efrein 2016年

実際、セキュリティの面では、本番環境からのパスワードを開発環境に適用しないでください。開発者に本番環境へのアクセスを提供しましたが、開発者がセキュリティ管理者の役割を持っていないことを願っています。

ユーザーがパスワードを持っている場合は、SQLユーザーを扱っているため、ドメインは関係ありません。また、パスワードはロックされないため、ユーザーが作成されたらすぐに変更できます。スクリプトは、実行する前に変更できます。不要なユーザーや不要なユーザーをすべて削除すれば、問題ありません。私の提案はあなたの問題の解決策ではありませんが、おそらくクローン後に、許可が処理されなくなるため、より少ないユーザーを削除する必要があります。最適ではないが、おそらく改善^^
Paolo

0

これに似たカーソルが機能するはずです

Use DB_name

declare @username varchar(50)
declare user_cursor cursor for select '['+name+']' from sys.database_principals where type in ('u', 's') and principal_id>4
open user_cursor
fetch next from user_cursor into @username
while (@@fetch_status=0)
begin
EXEC sp_dropuser @username
fetch next from user_cursor into @username
end
close user_cursor
deallocate user_cursor
go
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.