Zasfe
Zasfe's memory
Zasfe
전체 방문자
오늘
어제
  • 분류 전체보기 (143)
    • 이야기 (50)
    • 뜻알기 (1)
    • 컴퓨터 (91)

인기 글

최근 글

태그

  • IIS
  • Function
  • 캐슬
  • error
  • backup
  • 배치파일
  • Autoit
  • webknight
  • rsync
  • web.config
  • webknight2.2
  • 홈페이지 보안 강화도구
  • 저장프로시저
  • nslookup
  • Skin
  • Security
  • Microsoft Windows 2000 Scripting Guide
  • 보안
  • webhacking
  • 생각
  • MSSQL
  • webknight 2.2
  • MSSQL2K
  • VBScript
  • webknight2.1
  • Castle
  • cmd
  • ASP
  • webshell
  • IIS7
hELLO · Designed By 정상우.
Zasfe

Zasfe's memory

컴퓨터

EM 접속시간을 줄여보자 - sp_MSdbuseraccess 편

2007. 11. 15. 14:42
exec sp_MSdbuseraccess  N'db', N'%'
 
이 프로시져는 사용자가 접근가능한 모든 데이터베이스 목록을 확인 할때 사용을 합니다.
별로 쓰일 용도가 없을것 같지만, MSSQL 관리도구중 사용이 간단한 엔터프라이즈 메니져(Enterprise Manager. 이하 EM)로
접속을 하면 모든 데이터베이스 목록을 불러오는 부분에서 사용이 됩니다.

 
Microsoft 기술문서에 적혀있는 부분을 인용하면 다음과 같습니다.
기술문서 : KB88969
CAUSE
SQL Server Enterprise Manager calls the sp_MSdbuseraccess stored procedure to determine the accessibility of each database. The sp_MSdbuseraccess stored procedure then runs against every database in SQL Server to determine the user’s permission level in that database. For x number of databases, the sp_MSdbuseraccess stored procedure will run x + 1 times. These multiple calls can take several minutes to finish. A busy system can make the calls take longer to finish.

요약하면, "SQL 서버의 EM 으로 접속을 힐때 각각의 데이터베이스에 접속이 가능한지를 확인하는 저장프로시져라서, 모든 데이터베이스에 대해서 실행이 되기때문에 데이터베이스의 총갯수 + 1번 실행이 되어 로드율이 높을경우, 결과를 확인하는것이 오래걸린다 " 라는 이야기 입니다.
 
많은 분들이 극찬을 하시는 MSSQL 도움말 을 보면 다음과 같이 나옵니다. 

MSSQL 도움말

sp_MShasdbaccess
사용자가 액세스할 수 있는 모든 데이터베이스의 이름과 소유자를 나열합니다.

구문
sp_MShasdbacess

반환 코드 값
0(성공) 또는 1(실패)

사용 권한
public 역활에 대한 기본 권한을 실행합니다.
 
빈번하게 사용되는 EM 이고, 응답시간이 느려서 EM 이 시간제한을 무제한으로 변경하여도 시간제한으로 끊어지는 현상 이 발생하는 힘든 상황에서도 이 프로시져를 수정하여서 응답속도를 줄일수만 있다면 상당한 효과를 볼수 있습니다. 사용량이 많은 SQL 서버의 경우, EM 으로의 작업이 어렵습니다. 그나마 로컬(터미널/콘솔)에서의 작업은 조금 느려도 할만하겠지만, 외부에서 EM 을 이용한 작업은 정말 눈물을 머금게합니다.
이러한 저와 같은 EM 에 최적화(?)되어 있는 사용자들에게는 반드시 필요한 부분이고, 저또한 사용자의 하나로써 적극 권장합니다.

/*******************************************************************************/
/* exec sp_MSdbuseraccess 'perm', 'dbname' -- selecting priv bit from specified database                       */
/* exec sp_MSdbuseraccess 'db', 'dbname'   -- select databases, must change the database if dbname is specified */
/* exec sp_MSdbuseraccess 'init', 'dbname' -- noop                                                       */
/*******************************************************************************/
print N''
print N'Dropping sp_MSdbuseraccess'
print N''
go
if exists (select * from master.dbo.sysobjects where (OBJECTPROPERTY(id, N'IsProcedure') = 1 or OBJECTPROPERTY(id, N'IsExtendedProc') = 1) and name = N'sp_MSdbuseraccess')
	drop procedure sp_MSdbuseraccess
go

print N''
print N'Creating sp_MSdbuseraccess'
print N''
go
create proc sp_MSdbuseraccess
	@mode nvarchar(10) = N'perm', @qual nvarchar(128) = N'%'
as
   set deadlock_priority low
   
   create table #TmpDbUserProfile (
      dbid        int NOT NULL PRIMARY KEY,
      accessperms int NOT NULL
      )

   create table #TmpOut (
      name        nvarchar(132) NOT NULL,
      version     smallint,
      crdate      datetime,
      owner       nvarchar(132),
      dbid        smallint NOT NULL,
      status      int,
      category    int,
      status2     int,
      fulltext    int,
      )

   set nocount on

   declare @accessbit int
	if (lower(@mode) like N'perm%') begin
      /* verify */
      declare @id int, @stat int, @inval int
      select @id = dbid, @stat = status from master.dbo.sysdatabases where name = @qual
      if (@id is null) begin
         RAISERROR (15001, -1, -1, @qual)
         return 1
      end

      /* Can we access this database? */
      declare @single int
      select @single = DATABASEPROPERTY( @qual, N'issingleuser' )
/*      if ((@single <> 0) or ((@stat & SQLDMODBStat_Inaccessible) <> 0)) begin  */
      if ((@single <> 0) or
         (DATABASEPROPERTY(@qual, N'isdetached') <> 0) or
         (DATABASEPROPERTY(@qual, N'isshutdown') <> 0) or
         (DATABASEPROPERTY(@qual, N'issuspect') <> 0) or
         (DATABASEPROPERTY(@qual, N'isoffline') <> 0) or
         (DATABASEPROPERTY(@qual, N'isinload') <> 0) or
         (DATABASEPROPERTY(@qual, N'isinrecovery') <> 0) or
         (DATABASEPROPERTY(@qual, N'isnotrecovered') <> 0)) begin
         select @inval = 0x80000000
         select @inval
         return 0
      end
      select @accessbit = has_dbaccess(@qual)
      if ( @accessbit <> 1) begin
         select @inval = 0x40000000
         select @inval
         return 0
      end

      /** We can access this database, and we must locate the specified database to get the priv bit **/
      declare @dbTempname nvarchar(258)
      declare @tempindex int
      SELECT @dbTempname = REPLACE(@qual, N']', N']]')
      exec (N'[' + @dbTempname + N']' + N'..sp_MSdbuserpriv ')
      return 0
   end

   /* If 'db', we want to know what kind of access we have to the specified databases */
   /* If we are not in the master database, we are selecting a single database, and we want to correct role bit to save round trip */
   if (lower(@mode) like N'db%') begin
      /*  Make sure that you are in either the master database or the current database, so that you do not affect other databases. */
      declare @dbrole int
      select @dbrole = 0x0000

      if (db_id() <> 1)
         select @qual = db_name()

      /* If dbname contains a single quotation mark ('), double the single quotation mark for the cursor because the cursor statement is inside two single quotation marks ('').  */
      declare @qual2 nvarchar(517)
      SELECT @qual2 = REPLACE(@qual, N'''', N'''''')

      /* The preprocessor will not replace information within quotation marks, and we have to use the str function. */
      declare @invalidlogin nvarchar(12)
      select @invalidlogin = ltrim(str(convert(int, 0x40000000), 11))
      declare @inaccessible nvarchar(12)
      select @inaccessible = ltrim(str(convert(int, 0x80000000), 11))

      /* We cannot 'use' a database that has a version that is less than the minimum version. */
      /* The SQL Server 6.0 version minimum is 406; the SQL Server 6.5 version minimum is 408.  The SQL Server 7.0  version is 408; however,  it might change later. */
      declare @mindbver smallint
      if (@@microsoftversion >= 0x07000000)
         select @mindbver = 408
      else
         select @mindbver = 406

      /* Select all matching databases -- we want an entry even for the inaccessible databases. */
      declare @dbid smallint, @dbidstr nvarchar(12), @dbstat int, @dbname nvarchar(258), @dbver smallint
      declare @dbbits int, @dbbitstr nvarchar(12)

      /* !!! If the database name contains a left bracket ([), the LIKE operator cannot find the name because the LIKE operator treats a left bracket  as a wildcard character. */
      /* !!! If the @qual2 variable is a percent sign (%), the equal (=) operator does not work. */
      declare @temp int
      select @tempindex = charindex(N'[', @qual2)
      if (@tempindex <> 0)
         exec(N'declare hCdbs cursor global for select name, dbid, status, version from master.dbo.sysdatabases where name = N''' + @qual2 + N'''')
      else
         exec(N'declare hCdbs cursor global for select name, dbid, status, version from master.dbo.sysdatabases where name like N''' + @qual2 + N'''')

      open hCdbs

      /* Loop for each database; if the database can be accessed, recursively call ourselves to add the database. */
      fetch hCdbs into @dbname, @dbid, @dbstat, @dbver
      while (@@fetch_status >= 0) begin
         /* The preprocessor will not replace information within quotation marks, and we have to use the str function. */
         select @dbidstr = ltrim(str(convert(int, @dbid)))

         /* If the database is a single user database and there is an entry for it in sysprocesses that is not us, we cannot use it. */
         declare @single_lockedout int
         select @single_lockedout = DATABASEPROPERTY( @dbname, N'issingleuser' )
         if (@single_lockedout <> 0)
            select @single_lockedout = 0 where not exists
               (select * from master.dbo.sysprocesses p where dbid = @dbid and p.spid <> @@spid)

         /* First, see if the database can be accessed (not in load, not in recovery, not offline, not in single-use with another user besides us, and so on.) */
/*         if ((@single_lockedout <> 0) or ((@dbstat & SQLDMODBStat_Inaccessible) <> 0) or (@dbver < @mindbver)) begin   */
         if ((@single_lockedout <> 0) or
            (@dbver < @mindbver) or
            (DATABASEPROPERTY(@dbname, N'isdetached') <> 0) or
            (DATABASEPROPERTY(@dbname, N'isshutdown') <> 0) or
            (DATABASEPROPERTY(@dbname, N'issuspect') <> 0) or
            (DATABASEPROPERTY(@dbname, N'isoffline') <> 0) or
            (DATABASEPROPERTY(@dbname, N'isinload') <> 0) or
            (DATABASEPROPERTY(@dbname, N'isinrecovery') <> 0) or
            (DATABASEPROPERTY(@dbname, N'isnotrecovered') <> 0) ) begin
            /* Inaccessible, but we can set dbo if we are an sa or if the suser_id function is the database owner sid. */
            exec (N'insert #TmpDbUserProfile values (' + @dbidstr + N', ' + @inaccessible + N')')
            end
         else begin
            /* Determine whether the current user has access to the database. */
            select @accessbit = has_dbaccess(@dbname)
            if ( @accessbit <> 1) begin
               exec (N'insert #TmpDbUserProfile values (' + @dbidstr + N', ' + @invalidlogin + N')')
               end
            else begin
               /* The current user does have access to this database, and we are not trying to obtain priv at this point. */
               select @dbbits = 0x03ff
               select @dbbitstr = ltrim(convert(nvarchar(12), @dbbits))
               exec (N'insert #TmpDbUserProfile values (' + @dbidstr + N', ' + @dbbitstr + N')')
               end
            end

         fetch hCdbs into @dbname, @dbid, @dbstat, @dbver
      end /* while FETCH_SUCCESS */
      close hCdbs
      deallocate hCdbs

      /* Select sysdatabases information in the temp table first to avoid a deadlock in the restore process. */
      if (@tempindex <> 0)
         insert #TmpOut
         select o.name, o.version, o.crdate, suser_sname(o.sid), o.dbid, o.status, o.category, o.status2, DatabaseProperty(o.name, N'isfulltextenabled')
            from master.dbo.sysdatabases o where o.name = @qual
      else
         insert #TmpOut
         select o.name, o.version, o.crdate, suser_sname(o.sid), o.dbid, o.status, o.category, o.status2, DatabaseProperty(o.name, N'isfulltextenabled')
            from master.dbo.sysdatabases o where o.name like @qual

      /* 1. If on all databases,  dbrole is dummy, we must obtain it later. */
      /* 2. Do not double the single quotation mark (') characters in the database name. */
      /* 3. To speed up the connection, the accessperms column only indicates whether the user can access the database. The column does not contain */
      /*    permission information. We will retrieve the permission information by using the sp_MSdbuserpriv stored procedure when we need that information. */
      /* !!! If the name contains a left bracket ('[), the LIKE operator cannot find it because the LIKE operator treats a left bracket  as a wildcard character. */
      /* !!! If the @qual2 variable is a percent sign (%), the equal (=) operator does not work. */
      if (@tempindex <> 0)
         select o.name, o.version, o.crdate, o.owner, o.dbid, lSize = 0, NonDbo = 0, Status = o.status, spaceavail = 0,
            LogOnSepDev = 1, o.category, t.accessperms, @dbrole, o.fulltext, o.status2,
            collation = convert(sysname, databasepropertyex(o.name, N'collation'))
            from #TmpOut o left outer join #TmpDbUserProfile t on t.dbid = o.dbid 
	where o.name = @qual and (t.accessperms & 0x40000000 = 0) order by o.name
      else
         select o.name, o.version, o.crdate, o.owner, o.dbid, lSize = 0, NonDbo = 0, Status = o.status, spaceavail = 0,
            LogOnSepDev = 1, o.category, t.accessperms, @dbrole, o.fulltext, o.status2,
            collation = convert(sysname, databasepropertyex(o.name, N'collation'))
            from #TmpOut o left outer join #TmpDbUserProfile t on t.dbid = o.dbid 
		where o.name like @qual and (t.accessperms & 0x40000000 = 0) order by o.name

      DROP TABLE #TmpDbUserProfile
      DROP TABLE #TmpOut
      return 0
   end
go
/* End sp_MSdbuseraccess */

exec sp_MS_marksystemobject sp_MSdbuseraccess
go
grant execute on sp_MSdbuseraccess to public
go


 사용방법은 간단합니다.
위의 쿼리를 쿼리분석기로 실행만 하면 바로 적용이 됩니다.

1. EM 에 접속해서 현재의 상황을 확인합니다. 2. 위의 코드를 쿼리분석기에 입력합니다.
3. 쿼리를 실행합니다. 4. EM 으로 접속을 해봅니다.


위 테스트는 일반 사용자계정을 대상으로 진행을 하였습니다. 관리자(SystemAdmin)계정은 모든 데이터베이스에 접근권한이 있기때문에 적용을 하여도 차이가 없습니다.

출처 : MS 기술문서 KB889696

'컴퓨터' 카테고리의 다른 글

감사정책에 대해서 알고 계십니까?  (2) 2008.01.02
Flash Uploader 업로드 불가 문제  (3) 2007.11.16
Scripting.Dictionary 사용하기..1  (1) 2007.11.14
LMHOSTS 사용하기  (0) 2007.10.25
지정한 트랜잭션 코디네이터에 새 트랜잭션을 기록할 수 없습니다  (0) 2007.10.25
    '컴퓨터' 카테고리의 다른 글
    • 감사정책에 대해서 알고 계십니까?
    • Flash Uploader 업로드 불가 문제
    • Scripting.Dictionary 사용하기..1
    • LMHOSTS 사용하기
    Zasfe
    Zasfe
    느낌. 기억

    티스토리툴바