반응형
반응형

SELECT *

FROM sys database_principals AS pr

INNER JOIN sys.database_permissions AS pe

ON pe.grantee_principal_id = pr.principal_id

INNER JOIN sys.objects AS o

ON pe . major_id = o . object_id

INNER JOIN sys.schemas AS s

ON o.schema_id = s.schema_id

ORDER BY pr.name



SELECT

pr.name

, pe.type

, pe.permission_name

, pe.state_desc

, o.name

, o.object_id

FROM sys.database_principals AS pr

INNER JOIN sys.database_permissions AS pe ON pe.grantee_principal_id = pr.principal_id

INNER JOIN sys.objects AS o ON pe.major_id = o.object_id

INNER JOIN sys.schemas AS s ON o.schema_id = s.schema_id



SELECT

pr.name

, pe.type

, pe.permission_name

, pe.state_desc

, o.name as object_name

, o.object_id

FROM sys.database_principals AS pr

INNER JOIN sys.database_permissions AS pe ON pe.grantee_principal_id = pr.principal_id

INNER JOIN sys.objects AS o ON pe.major_id = o.object_id

INNER JOIN sys.schemas AS s ON o.schema_id = s.schema_id

ORDER BY pr.name, pe.permission_name, o.name

반응형
반응형

예제 이므로 밑에 데이터를 선언해서 사용했지만. 원래는 CS 단에서 노란부분 처럼 XML로 만들어서 던졌어요.

EXEC USP_SWS_SAVE_INSERT

'<LIST><ROW><SEQ>1</SEQ><MUID>AAA</MUID></ROW><ROW><SEQ>2</SEQ><MUID>BBB</MUID></ROW></LIST>',

null, null

ALTER PROCEDURE USP_SAVE_INSERT
@P_XMLDATA VARCHAR(MAX), -- DETAIL XML DATA
@P_RTN_CODE VARCHAR(4) OUTPUT,
@P_RTN_MSG VARCHAR(2000) OUTPUT


예제

DECLARE @XML_DATA VARCHAR(MAX) -- XML DATA
DECLARE @XML_RTN INT -- XML 핸들 번호 RETURN
SET @XML_DATA = '<LIST><ROW><SEQ>1</SEQ><ID>AAA</ID></ROW><ROW><SEQ>2</SEQ><ID>BBB</ID></ROW></LIST>'


-- XML을 담을 임시 테이블을 선언해줍미다.
CREATE TABLE #TEMP_XML
(
SEQ INT,
ID VARCHAR(20)
)

-- XML 문서의 새로 생성된 내부 표현에 액세스하는 데 사용할 수 있는 핸들을 받습니다.
EXEC sp_xml_preparedocument @XML_RTN OUTPUT, @XML_DATA

INSERT INTO #TEMP_XML
SELECT *
FROM OPENXML (@XML_RTN, '/LIST/ROW', 3)
WITH
(
SEQ INT,
ID VARCHAR(20)
)

-- 아까 생성된 XML 핸들을 제거합니다.
EXEC sp_xml_removedocument @XML_RTN

SELECT * FROM #TEMP_XML




이제 임시 테이블에 넣은 데이터를 기준으로 아래에 기능을 더 추가하면 되겠지요?

아래는 위의 예제를 직접 실행해본겁미다~.



오후.. 5시쯤...혼자 열라 해메던 문제......

XML 데이타 필드 안에 한글이 들어있을 경우 위와 같이 했다간 에러가 발생하더군요.

XML 을 받는 변수의 속성을 VARCHAR(MAX)가 아닌 NVARCHAR(MAX) 하여야 됩니다.

아무래도 NVARCHAR는 유니코드/다국어지원이 되기 때문인거 같은데.. 걍 VARCHAR에도 한글은 들어가 지는뎁...

반응형
반응형

1. xp_cmdshell enable

USE master

EXEC sp_configure 'show advanced options', 1;
go
RECONFIGURE;
go

EXEC sp_configure 'xp_cmdshell', 1;
go
RECONFIGURE;
go



2. xp_cmdshell disable

USE master

EXEC sp_configure 'show advanced options', 1;
go
RECONFIGURE;
go

EXEC sp_configure 'xp_cmdshell', 0;
go
RECONFIGURE;
go 

추가) 2010.03.08

MSSQL2005부터는 위에서처럼 기능을 사용하도록 허용하더라도 로그인한 계정에 따라 실행이 안될 수 있다.
그것은 로그인한 계정에 sysadmin 권한이 없어 실행할 수 없는 것인데, 해당 계정에 sysadmin권한을 부여하기
힘든 상황이라면 아래와 같이 해결이 가능하다.

CREATE PROCEDURE UP_TEST_PROC
WITH EXECUTE AS 'dbo'
AS

해당 SP를 dbo권한으로 실행하도록 SP를 생성하는 것이다.
반응형
반응형


자주가는 블로그에 재미있는 꺼리가 올라와서 간단히 정리해봤습니다.
 
 
다음과 같은 형태의 데이터를
    
다음과 같이 같은 id 그룹별로 쉼표(,)로 붙여서 출력하는 문제입니다.
    
 
조건은 
  a. 커서를 쓰면 안되며,
  b. 임시 테이블 또는 테이블 변수와 같은 것 사용 없이 쿼리 한 방으로 결과 뽑기
  c. 대신 SQL 2005의 CTE 등은 이용해도 되기~ 입니다.
 
아래의 여러 고수들이 제시한 방법들을 보시기 전에 먼저 한 번 고민해 보시길 바랍니다.^^
 
 
테스트용 데이터 생성하기
 

USE TEMPDB

GO

 

 

IF EXISTS (SELECT * FROM SYS.TABLES WHERE NAME = 't1' AND type = 'U')

        DROP TABLE t1

Go

 

 

CREATE TABLE t1 (id INT, NAME VARCHAR(MAX))

INSERT t1 values (1,'Jamie')

INSERT t1 values (1,'Joe')

INSERT t1 values (1,'John')

INSERT t1 values (2,'Sai')

INSERT t1 values (2,'Sam')

INSERT t1 values (3,'Roger')

INSERT t1 values (4,'Walter')

INSERT t1 values (4,'Teddy')

GO

 

SELECT * FROM T1

GO

/*

id      NAME

-----------------

1       Jamie

1       Joe

1       John

2       Sai

2       Sam

3       Roger

4       Walter

4       Teddy

*/

 
 
 
 
[방법 1]

--------------------------------------------------------------------------------------------

-- Nick Barclay

--------------------------------------------------------------------------------------------

WITH ConcatNamesCTE (id, [NAME], rn)

AS

(

        SELECT id, [NAME], rn

        FROM

        (

               SELECT id, [NAME],

                       row_number() OVER(PARTITION BY id ORDER BY id) AS rn

               FROM t1

        ) a

        WHERE rn = 1

        UNION ALL

        SELECT b.id, cn.[NAME] + ',' + b.[NAME], b.rn

        FROM

        (

               SELECT id, [NAME],

                       row_number() OVER(PARTITION BY id ORDER BY id) AS rn

               FROM t1

        ) b

        INNER JOIN ConcatNamesCTE cn ON cn.id = b.id AND cn.rn + 1 = b.rn

)

SELECT d.id, d.[NAME]

FROM

(

        SELECT MAX(rn) AS rn, id

        FROM ConcatNamesCTE

        GROUP BY id

) c

INNER JOIN ConcatNamesCTE d ON d.id = c.id AND d.rn = c.rn

ORDER BY id

GO

 
 
[방법 2]

--------------------------------------------------------------------------------------------

--Adrian Downes

--------------------------------------------------------------------------------------------

SELECT res.id, MAX(res.[NAME]) AS [NAME]

FROM

(

        SELECT c.id,

        CASE

               WHEN PATINDEX('%' + d.[NAME] + '%', c.[NAME]) = 0 AND c.id = d.id

                       THEN c.[NAME] + ', ' + d.[NAME]

               ELSE c.[NAME]

        END AS [NAME]

        FROM

        (

               SELECT a.id, MIN(a.[NAME]) AS [NAME]

               FROM

               (

                       SELECT y.id,

                       CASE

                               WHEN PATINDEX('%' + z.[NAME] + '%', y.[NAME]) = 0 AND y.id = z.id

                              THEN y.[NAME] + ', ' + z.[NAME]

                              END AS [NAME]

                       FROM t1 y

                       INNER JOIN t1 z ON y.id = z.id

               ) a

               GROUP BY a.id

        ) c

        INNER JOIN t1 d ON c.id = d.id

) res

GROUP BY res.id

GO

 
 
[방법 3]

--------------------------------------------------------------------------------------------

--Jamie Hunter

--------------------------------------------------------------------------------------------

SELECT

        DISTINCT

        id

        , STUFF(

               (SELECT ',' + name AS [text()] FROM t1 b WHERE b.id = a.id FOR XML PATH(''))

               ,  1,  1,  ''

               ) AS name_csv

FROM t1 a

ORDER BY 1

GO

 
 
[방법 4]

--------------------------------------------------------------------------------------------

--Rick R

--------------------------------------------------------------------------------------------

WITH t2 AS (

        SELECT id, MIN(name) name

        FROM t1 GROUP BY id

        UNION ALL

        SELECT a.id, a.[NAME] + ',' + b.[NAME] AS name

        FROM t1 a JOIN t2 b ON a.id = b.id AND a.[NAME] > b.[NAME]

)

SELECT id, MAX(name) FROM t2

GROUP BY id

GO

 
 
 
개인적으로는 방법 3이 좋게 보이네요..^^ 여러분은 어떤 방법이 제일 좋으신지요..
더 좋은 방법 있으면 리플 달아주세요.
반응형
반응형
http://support.microsoft.com/kb/240872

SQL Server를 실행하는 서버에서 SQL Server를 실행하는 다른 서버로 데이터베이스를 이동할 때 master 데이터베이스에 있는 로그인과 사용자 데이터베이스에 있는 사용자의 SID(보안 ID)가 일치하지 않을 수 있습니다. 기본적으로 SQL Server 7.0, SQL Server 2000 및 SQL Server 2005에서는 이러한 일치하지 않는 사용자를 매핑하는 sp_change_users_login 시스템 저장 프로시저를 제공합니다. 그러나 sp_change_users_login 저장 프로시저는 표준 SQL Server 로그인을 매핑하는 데만 사용할 수 있으며 한 번에 한 사용자에 대해서만 매핑을 수행해야 합니다. sp_change_users_login 저장 프로시저에 대한 자세한 내용은 SQL Server 7.0, SQL Server 2000 및 SQL Server 2005 온라인 설명서의 "sp_change_users_login" 항목을 참조하십시오.

SQL Server 7.0 이상 버전에서는 SID를 사용하여 master 데이터베이스의 로그인과 사용자 데이터베이스의 사용자 간의 매핑을 유지할 수 있습니다. 이러한 매핑은 사용자 데이터베이스에서 로그인에 대한 올바른 사용 권한을 유지하는 데 필요합니다. 이러한 매핑이 손실되면 로그인에서 다음과 같은 사용 권한 문제가 발생합니다. 단, 이에 국한되지는 않습니다.
  • 새 서버에 SQL Server 로그인이 존재하지 않는 경우 사용자가 로그온하려고 하면 다음과 같은 오류 메시지가 나타날 수 있습니다.
    서버: 메시지 18456, 수준 16, 상태 1
    사용자 '%ls'이(가) 로그인하지 못했습니다.
  • 새 서버에 SQL Server 로그인이 있지만 master 데이터베이스의 SID가 사용자 데이터베이스의 SID와 다른 경우 사용자가 SQL Server에 성공적으로 로그온할 수는 있지만 해당 데이터베이스에 액세스하려고 하면 다음과 같은 오류 메시지가 나타날 수 있습니다.
    서버: 메시지 916, 수준 14, 상태 1, 줄 1
    서버 사용자 '%.*ls'은(는) '%.*ls' 데이터베이스에서 유효한 사용자가 아닙니다.
    참고 SQL Server 2005에서는 다음과 같은 오류 메시지가 나타날 수 있습니다.

    서버 사용자 '%1!s!'은(는) 데이터베이스 '%2!s!'에 유효한 사용자가 아닙니다. 먼저 데이터베이스에 사용자 계정을 추가하십시오.
SQL Server 7.0 보안 모델에 대한 자세한 내용은 "Microsoft SQL Server 7.0 보안" 백서를 참조하십시오. 이 백서를 보려면 아래의 Microsoft 웹 사이트를 방문하십시오.
http://msdn2.microsoft.com/en-us/library/Aa226173(SQL.70).aspx (http://msdn2.microsoft.com/en-us/library/Aa226173(SQL.70).aspx) (영문)
SQL Server 2000 보안 모델에 대한 자세한 내용은 Microsoft 기술 자료의 다음 문서를 참조하십시오.
322712  (http://support.microsoft.com/kb/322712/ ) Microsoft SQL Server 2000 SP3 보안 기능 및 최상의 방법
 

제한 사항

  • sysusers 테이블에 개체를 소유한 컴퓨터 이름 또는 도메인 이름 접두사가 없는 사용자가 있는 경우 이러한 개체가 응용 프로그램에서 username.objectname과 같은 형식의 두 부분으로 된 이름을 사용하여 참조되면 sp_sidmap 저장 프로시저가 sysxlogins 테이블에 표시된 것처럼 이러한 사용자의 이름을 컴퓨터 이름 또는 도메인 이름의 접두사로 바꾸기 때문에 응용 프로그램이 중단될 수 있습니다. 이 문제를 해결하려면 sp_sidmap 저장 프로시저가 완료된 후 sysusers 테이블에서 영향을 받는 사용자의 이름을 이전 이름으로 바꾸거나 주 지원 공급자에게 문의하십시오.
  • 이 문서에서는 별칭을 다루지 않으므로 별칭을 직접 관리해야 합니다.
  • 새 SQL Server 서버에 표준 SQL Server 로그인이 없는 경우 암호가 NULL인 로그인을 추가할 수 있습니다. 이러한 로그인의 암호를 적절하게 변경해야 할 수도 있습니다.
  • 사용자 데이터베이스에서 sysxlogins 테이블에 표시된 것과 다른 이름으로 사용자를 만든 경우에는 해당 사용자의 적절한 로그인을 알 수 없습니다. 따라서 sp_sidmap 저장 프로시저를 실행하기 전에 다음을 수행합니다.
    1. 이 사용자가 소유한 모든 개체를 준비 데이터베이스로 전송합니다.
    2. 사용자를 삭제하고, 해당 사용자를 올바른 이름으로 추가한 다음 이 사용자에 대한 개체를 모두 다시 전송합니다.
  • 사용자에게 적절한 로그인이 없고 로컬 컴퓨터 이름 또는 도메인 이름의 접두사도 없는 경우 이 사용자에 대해 오류 메시지가 나타납니다. 이 오류 메시지는 먼저 Windows 수준에서 사용자를 추가한 다음 SQL Server에 로그인으로 추가해야 한다는 것을 나타냅니다. 메시지에 나타난 대로 수행한 후에는 sp_sidmap 저장 프로시저를 다시 실행해야 합니다.
  • 사용자에게 도메인 이름이나 로컬 Windows 서버 이름의 접두사가 있지만 sysxlogins 테이블에 적절한 로그인이 없으면 저장 프로시저가 이 사용자를 SQL Server에 새 로그인으로 추가하려고 시도합니다. Windows 사용자가 존재하지 않으면 결과 창에 메시지를 출력하고 먼저 Windows 사용자를 추가한 다음에 로그인을 수동으로 만듭니다.
  • 한 사용자에 대해 sysusers 테이블에 둘 이상의 로그인이 있으면 동일한 사용자 이름을 갖는 로그인이 모두 나열된 메시지가 결과 파일에 출력됩니다. 이때 사용자가 하나의 로그인에만 연결되도록 직접 조정해야 합니다.

      sysusers 테이블에 "johndoe"라는 사용자가 있고 sysxlogins 테이블에 "Test\johndoe" 및 "Test2\johndoe"라는 이름의 로그인이 있는 경우 저장 프로시저를 실행하면 로그인이 둘 이상인 사용자가 있으므로 시스템 관리자가 두 로그인 중 하나를 선택해야 한다는 메시지가 나타납니다. 이러한 경우에 이 문서에 설명되어 있는 두 번째 저장 프로시저인 sp_prefix_sysusersname을 실행해야 합니다. 이러한 상황에 대한 자세한 내용은 Readme.txt 파일에도 설명되어 있습니다.

표준 로그인 및 통합 로그인 매핑

SQL Server를 실행하는 서버에서 SQL Server를 실행하는 다른 서버로 데이터베이스를 이동한 후에는 사용자 작업을 최소화할 수 있도록 다음 단계를 수행하십시오.
  1. master 데이터베이스의 sysxlogins 테이블에 해당 데이터베이스의 sysusers 테이블에 있는 각 사용자에 대한 로그인이 하나만 있는지 확인합니다.

    참고 표준 SQL Server 로그인을 추가하려면 SQL Server 온라인 설명서의 "sp_addlogin" 항목을 참조하십시오. 통합 SQL Server 로그인을 추가하려면 SQL Server 온라인 설명서의 "sp_grantlogin" 항목을 참조하십시오.
  2. MapSids.exe 파일을 다운로드한 다음 Sp_sidmap.sql 및 Readme.txt 파일을 추출합니다.
  3. SQL Server를 실행하는 서버에 시스템 관리자로 로그인한 다음 사용자 데이터베이스에서 Sp_sidmap.sql 파일을 실행합니다. Sp_sidmap.sql 파일을 실행하면 sp_sidmap 저장 프로시저와 sp_prefix_sysusersname 저장 프로시저가 생성됩니다.
  4. 저장 프로시저를 실행하는 사용자 이외의 다른 사용자가 데이터베이스에 액세스하지 않는지 확인합니다.
  5. 쿼리 분석기에 결과가 표 형식이 아니라 텍스트 형식으로 표시되는지 확인합니다. 결과를 텍스트 형식으로 표시하려면 CTRL^T를 누르거나 쿼리를 누른 다음 텍스트로 결과 표시를 누릅니다. 결과와 정보 메시지를 하나의 창에서 볼 수 있고 출력을 텍스트 파일로 저장할 수 있어야 합니다. 나중에 일부 매핑을 확인하려면 이 파일이 필요할 수도 있습니다.
  6. 매개 변수가 올바르게 전달되었는지 확인할 방법이 없으므로 sp_sidmap 저장 프로시저로 올바르게 전달되도록 해야 합니다.
    EXEC sp_SidMap @old_domain = old_domain_name,
    @new_domain = new_domain_name,
    @old_server = old_server_name,
    @new_server = new_server_name
    이전 도메인 이름과 새 도메인 이름 및 서버 이름의 값을 적절하게 변경합니다.
  7. 결과를 파일에 저장하고 Readme.txt 파일에 표시된 지침에 따릅니다.

    참고 위의 저장 프로시저를 실행하면 데이터베이스에서 sysusers 테이블만 변경됩니다. 시작할 때의 상태로 되돌리려면 백업을 사용하여 데이터베이스를 복원하거나 데이터베이스를 다시 연결합니다.
반응형
반응형


-- 숫차형 IP주소를 IP Address로 변환하는 함수 작성
CREATE FUNCTION dbo.Func_ConvertNumlp(@ip BIGINT)
RETURNS VARCHAR(15)
AS
BEGIN
    DECLARE @delimiter VARCHAR(1)
        ,@SUBNET_MASK INT
        ,@idx  INT
        ,@ipSeg  BIGINT
        ,@segments VARCHAR(15)
       
    SET @delimiter = '.'
    SET @SUBNET_MASK = 256
    SET @idx = 1
    SET @ipSeg = 0
    SET @segments = ''
    
    WHILE (@idx <= 4)
    BEGIN
        SET @ipSeg = CAST(@ip / POWER(@SUBNET_MASK, 4-@idx) AS BIGINT)
        SET @ip = @ip - CAST(@ipSeg * POWER(@SUBNET_MASK, 4-@idx) AS BIGINT)
        IF (@idx = 1)
            SET @segments = CAST(@ipSeg AS VARCHAR)
        ELSE
            SET @segments = @segments + @delimiter + CAST(@ipSeg AS VARCHAR)
            SET @idx = @idx + 1 
    END
    
    RETURN @segments  
END
GO

--IP Address를 숫자형 IP주소로 변환하는 함수 작성
CREATE FUNCTION dbo.Func_ConvertIpNum(@ip NVARCHAR(15))
RETURNS BIGINT
AS
BEGIN
    DECLARE @delimiter NVARCHAR(1)
        ,@SUBNET_MASK   INT
        ,@idx INT
        ,@ipNum FLOAT
        ,@textXML XML
       
    DECLARE @segments TABLE (id  INT, col   INT)
    
    SET @delimiter = '.'
    SET @SUBNET_MASK = 256
    SET @idx = 4
    SET @ipNum = 0
    SELECT @textXML = CAST('<col>' + REPLACE(@ip, @delimiter, '</col><col>') + '</col>' AS XML);
    
    INSERT INTO @segments (id, col)
    SELECT ROW_NUMBER() OVER (ORDER BY col) AS id
        , T.col.value('.', 'int') AS col
    FROM @textXML.nodes('/col') T(col)
    ORDER BY id DESC
    
    SELECT @ipNum = @ipNum + (CAST((col % @SUBNET_MASK) AS FLOAT) * POWER(@SUBNET_MASK, @idx-id))
    FROM @segments
    
    RETURN CAST(@ipNum AS BIGINT)      
END
GO

CREATE TABLE ipcountry
(
    startip VARCHAR(15),
    endip VARCHAR(15),
    startipnum   BIGINT,
    endipnum    BIGINT,
    country   VARCHAR(2),
    city VARCHAR(20)
);
GO

INSERT INTO ipcountry VALUES ('192.168.0.1', '192.168.10.23', 3232235521, 3232238103, 'KR', 'Seoul');
INSERT INTO ipcountry VALUES ('192.168.10.24', '192.168.101.255', 3232238104, 3232261631, 'US', 'New York');
INSERT INTO ipcountry VALUES ('192.168.102.1', '192.168.102.10', 3232261633, 3232261642, 'CN', 'Beijing');
GO

SELECT * FROM ipCountry
WHERE dbo.Func_ConvertlpNum('192.168.0.40') BETWEEN startipnum AND endipnum


-출처 : 태기의 데이터베이스 때려잡기

반응형
반응형

/*
LPAD 함수
SELECT dbo.Func_lpad(@str, @nofchars, @fillchar)
@str = 입력 문자열
@nofchars = 반환할 문자열의 전체길이
@fillchar = 덧붙일 문자
*/
CREATE FUNCTION dbo.Func_lpad (@str AS VARCHAR(8000), @nofchars AS INT, @fillchar AS VARCHAR(8000) = '')
    RETURNS VARCHAR(2000)
AS
    BEGIN
        RETURN
            CASE WHEN LEN(@str) >= @nofchars THEN SUBSTRING(@str, 1, @nofchars)
            ELSE SUBSTRING(REPLICATE(@fillchar, @nofchars), 1, @nofchars - LEN(@str)) + @str
            END 
    END
GO

출처 : 태기의 데이터베이스 때려잡기
반응형
반응형

CREATE PROCEDURE dbo.usr_Disable_Foreign_Keys
    @disable BIT = 1
AS
    DECLARE
        @sql  VARCHAR(500),
        @schemaName VARCHAR(128),
        @tableName VARCHAR(128),
        @foreignKeyName  VARCHAR(128)
    DECLARE foreignKeyCursor CURSOR FOR
    SELECT
        ref.constraint_name AS FK_Name,
        fk.table_schema AS FK_Schema,
        fk.table_name AS FK_Table
    FROM
        INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS ref
        INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS fk
            ON ref.constraint_name = fk.constraint_name  
    ORDER BY 
        fk.table_schema,
        fk.table_name,
        ref.constraint_name
    
    OPEN foreignKeyCursor
    
    FETCH NEXT FROM foreignKeyCursor
    INTO @foreignKeyName, @schemaName, @tableName
    WHILE (@@FETCH_STATUS = 0)
        BEGIN
            IF @disable = 1
                SET @sql = 'ALTER TABLE ['
                    + @schemaName + '].['
                    + @tableName + '] NOCHECK CONSTRAINT ['
                    + @foreignKeyName + ']'  
            ELSE
                SET @sql = 'ALTER TABLE ['
                    + @schemaName + '].['
                    + @tableName + '] CHECK CONSTRAINT ['
                    + @foreignKeyName + ']'
        
            PRINT 'Executing Statement - ' + @sql
           
            EXECUTE(@sql)
            
            FETCH NEXT FROM foreignKeyCursor
            INTO @foreignKeyName, @schemaName, @tableName
        END

    CLOSE foreignKeyCursor
    DEALLOCATE foreignKeyCursor
    
GO 

EXEC dbo.usr_Disable_Foreign_Keys 0   --0: 활성화
EXEC dbo.usr_Disable_Foreign_Keys 1   --1: 비활성화

출저 : 태기의 데이터베이스 때려잡기
반응형

'연구개발 > SQL2008' 카테고리의 다른 글

IP 주소 범위 체크  (0) 2010.05.20
오라클의 LPAD 함수  (0) 2010.05.20
DBCC 명령문  (0) 2010.05.06
추척파일을 테이블로 변환하기  (0) 2010.05.04
로그파일이 없을 시 복원  (0) 2010.05.03
반응형

DBCC TRACEON / TRACEOFF / TRACESTATUS

DBCC SQLPERF(LOGSPACE / IOSTATS / LRUSTATS / NETSTATS)

DBCC OPENTRAN

DBCC CHECKDB

DBCC INPUTBUFFER / OUTPUTBUFFER

DBCC PROCCACHE

DBCC SHOWCONFIG

DBCC SHOW_STATISTICS

DBCC USEROPTIONS

DBCC dllname(FREE)

DBCC INDEXDEFRAG

DBCC PINTABLE / UNPINTABLE

DBCC DROPCLEANBUFFERS

DBCC FREEPROCCACHE
반응형
반응형

SELECT IDENTITY(INT, 1, 1) AS RowNumber, *
INTO #temp_trc
FROM ::fn_trace_gettable('D:\Temp\test.trc', DEFAULT)


SELECT TOP 10 RowNumber, CONVERT(VARCHAR(10), TextData) AS Text, Duration
FROM #temp_trc
반응형

'연구개발 > SQL2008' 카테고리의 다른 글

데이터베이스 내의 모든 외래키 비활성화/활성화  (0) 2010.05.20
DBCC 명령문  (0) 2010.05.06
로그파일이 없을 시 복원  (0) 2010.05.03
데이터베이스 복원 / 온라인  (0) 2010.05.03
노출 영역 구성  (0) 2010.05.02
반응형

CREATE DATABASE [testDB] ON PRIMARY
(
NAME = 'testDB'
,FILENAME = 'D:\testDB.mdf'
)
FOR ATTACH ATTACH_REBUILD_LOG




로그파일이 있을 때 복원

CREATE DATABASE [testDB] ON PRIMARY
(
    NAME = 'testDB'
    ,FILENAME = 'D:\데이터파일\testDB.mdf'
)
LOG ON
(
    NAME = N'testDB_log'
    ,FILENAME = 'D:\로그파일\testDB_log.ldf'
)
FOR ATTACH


SQL 2000
EXEC sp_detach_db 'TestBilingMember', 'true'

EXEX sp_attach_single_file_db 'TestBilingMember',
 'D:\Test\TestBilingMember.mdf'

반응형

'연구개발 > SQL2008' 카테고리의 다른 글

DBCC 명령문  (0) 2010.05.06
추척파일을 테이블로 변환하기  (0) 2010.05.04
데이터베이스 복원 / 온라인  (0) 2010.05.03
노출 영역 구성  (0) 2010.05.02
데이터베이스 복원 예시 (오프라인)  (0) 2010.04.29
반응형


/**
온라인 복원 (전체가 깨졋을 경우로 가정)
 - 대용량 데이터베이스로 가정하고 중요도가 높은 테이블이 포함된 파일그룹부터 복원할 경우
**/
USE master
RESTORE DATABASE testDB2 FROM DISK = 'D:\testDB2-FULL.bak'
WITH RECOVERY, REPLACE

/*
1. 기존의 백업을 무시하고 전체 백업실시
 백업장치를 이용하기 위해 백업장치 3개 생성
    USE master
    EXEC sp_addumpdevice 'disk', 'backupDevice1', 'D:\back1.bak'
    EXEC sp_addumpdevice 'disk', 'backupDevice2', 'D:\back2.bak'
    EXEC sp_addumpdevice 'disk', 'backupDevice3', 'D:\back3.bak'
*/
BACKUP DATABASE testDB2 TO backupDevice1, backupDevice2, backupDevice3
WITH NAME = N'전체 백업', INIT, FORMAT
GO

/* 확인 */
RESTORE HEADERONLY FROM backupDevice1, backupDevice2, backupDevice3

/* 데이터를 추가 */
USE testDB2
INSERT INTO tbl1 VALUES (60)
INSERT INTO tbl2 VALUES (61)
INSERT INTO tbl3 VALUES (62)
GO

/* 데이터베이스 오프라인 후 저장된 파일(FG*.*)를 모두 삭제 */
USE master
ALTER DATABASE testDB2 SET OFFLINE

/* 다시 온라인 시키면 오류메시지와 함께 온라인 된다. */
ALTER DATABASE testDB2 SET ONLINE

/*
가장 중요한 파일그룹부터 복원. 하지만 주파일그룹은 반드시 제일 먼저 복원되어야함.
중요도는 tbl1 -> tbl3 -> tbl2 라고 가정한다면, 주파일그룹 -> FG3 -> FG2 순으로 복원
*/
/* 우선 트랜잭션 로그를 임시 파일에 백업 */
BACKUP LOG testDB2 TO DISK = 'D:\비상로그1.bak'
WITH NO_TRUNCATE, INIT

/* 다시 복원한다. 이번에는 임시 로그도 복원 */
RESTORE DATABASE testDB2 FILEGROUP = 'PRIMARY'
FROM backupDevice1, backupDevice2, backupDevice3
WITH PARTIAL, NORECOVERY
GO
RESTORE LOG testDB2 FROM DISK = 'D:\비상로그1.bak'
GO

/* PRIMARY 그룹의 tbl1에 접근 가능. */
USE testDB2
SELECT * FROM tbl1

/*데이터 추가. 현재 트랜잭션 로그 파일에 로그 기록을 추가*/
INSERT INTO tbl1 VALUES (70)

/* 나머지 그룹 테이블에 접근 불가능 */
SELECT * FROM tbl2

/* 다음으로 중요한 FG3를 복원. 비상로그를 먼저 백업함 */
BACKUP LOG testDB2 TO DISK = 'D:\비상로그2.bak'
WITH NO_TRUNCATE, INIT
GO

USE master
RESTORE DATABASE testDB2 FILEGROUP = 'FG3'
FROM backupDevice1, backupDevice2, backupDevice3
WITH PARTIAL, NORECOVERY
GO
RESTORE LOG testDB2 FROM DISK = 'D:\비상로그2.bak'
GO

/* 접근이 가능해졌는지 확인 */
USE testDB2
SELECT * FROM tbl3

/* FG2 복원 */
BACKUP LOG testDB2 TO DISK = 'D:\비상로그3.bak'
WITH NO_TRUNCATE, INIT
GO

USE master
RESTORE DATABASE testDB2 FILEGROUP = 'FG2'
FROM backupDevice1, backupDevice2, backupDevice3
WITH PARTIAL, NORECOVERY
GO
RESTORE LOG testDB2 FROM DISK = 'D:\비상로그3.bak'
GO

/* 확인 */
USE testDB2
SELECT * FROM tbl2

반응형
반응형

노출영역 구성 설정법

개체 탐색기의 기본 인스턴스에서 패싯을 선택
패싯의 선택박스 중 노출 영역 구성 선택

속성
* AdHocRemoteQueriesEnabled
 : 데이터베이스 인스턴스에서 먼저 연결된 서버를 정의하지 않고도 원격 쿼리를 실행할 수 있는 OPENROWSET 및 OPENDATASOURCE 함수를 사용할 수 있다.

* ClrIntegrationEnabled
 : 데이터베이스 엔진 내에서 CLR 기반 사용자 정의 함수, 프로시저, 트리거, 유형을 사용할 수 있다.

* DatabaseMailEnabled
 : 데이터베이스 엔진에서 SMTP를 이용하여 전자메일을 보낼 수 있다.

* OleAutomationEnabled
 : Transact-SQL 일괄 처리, 저장 프로시저 및 트리거에서 사용자 지정 OLE 자동화 개체를 참조 할 수 있다.

* RemoteDacEnabled
 : 원격 컴퓨터의 DAC를 사용 할 수 있다. 즉 전용 관리자 연결을 허용하는 기능인데, 서버에 어떠한 문제로 정상적으로 접속을 할 수 없을 경우에, 'sqlcmd' 명령의 옵션을 사용해서 접속을 허용한다.

* SeviceBrokerEndpointActive
 : 데이터베이스 엔진에 대한 큐 기능 및 메시징을 제공한다.

* SoapEndpointsEnabled
 : Soap 끝점의 상태가 시작되거나 중지됨을 선택한다.

* SqlMailEnabled
 : 데이터베이스 메일로 대체되었다. 이전 버전과의 호환성 때문에 남겨 둔 항목이다.

* WebAssistantEnabled
 : 데이터베이스 엔진에서 HTML을 생성하는 기능으로, SQL Server 2005/2008에서는 더 이상 사용되지 않는다.

* XPCmdShellEnabled
 : 데이터베이스 엔진 내에서 운영체제 명령을 실행할 수 있게 해 준다. 좀 위험한 기능이므로 체크하지 않도록 한다.
반응형
반응형

특정 파일 또는 파일 그룹이 깨졌을 경우

USE master;
DROP DATABASE testDB2
GO

CREATE DATABASE [testDB2] ON PRIMARY
(
    NAME = N'FG1-1',
    FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL10.MSSQLSERVER\MSSQL\DATA\FG1-1.mdf'
),
(
    NAME = N'FG1-2',
    FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL10.MSSQLSERVER\MSSQL\DATA\FG1-2.ndf'
),
FILEGROUP [FG2]
(
    NAME = N'FG2-1',
    FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL10.MSSQLSERVER\MSSQL\DATA\FG2-1.ndf'
),
(
    NAME = N'FG2-2',
    FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL10.MSSQLSERVER\MSSQL\DATA\FG2-2.ndf'
),
FILEGROUP [FG3]
(
    NAME = N'FG3-1',
    FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL10.MSSQLSERVER\MSSQL\DATA\FG3-1.ndf'
),
(
    NAME = N'FG3-2',
    FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL10.MSSQLSERVER\MSSQL\DATA\FG3-2.ndf'
)
LOG ON
(
    NAME = N'testDB2_log',
    FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL10.MSSQLSERVER\MSSQL\DATA\testDB2_log.ldf'
)
GO

USE testDB2
CREATE TABLE tbl1 (num int)
GO
CREATE TABLE tbl2 (num int) ON FG2
GO
CREATE TABLE tbl3 (num int ) ON FG3
GO

-- 제 1회 데이터 입력 후 전체 백업
USE testDB2;
INSERT INTO tbl1 VALUES (10)
INSERT INTO tbl2 VALUES (11)
INSERT INTO tbl3 VALUES (12)
GO

BACKUP DATABASE testDB2 TO DISK = 'D:\testDB2.bak'
WITH NAME = N'제1회-전체백업', INIT
GO

BACKUP LOG testDB2 TO DISK = 'D:\testDB2.bak'
WITH NAME = N'제1회-로그백업'
GO

--제 2회 데이터 입력 후 Primary 그룹만 백업
USE testDB2
INSERT INTO tbl1 VALUES (20)
INSERT INTO tbl2 VALUES (21)
INSERT INTO tbl3 VALUES (22)
GO

BACKUP DATABASE testDB2 FILEGROUP = 'PRIMARY'
TO DISK = 'D:\testDB2.bak'
WITH NAME = N'제2회-파일그룹백업'
GO

BACKUP LOG testDB2 TO DISK = 'D:\testDB2.bak'
WITH NAME = N'제2회-로그백업'
GO

--제 3회 데이터 입력 후 FG2 그룹만 백업
USE testDB2
INSERT INTO tbl1 VALUES (30)
INSERT INTO tbl2 VALUES (31)
INSERT INTO tbl3 VALUES (32)
GO

BACKUP DATABASE testDB2 FILEGROUP = 'FG2'
TO DISK = 'D:\testDB2.bak'
WITH NAME = N'제3회-파일그룹백업'
GO

BACKUP LOG testDB2 TO DISK = 'D:\testDB2.bak'
WITH NAME = N'제3회-로그백업'
GO

--제 4회 데이터 입력 후 FG3 그룹만 백업
USE testDB2
INSERT INTO tbl1 VALUES (40)
INSERT INTO tbl2 VALUES (41)
INSERT INTO tbl3 VALUES (42)
GO

BACKUP DATABASE testDB2 FILEGROUP = 'FG3'
TO DISK = 'D:\testDB2.bak'
WITH NAME = N'제4회-파일그룹백업'
GO

BACKUP LOG testDB2 TO DISK = 'D:\testDB2.bak'
WITH NAME = N'제4회-로그백업'
GO

-- 백업된 내용 확인
RESTORE HEADERONLY FROM DISK = 'D:\testDB2.bak'
/*
참고 : 
BackupType 의 의미는
1 - 데이터베이스 백업
2 - 트랜잭션 로그 백업
4 - 파일 또는 파일그룹 백업
5 - 전체 차등 백업
6 - 파일 차등 백업
7 - 부분 백업
8 - 부분 차등 백업
*/

--제 5회 데이터를 입력
USE testDB2
INSERT INTO tbl1 VALUES (50)
INSERT INTO tbl2 VALUES (51)
INSERT INTO tbl3 VALUES (52)
GO

-- 두번째 파일그룹 중 'FC2-1' 파일이 깨졌다고 가정.
    해당 데이터베이스 오프라인 후 FG2-1.ndf를 지워버린다.
USE master
ALTER DATABASE testDB2 SET OFFLINE
GO

-- 다시 온라인 처리(에러가 나지만 온라인으로 처리가 된다)
USE master
ALTER DATABASE testDB2 SET ONLINE
GO

-- 데이터 복원
    깨진 두번째 파일그룹을 복원하기 위해서는 백업 받은 것 중에서 '제3회-파일그룹백업' 과 '제3회-로그백업' 만 있으면 된다.
    
    - 파일그룹 복원
    USE master
    RESTORE DATABASE testDB2 FILEGROUP = 'FG2'
    FROM DISK = 'D:\testDB2.bak'
    WITH FILE = 5, NORECOVERY
    
    - 위의 구문을 실행하여 오류 발생.. 비상 로그 백업을 받도록 한다.
    BACKUP LOG testDB2 TO DISK = 'D:\testDB2.bak'
    WITH NAME = '제5회-비상로그백업', NO_TRUNCATE
    
    - 백업 내용 다시 확인
    RESTORE HEADERONLY FROM DISK = 'D:\testDB2.bak'
    
    - 다시 복원 진행
    USE master
    RESTORE DATABASE testDB2 FILEGROUP = 'FG2'
    FROM DISK = 'D:\testDB2.bak'
    WITH FILE = 5, NORECOVERY
    GO
    RESTORE DATABASE testDB2 FILEGROUP = 'FG2'
    FROM DISK = 'D:\testDB2.bak'
    WITH FILE = 6, NORECOVERY
    GO
    RESTORE DATABASE testDB2 FILEGROUP = 'FG2'
    FROM DISK = 'D:\testDB2.bak'
    WITH FILE = 8, NORECOVERY
    GO
    RESTORE DATABASE testDB2
    FROM DISK = 'D:\testDB2.bak'
    WITH FILE = 9, RECOVERY --디폴드는 RECOVERY
    GO
    
    - 데이터 확인
    USE testDB2;
    SELECT * FROM tbl1
    SELECT * FROM tbl2
    SELECT * FROM tbl3
    
    - 정상처리된 데이터베이스 전체 백업
    BACKUP DATABASE testDB2 TO DISK = 'D:\testDB-FULL.bak'
    WITH NAME = N'전체 백업', INIT
    GO
반응형

'연구개발 > SQL2008' 카테고리의 다른 글

데이터베이스 복원 / 온라인  (0) 2010.05.03
노출 영역 구성  (0) 2010.05.02
데이터베이스 백업 / 복구모델  (0) 2010.04.27
인덱스 정보 조회  (0) 2010.04.22
FullText Search 시에 가중치 주기  (0) 2010.04.22
반응형
데이터베이스의 복구 모델

+ 백업

- 전체 복구 모델
    전체 백업, 차등 백업, 로그 백업이 가능(Insert, Update, Select Into, Create Index 내용 모두)
    원하는 위치로의 복구
    
- 대량 로그 복구 모델
    전체 백업, 차등 백업, 로그 백업이 가능
    작업이 일어난 상태만 기록 될 뿐, 추가되거나 변경된 내용은 기록되지 않음.(Insert, Update 내용 모두, Create index, Select Into 발생기록만)
    원하는 위치로의 복구가 안됨.
    내용을 기록하지 않기에 전체 복구 모델에 비해 속도가 빠름
        
- 단순 복구 모델
    전체 백업, 차등 백업만 가능. 로그 백업은 할 수 없음.
    로그파일에 아무것도 기록되지 않음.
    단순 복구 모델은 문제점이 발생된 시점까지 데이터를 복구 할 수 없고, 마지막에 백업 받은 데이터까지만 복구가 가능
    
데이터베이스 백업의 종류
- 전체 백업, 차등 백업, 트랜잭션 로그 백업, 파일 및 파일그룹 백업, 부분 백업, 부분차등 백업

- 전체 백업(Full Backup)
    데이터베이스의 개체, 시스템 테이블, 데이터 모두 백업, 로그 또한 백업
    
    구문 : BACKUP DATABASE 데이터베이스이름 TO 백업할 파일 또는 장치
            BACKUP DATABASE AdventureWorks TO disk = "c:\백업폴더\advBackup.bak"
            BACKUP DATABASE AdventureWorks TO advDevice(백업장치)
            
        데이터베이스 저장 공간 확인 EXEC sp_spaceused

- 차등 백업(Differential Backup)
    구문 : BACKUP DATABASE 데이터베이스이름 TO 백업할 파일 도는 장치 WITH DIFFERENTIAL
    
    로그백업구문형식 : BACKUP LOG 데이터베이스이름 TO 백업할 파일 또는 장치
    
    로그백업을 하지 않고 로그 파일을 비우기 : 
        BACKUP LOG 데이터베이스이름 WITH NO_LOG
       OR
        BACKUP LOG 데이터베이스이름 WITH TRUNCATE_ONLY  
       
- 부분 백업(Partial Backup)
    전체 백업과 비슷하지만 읽기 전용 파일그룹은 백업하지 않음. 즉 주파일 그룹과 읽기/쓰기 파일그룹만을 백업.
    단순 복구 모델의 데이터베이스에 적용시키기에 적당함.
    
    구문 : BACKUP DATABASE 데이터베이스이름 READ_WRITE_FILEGROUPS TO 장치
    
- 부분 차등 백업
    차등 백업과 비슷하며 읽기 전용 파일그룹은 백업하지 않는다.  
    
- 미러 백업
    두 개의 백업 장치에 백업함.
    
    구문 : BACKUP DATABASE AdventureWorks
            TO disk = 'C:\adv.bak'
            MIRROR TO disk = 'd:\adv.bak'
            WITH FORMAT
            
- 복사전용 백업
    구문 : BACKUP DATABASE 데이터베이스이름 TO 장치 WITH COPY_ONLY
    


- 체크섬(CHECKSUM) 기능
    백업 시에 백업 받은 데이터에 이상이 없는지를 확인하면서 백업하는 기능

    구문 : BACKUP DATABASE 데이터베이스이름 TO 장치 WITH CHECKSUM
    
- 백업매체 초기화
    기존에 백업된 내용 모두 삭제 후 새로 백업
    
    구문 : BACKUP DATABASE 데이터베이스이름 TO 장치 WITH INIT
    
- 다중 백업장치의 초기화
    FORMAT을 붙이면 다중 백업장치(여러 개의 디스크나 테이프에 동시에 백업하는 것)를 초기화한 후에 백업수행
    
    구문 : BACKUP DATABASE 데이터베이스이름 TO 장치1, 장치2, 장치3 WITH FORMAT
    
- 비밀번호 지정
    구문 : BACKUP DATABASE 데이터베이스이름 TO 장치 WITH PASSWORD = '비밀번호'
    
- 백업 중 오류 발생 시 계속 여부
    CONTINUE_AFTER_ERROR 옵션은 오류 발생해도 계속 백업 진행
    STOP_ON_ERROR 은 오류 발생 시 백업 중지(DEFAULT)
    
    구문 : BACKUP DATABASE 데이터베이스이름 TO 장치 WITH CONTINUE_AFTER_ERROR
    
- 진행률 표시
    현재 백업되는 진행상황 보기
    
    구문 : BACKUP DATABASE 데이터베이스이름 TO 장치 WITH STATS
    
- 데이터베이스에 문제 발생 시 로그 백업
    구문 : BACKUP LOG 데이터베이스이름 TO 장치 WITH NO_TRUNCATE
    

    



+ 복원

- 전체 복원
    구문 : RESTORE DATABASE 데이터베이스이름 FROM 백업장치
    
- 차등 복원
    구문 : RESTORE DATABASE 데이터베이스이름 FROM 백업장치
    
- 로그 복원
    구문 : RESTORE LOG 데이터베이스이름 FROM 백업장치
    
- 복원 중 / 복원 완료
    복원 중 구문 : RESTORE DATABASE 데이터베이스이름 FROM 백업장치 WITH NORECOVERY
    복원 완료 구문 : RESTORE DATABASE 데이터베이스이름 FROM 백업장치 WITH RECOVERY

- 복원 후에 제한된 사용자만 접근 허용
    db_owner, dbcreator, sysadmin 역할의 권한을 가진 사용자만 접근 가능, 일반사용자 접근 불가
    구문 : RESTORE DATABASE 데이터베이스이름 FROM 백업장치 WITH RESTRICTED_USER
            확인절차 끝난 후
            RESTORE DATABASE 데이터베이스이름 WITH RECOVERY
            
- 복원시 데이터 파일의 이동
    구문 : RESTORE DATABASE 데이터베이스이름 FROM 백업장치
                WITH MOVE 'AdventureWorks_data' TO 'd:\adv.mdf'
                        , MOVE 'AdventureWorks_log' TO 'd:\adv.ldf'
                        
- 미러백업 매체에서 복원
    미러 백업 시 MIRROR TO 옵션을 사용해 백업한 매체에 대해 복원을 하는 경우
    구문 : BACKUP DATABASE AdventureWorks
            TO DISK = 'C:\adv.bak'
            MIRROR TO DISK = 'D:\adv.bak'
            WITH FORMAT
            
            D:\adv.bak으로 복원하면서 데이터파일 및 로그 파일 위치 이동시
            
            RESTORE DATABASE AdventureWorks
            FROM DISK = 'D:\adv.bak'
            WITH MOVE 'AdventureWorks_data' TO '데이터 파일 경로 및 파일명'
                    ,MOVE 'AdventureWorks_log' TO '로그 파일 경로 및 파일명'
                   
- 오류 발생시에도 계속 복원하기
    구문 : RESTORE DATABASE 데이터베이스이름 FROM 백업장치
            WITH CONTINUE_AFTER_ERROR
            
- 덮어쓰기
    구문 : RESTORE DATABASE 데이터베이스이름 FROM 백업장치 WITH REPLACE
    
- 정확한 시점까지만 복원
    구문 : RESTORE DATABASE 데이터베이스이름 FROM 백업장치 WITH STOPAT = '날짜와 시간'
    
- 데이터베이스 스냅숏으로 복원
    구문 : RESTORE DATABASE 데이터베이스이름 FROM DATABASE_SNAPSHOT = '스냅숏이름';
    

반응형

'연구개발 > SQL2008' 카테고리의 다른 글

노출 영역 구성  (0) 2010.05.02
데이터베이스 복원 예시 (오프라인)  (0) 2010.04.29
인덱스 정보 조회  (0) 2010.04.22
FullText Search 시에 가중치 주기  (0) 2010.04.22
커서 cursor  (0) 2010.04.20
반응형


/*
MSSQL 공통버전
*/
-- 인덱스 정보 조회
IF EXISTS (SELECT [name]
FROM sys.sysobjects
WHERE [type] = 'V' AND [name] = 'idxinfo')

BEGIN
DROP VIEW [dbo].[idxinfo]
END
GO

CREATE VIEW dbo.idxinfo
AS
SELECT o.id AS [TableId]
    , u.name AS [Owner]
    , o.name AS [TableName]
    , i.indid AS [IndexID]
    , CASE i.name WHEN o.name THEN '** NONE **'
        ELSE i.name END AS [IndexName]
    , CASE i.indid WHEN 1 THEN 'CLUSTERED'
                     WHEN 0 THEN 'HEAP'
        ELSE 'NONCLUSTERED' END AS [ClusterType]
, CASE WHEN (i.status & 2048) > 0 THEN 'PRIMARY KEY'
             WHEN (i.status & (2|4096)) > 0 THEN 'UNIQUE'
        ELSE ' ' END AS [UniqueType]
    , CASE WHEN (i.status & (2048)) > 0 OR ((i.status & (4096)) > 0) THEN 'CONSTRAINT'
             WHEN i.indid = 0 THEN '    '
        ELSE 'INDEX' END AS [IndexType]
    , CASE WHEN INDEX_COL(u.name + '_' + QUOTENAME(o.name), i.indid, 1) IS NULL THEN ' '
        ELSE INDEX_COL(u.name + '_' + QUOTENAME(o.name), i.indid, 1) END + 
      CASE WHEN INDEX_COL(u.name + '_' + QUOTENAME(o.name), i.indid, 2) IS NULL THEN ' '
        ELSE ', ' + INDEX_COL(u.name + '_' + QUOTENAME(o.name), i.indid, 2) END + 
      CASE WHEN INDEX_COL(u.name + '_' + QUOTENAME(o.name), i.indid, 3) IS NULL THEN ' '
        ELSE ', ' + INDEX_COL(u.name + '_' + QUOTENAME(o.name), i.indid, 3) END + 
      CASE WHEN INDEX_COL(u.name + '_' + QUOTENAME(o.name), i.indid, 4) IS NULL THEN ' '
        ELSE ', ' + INDEX_COL(u.name + '_' + QUOTENAME(o.name), i.indid, 4) END + 
      CASE WHEN INDEX_COL(u.name + '_' + QUOTENAME(o.name), i.indid, 5) IS NULL THEN ' '
        ELSE ', ' + INDEX_COL(u.name + '_' + QUOTENAME(o.name), i.indid, 5) END + 
      CASE WHEN INDEX_COL(u.name + '_' + QUOTENAME(o.name), i.indid, 6) IS NULL THEN ' '
        ELSE ', ' + INDEX_COL(u.name + '_' + QUOTENAME(o.name), i.indid, 6) END + 
      CASE WHEN INDEX_COL(u.name + '_' + QUOTENAME(o.name), i.indid, 7) IS NULL THEN ' '
        ELSE ', ' + INDEX_COL(u.name + '_' + QUOTENAME(o.name), i.indid, 7) END + 
      CASE WHEN INDEX_COL(u.name + '_' + QUOTENAME(o.name), i.indid, 8) IS NULL THEN ' '
        ELSE ', ' + INDEX_COL(u.name + '_' + QUOTENAME(o.name), i.indid, 8) END + 
      CASE WHEN INDEX_COL(u.name + '_' + QUOTENAME(o.name), i.indid, 9) IS NULL THEN ' '
        ELSE ', ' + INDEX_COL(u.name + '_' + QUOTENAME(o.name), i.indid, 9) END + 
      CASE WHEN INDEX_COL(u.name + '_' + QUOTENAME(o.name), i.indid, 10) IS NULL THEN ' '
        ELSE ', ' + INDEX_COL(u.name + '_' + QUOTENAME(o.name), i.indid, 10) END + 
      CASE WHEN INDEX_COL(u.name + '_' + QUOTENAME(o.name), i.indid, 10) IS NULL THEN ' '
        ELSE ', ' + INDEX_COL(u.name + '_' + QUOTENAME(o.name), i.indid, 11) END + 
      CASE WHEN INDEX_COL(u.name + '_' + QUOTENAME(o.name), i.indid, 11) IS NULL THEN ' '
        ELSE ', ' + INDEX_COL(u.name + '_' + QUOTENAME(o.name), i.indid, 12) END + 
      CASE WHEN INDEX_COL(u.name + '_' + QUOTENAME(o.name), i.indid, 12) IS NULL THEN ' '
        ELSE ', ' + INDEX_COL(u.name + '_' + QUOTENAME(o.name), i.indid, 13) END + 
      CASE WHEN INDEX_COL(u.name + '_' + QUOTENAME(o.name), i.indid, 13) IS NULL THEN ' '
        ELSE ', ' + INDEX_COL(u.name + '_' + QUOTENAME(o.name), i.indid, 14) END + 
      CASE WHEN INDEX_COL(u.name + '_' + QUOTENAME(o.name), i.indid, 14) IS NULL THEN ' '
        ELSE ', ' + INDEX_COL(u.name + '_' + QUOTENAME(o.name), i.indid, 14) END + 
      CASE WHEN INDEX_COL(u.name + '_' + QUOTENAME(o.name), i.indid, 15) IS NULL THEN ' '
        ELSE ', ' + INDEX_COL(u.name + '_' + QUOTENAME(o.name), i.indid, 15) END +  
      CASE WHEN INDEX_COL(u.name + '_' + QUOTENAME(o.name), i.indid, 16) IS NULL THEN ' '
        ELSE ', ' + INDEX_COL(u.name + '_' + QUOTENAME(o.name), i.indid, 16) END AS [AllColName]
    , OrigFillFactor AS [FillFactor] 
FROM sysobjects o (NOLOCK)
LEFT OUTER JOIN sysindexes i (NOLOCK)
ON o.id = i.id
JOIN sysusers u (NOLOCK)
ON o.uid = u.uid
WHERE o.type = 'U' AND i.indid < 255
AND o.name NOT IN ('dtproperties')
AND i.name NOT LIKE '_WA_Sys_%' --because of SQL Server 7.0
GO

--전체 인덱스 정보 조회
SELECT * FROM dbo.idxInfo
GO

--Heap 구조의(Clustered Index가 없는) 테이블 조회
SELECT * FROM dbo.idxInfo
WHERE ClusterType = 'HEAP'
GO

--AdventureWorks 의 dbo.Employee 테이블 인덱스 조회
SELECT * FROM dbo.idxInfo
WHERE Owner = 'head1ton'
    AND TableName = 'Employee'
GO




/*
MSSQL 2005 이상 버전
*/
--인덱스 정보 조회
IF EXISTS (SELECT [name]
             FROM sys.sysobjects
             WHERE [type] = 'V'
                AND [name] = 'idxInfo')

BEGIN
    DROP VIEW [dbo].[idxInfo]
END
GO

ALTER VIEW [dbo].[idxinfo]
AS
SELECT o.[schema_id]                  AS [schema_id]
    , SCHEMA_NAME(o.[schema_id]) AS [schema_name]
    , o.[object_id]                        AS [object_id]
    , o.[name]                            AS [table_name]
    , i.[name]                             AS [index_name]
    , i.[type_desc]                        AS [index_type]
    , CASE i.[is_unique] WHEN 1 THEN 'Unique'
                            WHEN 0 THEN 'Non-unique'
        ELSE ' ' END AS [uniqueness]
    , CASE WHEN i.[is_primary_key] = 1 THEN 'PK'
             WHEN i.[is_unique_constraint] = 1 THEN 'UK'
        ELSE ' ' END AS [constraint]
    , CASE WHEN INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 1) IS NULL THEN ' '
        ELSE INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 1) END +
      CASE WHEN INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 2) IS NULL THEN ' '
        ELSE INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 2) END +
      CASE WHEN INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 3) IS NULL THEN ' '
        ELSE INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 3) END +
      CASE WHEN INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 4) IS NULL THEN ' '
        ELSE INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 4) END +
      CASE WHEN INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 5) IS NULL THEN ' '
        ELSE INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 5) END +
      CASE WHEN INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 6) IS NULL THEN ' '
        ELSE INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 6) END +    
      CASE WHEN INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 7) IS NULL THEN ' '
        ELSE INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 7) END +
      CASE WHEN INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 8) IS NULL THEN ' '
        ELSE INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 8) END +
      CASE WHEN INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 9) IS NULL THEN ' '
        ELSE INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 9) END +
      CASE WHEN INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 10) IS NULL THEN ' '
        ELSE INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 10) END +
      CASE WHEN INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 11) IS NULL THEN ' '
        ELSE INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 11) END +
      CASE WHEN INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 12) IS NULL THEN ' '
        ELSE INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 12) END +
      CASE WHEN INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 13) IS NULL THEN ' '
        ELSE INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 13) END +
      CASE WHEN INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 14) IS NULL THEN ' '
        ELSE INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 14) END +
      CASE WHEN INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 15) IS NULL THEN ' '
        ELSE INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 15) END +         
      CASE WHEN INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 16) IS NULL THEN ' '
        ELSE INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 16) END AS [index_columns]
    , i.[fill_factor] AS [fillfactor]
    , CASE i.[is_disabled] WHEN 0 THEN 'Active'
                              WHEN 1 THEN 'Inactive' END AS [is_disabled]
FROM sys.objects o WITH(NOLOCK)
    LEFT OUTER JOIN sys.indexes i WITH(NOLOCK)
        ON o.[object_id] = i.[object_id]
WHERE o.[type] = 'U'
    AND i.[index_id] < 255
GO

-- 전체 인덱스 정보 조회
SELECT * FROM dbo.idxInfo;
GO

-- Heap구조의 (CLUSTERED INDEX가 없는) 테이블 조회
SELECT *
FROM dbo.idxInfo
WHERE index_type = 'HEAP'
GO

-- 특정 테이블의 인덱스 조회
SELECT *
FROM dbo.idxInfo
WHERE [OBJECT_ID] = OBJECT_ID('schema_name.table_name')
GO

--or
SELECT *
FROM dbo.idxInfo
WHERE [SCHEMA_NAME] = 'schema_name'
    AND [TABLE_NAME] = 'table_name'
GO


/*
sys.dm_db_index_physical_stats
Dynamic Management Function 을 사용하여 인덱스의 물리적 통계정보를 함께 조회
*/
IF EXISTS (SELECT [name]
            FROM sys.sysobjects
            WHERE [type] = 'V'
                AND [name] = 'idxinfo2')
                
BEGIN
    DROP VIEW [dbo.[idxinfo2]
END
GO

CREATE VIEW [dbo].[idxinfo2]
AS
SELECT o.[schema_id]                      AS [schema_id]
    , SCHEMA_NAME(o.[schema_id])     AS [schema_name]
    , o.[object_id]                            AS [object_id]
    , o.[name]                                AS [table_name]
    , i.[name]                                 AS [index_name]
    , i.[type_desc]                            AS [index_type]
    , CASE i.[is_unique] WHEN 1 THEN 'Unique'
                            WHEN 0 THEN 'Non-unique'
        ELSE ' ' END AS [uniqueness]
    , CASE WHEN INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 1) IS NULL THEN ' '
        ELSE INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 1) END +
      CASE WHEN INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 2) IS NULL THEN ' '
        ELSE INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 2) END +
      CASE WHEN INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 3) IS NULL THEN ' '
        ELSE INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 3) END +
      CASE WHEN INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 4) IS NULL THEN ' '
        ELSE INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 4) END +
      CASE WHEN INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 5) IS NULL THEN ' '
        ELSE INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 5) END +
      CASE WHEN INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 6) IS NULL THEN ' '
        ELSE INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 6) END +
      CASE WHEN INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 7) IS NULL THEN ' '
        ELSE INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 7) END +
      CASE WHEN INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 8) IS NULL THEN ' '
        ELSE INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 8) END +
      CASE WHEN INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 9) IS NULL THEN ' '
        ELSE INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 9) END +
      CASE WHEN INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 10) IS NULL THEN ' '
        ELSE INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 10) END +
      CASE WHEN INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 11) IS NULL THEN ' '
        ELSE INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 11) END +
      CASE WHEN INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 12) IS NULL THEN ' '
        ELSE INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 12) END +
      CASE WHEN INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 13) IS NULL THEN ' '
        ELSE INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 13) END +
      CASE WHEN INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 14) IS NULL THEN ' '
        ELSE INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 14) END +
      CASE WHEN INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 15) IS NULL THEN ' '
        ELSE INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 15) END +
      CASE WHEN INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 16) IS NULL THEN ' '
        ELSE INDEX_COL(SCHEMA_NAME(o.[schema_id]) + '_' + o.[name], i.index_id, 16) END AS [index_columns]
    , i.[fill_factor] AS [fillfactor]
    , CASE i.[is_disabled] WHEN 0 THEN 'Active'
                                WHEN 1 THEN 'Inactive' END AS [is_disabled]
    , [st].[index_depth]  AS [index_depth]
    , [st].[index_level]    AS [index_level]
    , [st].[page_count]   AS [page_count]
    , ROUND([st].[avg_fragmentation_in_percent], 2) AS [avg_fragmentation_in_percent]                      
FROM sys.objects o WITH (NOLOCK)
    LEFT OUTER JOIN sys.indexes i WITH (NOLOCK)
        ON o.[object_id] = i.[object_id]
    LEFT JOIN sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, 'SAMPLED') st
        ON [i].[object_id] = [st].[object_id] AND [i].[index_id] = [st].[index_id]
WHERE o.[type] = 'U'
    AND i.[index_id] < 255
GO

-- 전체 인덱스 정보 조회
SELECT * FROM dbo.idxInfo2;
GO

-- Heap구조의 (CLUSTERED INDEX가 없는) 테이블 조회
SELECT *
FROM dbo.idxInfo2
WHERE index_type = 'HEAP'
GO

-- 특정 테이블의 인덱스 조회
SELECT *
FROM dbo.idxInfo2
WHERE [OBJECT_ID] = OBJECT_ID('schema_name.table_name')
GO

--or
SELECT *
FROM dbo.idxInfo
WHERE [SCHEMA_NAME] = 'schema_name'
    AND [TABLE_NAME] = 'table_name'
GO
반응형

'연구개발 > SQL2008' 카테고리의 다른 글

데이터베이스 복원 예시 (오프라인)  (0) 2010.04.29
데이터베이스 백업 / 복구모델  (0) 2010.04.27
FullText Search 시에 가중치 주기  (0) 2010.04.22
커서 cursor  (0) 2010.04.20
DatePart 주차  (0) 2010.04.19
반응형


SELECT * FROM CONTAINSTABLE(FullTbl, *, N'일본');

KEY         RANK
----------- -----------
2           128
13          64

(2개 행이 영향을 받음)


SELECT f.id, c.RANK AS [가중치], f.title, f.description
FROM FullTbl AS f
INNER JOIN CONTAINSTABLE(FullTbl, *, N'일본') AS c
ON f.id = c.[Key]

(1개 행이 영향을 받음)
id          가중치         title      description
----------- ----------- ---------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
2           128         일본침몰       일본열도를 뒤흔든 거대한 지진 마침내 현실이 된 일본의 멸망
13          64          한반도        일본은 한반도의 경의선 철도 개통을 허가하지 않겠습니다.
반응형

'연구개발 > SQL2008' 카테고리의 다른 글

데이터베이스 백업 / 복구모델  (0) 2010.04.27
인덱스 정보 조회  (0) 2010.04.22
커서 cursor  (0) 2010.04.20
DatePart 주차  (0) 2010.04.19
인덱스.. 클러스터 인덱스 와 넌클러스터 인덱스  (0) 2010.04.15
반응형

커서의 옵션

STATIC / DYNAMIC / KEYSET 에 대해 알아보면

STATIC(정적) 은 원본 DB에 대한 데이터를 모두 복사하는 것.
 그러므로 원본 DB의 변경 및 삽입은 커서에 반영되지 않으며
DYNAMIC(동적) 은 원본 DB에 대한 현재 커서 포인트의 키 값만 복사하는 것.
 그러므로 원본 DB가 변경되거나 삽입된 정보를 커서에서도 확인가능하며
KEYSET(키집합) 은 원본 DB에 대한 키 값만 모두 복사하는 것. 
 그러므로 원본 DB가 변경되는 것은 커서에서도 확인 가능하지만 새로운 열이 삽입되는 것은 알 수가 없다.


DECLARE cursorTbl_cursor CURSOR
    FOR SELECT str1 FROM cursorTbl;

DECLARE @result CURSOR
EXEC sp_describe_cursor @cursor_return = @result OUTPUT,
    @cursor_source = N'GLOBAL'
    @cursor_identity = N'cursorTbl_cursor'

FETCH NEXT FROM @result
WHILE (@@FETCH_STATUS <> -1)
    FETCH NEXT FROM @result

실행하면 model필드의 값이 3으로 설정된 것이 보일 것이다.
1 : STATIC
2 : KEYSET
3 : DYNAMIC
4 : FAST_FORWARD

DEFAULT 시 DYNAMIC

concurrency 필드의 값은
1 : READ_ONLY (읽기 전용)
2 : SCROLL_LOCKS(스크롤잠금)
3 : OPTIMISTIC(낙관적)

STATUS 
-1 : 커서가 열리지 않은 상태

DEALLOCATE cursorTbl_cursor  커서 해제한다.




예외적으로 

CREATE TABLE keysetTbl (id INT, txt CHAR(5));
INSERT INTO keysetTbl VALUES (1, 'AAA');
INSERT INTO keysetTbl VALUES (2, 'BBB');
INSERT INTO keysetTbl VALUES (3, 'CCC');

DECLARE keysetTbl_cursor CURSOR GLOBAL FORWARD_ONLY KEYSET
FOR SELECT id, txt FROM keysetTbl;

DECLARE @result CURSOR
EXEC sp_describe_cursor @cursor_return = @result OUTPUT,
@cursor_source = N'GLOBAL', @cursor_identity = N'keysetTbl_cursor'

FETCH NEXT FROM @result
WHILE (@@FETCH_STATUS <> -1)
FETCH NEXT FROM @result


KEYSET으로 설정했음에도 model 필드의 값은 1(STATIC)이다.
이것은 keysetTbl에 고유 인덱스가 없기 때문에 암시적인 커서 변환이 일어나서 KEYSET이 STATIC으로 변환된 것이다.

이럴때는 
DECLARE keysetTbl_cursor CURSOR GLOBAL FORWARD_ONLY KEYSET TYPE_WARNING
    FOR SELECT id, txt FROM keysetTbl;
을 사용하면 정상적으로 나온다.

-뇌를 자극하는 SQL2008 중에서..







반응형

'연구개발 > SQL2008' 카테고리의 다른 글

인덱스 정보 조회  (0) 2010.04.22
FullText Search 시에 가중치 주기  (0) 2010.04.22
DatePart 주차  (0) 2010.04.19
인덱스.. 클러스터 인덱스 와 넌클러스터 인덱스  (0) 2010.04.15
테이블 정보 프로시저  (0) 2010.04.15
반응형

해당되는 월의 주차를 구할 때

 

select datepart(ww, '20100419') - datepart(ww, convert(datetime, left(convert(varchar, '20100419', 112), 6) + '01', 112)) + 1


select datepart(dw,getdate())


지난 주차 구할 때는


declare  @n_date datetime

set @n_date =dateadd(d,-datepart(dw,getdate()),getdate())


select @n_date-6, @n_date

반응형
반응형

인덱스를 보다가 보니
클러스터 인덱스와 넌클러스터 인덱스에 대한 점을 간단히 정의해보겠다.

클러스터 인덱스란 인덱스 생성시에 데이터페이지 즉 테이블에 순서적으로 입력이 된다.
그것이 곧 인덱스가 되어 색인 처럼 찾을 수 있는 것이다.
즉.. 테이블과 리프페이지가 동일하다.

1. INSERT INTO testTbl VALUES ('ABC')
2. INSERT INTO testTbl VALUES ('CDE')
3. INSERT INTO testTbl VALUES ('DEF')
4. INSERT INTO testTbl VALUES ('BCD')

SELECT * FROM testTbl

-----------------------------
ABC
BCD
CDE
DEF
-----------------------------

반면,

넌클러스터 인덱스는 입력시에 테이블에 들어가는 것은 입력하는 순서대로 입력이 된다. 
1. INSERT INTO testTbl VALUES ('ABC')
2. INSERT INTO testTbl VALUES ('CDE')
3. INSERT INTO testTbl VALUES ('DEF')
4. INSERT INTO testTbl VALUES ('BCD')

SELECT * FROM testTbl

-----------------------------
ABC
CDE
DEF
BCD
-----------------------------

하지만 넌클러스터 인덱스는 테이블 자체(데이터페이지) 가 색인이 되는 것이 아니라
리프페이지 그곳에 넌클러스터 인덱스로 설정된 필드가 이름 순으로 페이지번호+#오프셋 과 함께 생성이 된다.

문제의 요점은.. 이름순과 페이지번호+#오프셋(주소)으로 설정이 되지만
인덱스 검색이 이름으로 검색되는 되는 것인가? 페이지번호+#오프셋(주소)로 검색되는 것인가?

.. 본인이 생각한 결과는..
이름으로 검색하고 검색된 내용에 대해 주소로 해당 데이터를 찾는다. 라고 생각한다...
틀렸는감?????
반응형

'연구개발 > SQL2008' 카테고리의 다른 글

커서 cursor  (0) 2010.04.20
DatePart 주차  (0) 2010.04.19
테이블 정보 프로시저  (0) 2010.04.15
문자열 검사 / 문자겸사 / 필드검사  (0) 2010.04.13
Database 수정  (0) 2010.04.11
반응형

CREATE PROCEDURE sp_IndexInfo
@tablename sysname
AS
SELECT @tablename AS '테이블이름',
I.name AS '인덱스이름',
I.type_desc AS '인덱스타입',
A.data_pages AS '페이지개수',
A.data_pages * 8 AS '크기(KB)',
P.rows AS '행개수'
FROM sys.indexes I
INNER JOIN sys.partitions P
ON P.object_id = I.object_id
AND OBJECT_ID(@tablename) = I.object_id
AND I.index_id = P.index_id
INNER JOIN sys.allocation_units A
ON A.container_id = P.hobt_id
GO
반응형
반응형
use tempdb
CREATE TABLE TEST (A VARCHAR(10))
INSERT TEST VALUES('가나다') 
INSERT TEST VALUES('ABC') 
INSERT TEST VALUES('radio') 
INSERT TEST VALUES('123')
INSERT TEST VALUES('hEAd1가Ton')
select * from TEST
-- 한글만 검색 
SELECT * FROM TEST WHERE A LIKE '%[가-힣]%'
-- 영문만 검색
SELECT * FROM TEST WHERE A LIKE '%[A-Za-z]%'
반응형
반응형

데이터베이스 수정에 대해 늘 접하지만 SSMS 툴로만 하던 일을 T-SQL로 진행해보자.

1. 데이터베이스 파일 크기 늘리기
testDB가 있다고 가정했을 시에
자동 생성시 3M가 잡혀있을 것이다.

그것을 50M로 변경해준다면

ALTER DATABASE testDB
MODIFY FILE
(
NAME = testDB,
SIZE = 50
)
GO

다시 100사이즈로 변경해야 한다면
ALTER DATABASE testDB
MODIFY FILE
(
NAME = testDB,
SIZE = 100
)
GO

이렇게 하면 된다.

100 사이즈가 너무 크게 설정한 것 같아 다시 작게 설정하려고 한다면 에러메시지를 발생시키게 될 것이다.
왜냐믄 큰 사이즈에서 작은 사이즈로는 줄일 수가 없으므로.....

작게 줄이고 싶다면 DBCC SHRINKFILE 을 사용해야한다.

USE testDB
DBCC SHRINKFILE (testDB, 80)


2. 데이터베이스 이름 변경

데이터베이스의 이름을 변경하고 싶다면 단일사용자 모드로 변경 후 데이터베이스의 이름을 변경한다. 그리고 나서 다시 다중사용자 모드로 변경해준다.
USE testDB
ALTER DATABASE testDB
SET SINGLE_USER
GO
ALTER DATABASE testDB
MODIFY NAME = devDB
GO
ALTER DATABASE devDB
SET MULTI_USER
GO

3. 데이터베이스의 논리적/물리적 이름 변경
- 논리적 이름 변경

USE devDB
ALTER DATABASE devDB
MODIFY FILE
(
NAME = testDB,
NEWNAME = devDB
)
GO
ALTER DATABASE devDB
(
NAME = testDB_log,
NEWNAME = devDB_log
)
-물리적이름 및 경로 변경
물리적 이름 및 경로를 변경하기 위해서는 데이터베이스를 OFFLINE 시켜야한다.
ALTER DATABASE devDB
SET OFFLINE

ALTER DATABASE devDB
MODIFY FILE
(
NAME = devDB,
FILENAME = 'C:\DEV\devDB.mdf'
)
GO
ALTER DATABASE devDB
MODIFY FILE
(
NAME = devDB_log,
FILENAME = 'C:\DEV\devDB_log.ldf'
)
GO

ALTER DATABASE devDB
SET ONLINE

변경 후 확인
EXEC sp_helpdb devDB
반응형
반응형

이번 강좌에서는 사용자 정의 테이블 데이터 형식(User Defined Table Data Type)에 대한 내용을 살펴보겠습니다. 이론적인 설명보다 다음의 예를 보시면 사용자 정의 테이블 데이터 형식이 어떤 용도로 사용될 수 있는지 알 수 있을것입니다.

1) 데이터베이스와 테이블 만들기

우선 다음과 같이 Sample 이라는 이름의 연습용 데이터베이스와 Member 테이블을 만들겠습니다.

  1. -- 연습용 데이터베이스 만들기   
  2. CREATE DATABASE Sample   
  3. GO   
  4. -- 테이블 만들기   
  5. USE Sample   
  6. GO   
  7. CREATE TABLE Member (   
  8.  MemID varchar(10) PRIMARY KEY,   
  9.  MemName nvarchar(10),   
  10.  MemMail varchar(100)   
  11. )   
  12. GO  

2) 회원 정보 입력하기

이러한 상황에서 우리는 일반적으로 다음과 같이 Member 테이블에 회원 정보를 기록하는 저장 프로시저를 만들어 사용을 합니다.

  1. -- 프로시저 만들기   
  2. CREATE PROCEDURE usp_InsertMember   
  3.  @MemID varchar(10),   
  4.  @MemName nvarchar(10),   
  5.  @MemMail varchar(100)   
  6. AS  
  7.  SET NOCOUNT ON  
  8.  INSERT INTO Member(MemID, MemName, MemMail)    
  9.   VALUES(@MemID@MemName@MemMail)   
  10.  SET NOCOUNT OFF  
  11. GO  

위와 같이 저장 프로시저를 만들고 다음과 같이 호출해서 회원 정보를 입력합니다.

  1. -- 프로시저 수행   
  2. EXECUTE usp_InsertMember 'jrlee', N'이장래''jrlee@sample.com'  
  3. EXECUTE usp_InsertMember 'hong', N'홍길동''gildong@sample.com'  
  4. EXECUTE usp_InsertMember 'han', N'한국인''han@sample.com'  
  5. GO   
  6. -- 확인   
  7. SELECT * FROM Member   
  8. GO   
  9. /*  
  10. [결과]  
  11. MemID   MemName   MemMail  
  12. ------- -------- ---------------------------------  
  13. han     한국인    han@sample.com  
  14. hong    홍길동    gildong@sample.com  
  15. jrlee   이장래    jrlee@sample.com  
  16. (3개 행이 영향을 받음)  
  17. */  

자, 이제 사용자 정의 테이블 데이터 형식을 사용하는 예를 보도록 하겠습니다.

3) 새로운 테이블 만들기

사용자 정의 테이블 데이터 형식 사용예를 보기 위해 앞에서 사용한 Member 테이블과 같은 구조의 Member2 테이블을 만들어 사용하겠습니다.

  1. -- 테이블 만들기   
  2. CREATE TABLE Member2 (   
  3.  MemID varchar(10) PRIMARY KEY,   
  4.  MemName nvarchar(10),   
  5.  MemMail varchar(100)   
  6. )   
  7. GO  

4) 사용자 정의 테이블 데이터 형식 만들기

사용자 정의 테이블 데이터 형식은 다음과 같이 CREATE TYPE 문을 사용해 만듭니다. 일반적인 테이블을 만드는 방법과 거의 유사하게 만드는 것을 알 수 있습니다.

  1. -- 사용자 정의 테이블 데이터 형식 만들기   
  2. CREATE TYPE MemberTableType AS TABLE (   
  3.  MemID varchar(10),    
  4.  MemName nvarchar(10),    
  5.  MemMail varchar(100)   
  6. )   
  7. GO  

5) 저장 프로시저 만들기

앞의 단계에서 만든 사용자 정의 테이블 데이터 형식을 사용하는 저장 프로시저를 만듭니다. 매개변수에 @MemList MemberTableType 처럼 앞의 단계에서 만든 사용자 정의 테이블 데이터 형식을 갖는 매개 변수를 선언합니다.

  1. -- 저장 프로시저 만들기   
  2. CREATE PROCEDURE usp_InsertMember2   
  3.     @MemList MemberTableType READONLY  -- READONLY 속성을 필히 지정해야 함   
  4. AS    
  5.     SET NOCOUNT ON  
  6.     INSERT INTO Member2(MemID, MemName, MemMail)   
  7.         SELECT * FROM  @MemList  
  8.  SET NOCOUNT ON  
  9. GO  

6) 회원 정보 입력하기

앞의 단계에서 만든 저장 프로시저를 사용해 Member2 테이블에 회원 정보를 입력할 때 이제 다음과 같이 하면 됩니다. 앞에서 만든  MemberTableType 사용자 정의 테이블 데이터 형식을 갖는 변수 @MemberTVP를 선언하고 이 변수에 입력하고자 하는 회원 정보를 입력합니다. 그리고 @MemberTVP 변수를 저장 프로시저의 매개변수로 전달합니다.
이렇게 되면 @MemberTVP 변수에 입력된 여러 회원 정보가 저장 프로시저에 한번에 전달되고, 이 전달된 회원들 정보는 저장 프로시저에 의해 Member2 테이블에 입력됩니다. 결국 저장 프로시저는 한번만 호출된 결과를 가져옵니다.

  1. -- 프로시저 수행 예 (1)   
  2. DECLARE @MemberTVP AS MemberTableType   
  3. INSERT INTO @MemberTVP (MemID, MemName, MemMail)    
  4.  VALUES('jang', N'이장군''jgoon@sample.com')   
  5. INSERT INTO @MemberTVP (MemID, MemName, MemMail)    
  6.  VALUES('jane', N'박제인''jane@sample.com')   
  7. INSERT INTO @MemberTVP (MemID, MemName, MemMail)    
  8.  VALUES('tomy', N'정토미''jtomy@sample.com')   
  9. EXEC usp_InsertMember2 @MemberTVP;   
  10. GO   
  11. -- 확인   
  12. SELECT * FROM Member2   
  13. GO   
  14. /*  
  15. [결과]  
  16. MemID  MemName  MemMail  
  17. ------ -------- ---------------------------------  
  18. jane   박제인    jane@sample.com  
  19. jang   이장군    jgoon@sample.com  
  20. tomy   정토미    jtomy@sample.com  
  21. (3개 행이 영향을 받음)  
  22. */  

다음과 같은 수행도 가능합니다. 맨 앞에서 살펴본 일반적인 예에서 Member 테이블에 입력된 3명의 회원 정보를 Member2 테이블에 입력하고 싶으면 다음과 같이 수행하면 됩니다.

  1. -- 프로시저 수행 예(2)   
  2. DECLARE @MemberTVP AS MemberTableType;   
  3. INSERT INTO @MemberTVP (MemID, MemName, MemMail)    
  4.  SELECT * FROM Member   
  5. EXEC usp_InsertMember2 @MemberTVP  
  6. GO   
  7. -- 확인   
  8. SELECT * FROM Member2    
  9. GO   
  10. /*  
  11. [결과]  
  12. MemID   MemName  MemMail  
  13. ------- -------- ---------------------------------  
  14. hong    홍길동    gildong@sample.com  
  15. jane    박제인    jane@sample.com  
  16. jang    이장군    jgoon@sample.com  
  17. jrlee   이장래    jrlee@sample.com  
  18. tomy    정토미    jtomy@sample.com  
  19. (5개 행이 영향을 받음)  
  20. */  

 

이처럼 SQL Server 2008에 추가된 사용자 정의 테이블 데이터 형식은 행 집합을 저장 프로시저에 전달할 수 있어 매우 유용하게 사용될 수 있습니다. 단, 한번 만들어져서 사용 중인 사용자 정의 테이블 데이터 형식를 바꾸기 힘들고 제거하기도 힘듭니다. 물론 방법이 있지만 번거로운 작업이 됩니다. 그러므로 명확한 분석을 기반으로 디자인 되어야 합니다.

 

반응형

'연구개발 > SQL2008' 카테고리의 다른 글

문자열 검사 / 문자겸사 / 필드검사  (0) 2010.04.13
Database 수정  (0) 2010.04.11
제 7 장: 리소스 관리자  (0) 2009.06.21
제 6 장: SSMS의 디버깅 기능  (0) 2009.06.21
제 5 장: SSMS의 IntelliSense 기능  (0) 2009.06.21
반응형

MS SQL 서버는 동시에 여러 프로세스을 수행합니다. 동시에 수행되는 여러 프로세스들은 각자의 처리를 위해 제한된 자원을 가지고 경합을 벌이게 됩니다. 서버의 CPU와 메모리는 한정되어 있는 상황에서 이러한 프로세스들 끼리의 경합은 정작 중요한 프로세스가 필요한 시점에 수행되지 못하고 상대적으로 중요하지 않는 프로세스들의 수행으로 인해 예정된 시간보다 늦게 수행되는 경우가 있습니다. 예를 들어 복잡한 쿼리를 장 시간 수행해서 얻어지는 보고서 산출 시스템을 수행한 경우 갑작스런 서버 부하를 야기시켜 중요한 고객과의 응대를 위한 고객 서비스 시스템이 갑자기 느려져 업무에 방해가 될 수 있습니다.SQL Server 2008에서는 이러한 문제를 해결할 수 있는 방법으로 리소스 관리자(Resource Governor) 기능을 제공하고 있습니다. 이번 강좌에서는 리스스 관리자에 대해 살펴보도록 하겠습니다.

1. 리소스 관리자(Resource Governor)란?

리스스 관리자란 서버의 자원(CPU와 메모리를 이야기 함)을 여러 프로세스들에 대해 정해진 규칙에 따라 배정하는 리소스 관리 기능입니다. 이러한 리소스 관리를 통해서 악성 쿼리로 인해 자원이 불필요하게 낭비되는 문제를 해결할 수 있으며, 프로세스간의 중요도를 설정하여 중요한 프로세스가 우선적으로 처리되도록 할 수 있습니다. 리소스 관리는 리소스 풀, 작업 그룹, 그리고 사용자 정의 분류자 함수를 사용하여 가능하게 됩니다.

2. 리소스 풀(Resource Pool)

리소스 풀은 작업 그룹을 그룹 짓는 역할을 합니다. 리소스 풀들에 대해 각각의 리소스 풀이 사용할 수 있는 최대 CPU, 최소 CPU, 최대 메모리, 최소 메모리를 설정하게 됩니다. 자원을 많이 할당 받은 리소스 풀에 포함된 작업 그룹들은 다른 작업 그룹에 비해 상대적으로 많은 자원을 사용할 수 있게 됩니다. 리소스 풀은 기본적으로 Default 리소스 풀과 Internal 리소스 풀을 갖게 됩니다.

- Default 리소스 풀
Default 작업 그룹이 포함되는 리소스 풀입니다. 또한 사용자가 정의한 작업 그룹 중에서 리소스 풀을 배정받지 못한 작업 그룹도 Default 리소스 풀에 포함됩니다.

- Internal 리소스 풀
시스템 내부적인 처리릉 위한 모든 프로세스가 포함되는 Internal 작업 그룹이 포함되는 리소스 풀입니다.

아래 [화면1]에서는 리소스 관리자 부분에서 기본적으로 포함되어 있는 Default 리소스 풀과 Internal 리소스 풀을 확인한 예입니다.


[화면1]

리소스 관리자는 기본적으로 사용하지 않는 상태로 되어 있습니다. 리소스 관리자를 사용하기 위해서는 아래 [화면2]에서와 같이 사용 가능을 설정해 주어야 합니다. 아니면 나중에 리소스 풀을 만들 때 사용 가능하도록 설정 할 수 도 있습니다.


[화면2]

개체 탐색기에서 리소스 풀을 만드는 과정을 살펴보겠습니다. [화면3]과 같이 새 리소스 풀을 만드는 메뉴를 선택합니다.


[화면3]

그러면 아래 [화면4]에서와 같이 리소스 풀을 만드는 화면이 표시됩니다. 이 화면에서 리소스 풀 이름을 설정하고 해당 리소스 풀에 대한 CPU와 메모리에 대한 제한을 설정합니다.


[화면4]

아래 [화면5]에서는 Low Importance Pool 이라는 이름의 리소스 풀이 만들어진 모습을 보여줍니다.


[화면5]

3. 작업 그룹(Workload Group)

실제로 처리되는 프로세스를 포함하는 것이 작업 그룹입니다. 이러한 작업 그룹이 리소스 풀에 포함됩니다. SQL Server에서 처리되는 모든 프로세스는 하나의 작업 그룹에 포함되어야 합니다. 만들 특정 작업 그룹을 할당 받지 못한 경우 Default 작업 그룹에 포함됩니다. 하나의 리소스 풀에는 여러 작업 그룹들이 포함될 수 있습니다. 하지만 하나의 작업 그룹은 하나의 리소스 풀에만 포함되어야 합니다. 하나의 리소스 풀에 포함된 여러 작업 그룹은 해당 리소스 풀에 할당된 자원을 사용하게 됩니다. 또한 같은 리소스 풀에 포함된 작업 그룹들이라 하더라도 작업 그룹에 대한 중요도를 설정하면 설정된 중요도에 따라 리소스 풀에 할당된 자원 중에 사용하게 되는 자원의 크기가 달라지게 됩니다.

[화면6]에서와 같이 리소스 풀을 마우스 우측 클릭해 표시되는 단축 메뉴에서 새 작업 그룹을 만드는 메뉴를 선택하여 작업 그룹을 만들 수 있습니다. 물론 이전의 [화면4]에서 하단 부분에 작업 그룹을 바로 등록해도 됩니다.


[화면6]

[화면7]에서는 Low Importance Group 이라는 이름의 작업 그룹을 Low Importance Pool 리소스 풀에 만드는 과정을 보여줍니다. 이 화면에서 각 작업 그룹에 대한 중요도 등을 설정 할 수 있습니다.


[화면7]

[화면8]에서는 리소스 풀과 작업 그룹이 등록된 결과를 보여줍니다.


[화면8]

4. 분류자 함수(Classifier Function)

SQL Server에 처리를 위해 도달한 모든 프로세스는 특정 작업 그룹에 배정되도록 정확히 분류되어야 합니다. 이러한 역할을 담당하는 것이 분류자 함수입니다. 분류자 함수는 master 데이터베이스에 만들어져야 합니다. 그리고 여러 분류자 함수를 만들 수 있지만 사용될 수 있는 분류자 함수는 단 하나만 가능합니다. 분류자 함수에서는 프로세스에 사용된 로그인 계정, 응용 프로그램 이름, 호스트 이름 등등의 정보를 참조해 해당 프로세스를 어떤 작업 그룹에 포함시킬 지 결정하게 됩니다. 특정 작업 그룹에 포함되지 못한 프로세스는 Default 작업 그룹에 자동으로 포함되며, 결국 Default 리소스 풀에 포함되게 됩니다.

아래 스크립트는 fn_ClassftApps 라는 이름의 분류자 함수를 만드는 예를 보여줍니다. 응용 프로그램 이름을 가지고 작업 그룹을 결정하고 있음을 알 수 있습니다.

USE master
GO

CREATE FUNCTION dbo.fn_ClassifyApps()
  RETURNS sysname
  WITH SCHEMABINDING
AS
  BEGIN
    DECLARE
@ret sysname
    IF (APP_NAME() LIKE '%Low Importance Application%')
        SET @ret = 'Low Importance Group'
    RETURN @ret
  END
GO

[화면9]에서는 앞에서 만든 분류자 함수를 리소스 관리자에서 사용하도록 설정하고 있는 화면입니다.분류자 함수 이름(C) 부분에 앞에서 만든 함수 이름이 표시됨을 알 수 있습니다.


[화면9]

지금까지는 리소스 관리자를 사용하기 위해 리소스 풀과 작업 그룹, 그리고 분류자 함수를 만들고 이를 사용하도록 설정하는 방법을 살펴보았습니다. 이제는 실제 리소스 관리자를 사용해 프로세스들의 자원 사용을 제어하는 예를 보도록 하겠습니다.

5. 리소스 괸리자 사용 예

모든 작업은 개체 탐색기 대신 T-SQL을 사용하도록 하겠습니다. 반복적인 작업은 개체 탐색기보다 T-SQL이 더 효율적이라 생각합니다.

1) 리소스 관리자 사용 가능 설정 및 리소스 풀 만들기

먼저 리소스 관리자 사용을 설정하고 두 개의 리소스 풀을 만들겠습니다. PoolMarketing 리소스 풀은 영업 정보 관련된 작업을 포함할 것이며, PoolCustomer 리소스 풀은 고객 관련 중요 작업을 포함할 것이라 가정하겠습니다.

-- 1) 리소스 관리자 사용 가능 설정

ALTER RESOURCE GOVERNOR RECONFIGURE
GO

-- 2) 리소스 풀 만들기

-- 모든 매개변수는 기본값을 사용함
CREATE RESOURCE POOL PoolMarketing
CREATE RESOURCE POOL PoolCustomer

2) 작업 그룹 만들기

앞의 과정에서 만든 두 리소스 풀에 작업 그룹을 포함시키겠습니다. PoolMarketing 리소스 풀에는 GroupMarketing 작업 그룹과 GroupReport 작업 그룹이 포함됩니다. PoolCustomer 리소스 풀에는 GroupCustomer 작업 그룹만 포함됩니다.

-- 3) 작업 그룹 만들기

-- 모든 매개변수는 기본값을 사용함
CREATE WORKLOAD GROUP GroupMarketing
  USING PoolMarketing

CREATE WORKLOAD GROUP GroupReport
  USING PoolMarketing

CREATE WORKLOAD GROUP GroupCustomer
  USING PoolCustomer
GO

ALTER RESOURCE GOVERNOR RECONFIGURE;
GO

3) 로그인 계정 만들기

여러 작업들이 SQL Server에 접근 할 때 사용할 각각의 로그인 계정을 만들겠습니다. 이들 로그인 계정을 사용해 프로세스를 구분하여 특정 작업 그룹으로 배정할 것입니다.

-- 4) 로그인 계정 만들기

USE master
GO

-- 로그인 계정을 근거로 작업 그룹이 할당할 예정
CREATE LOGIN UserMarketing WITH PASSWORD = 'UserMarketingPwd', CHECK_POLICY = OFF
CREATE LOGIN UserReport WITH PASSWORD = 'UserReportPwd', CHECK_POLICY = OFF
CREATE LOGIN UserCustomer WITH PASSWORD = 'UserCustomerPwd', CHECK_POLICY = OFF
GO

4) 분류자 함수 만들기

로그인 계정을 사용해 작업 그룹을 배정하는 분류자 함수를 만들고 리소스 관리자에서 사용하도록 설정하겠습니다. 분류자 함수는 필히. master 데이터베이스에 만들어야 합니다.

USE master
GO

IF OBJECT_ID('DBO.CLASSIFIER_V1','FN') IS NOT NULL
  DROP FUNCTION DBO.CLASSIFIER_V1
GO

CREATE FUNCTION CLASSIFIER_V1 ()
  RETURNS SYSNAME WITH SCHEMABINDING
BEGIN
  DECLARE
@val varchar(32)
  SET @val = 'default'
  IF 'UserCustomer' = SUSER_SNAME()
    SET @vall = 'GroupCustomer'
  ELSE IF 'UserMarketing' = SUSER_SNAME()
    SET @val = 'GroupMarketing'
  ELSE IF 'UserReport' = SUSER_SNAME()
    SET @val = 'GroupReport'
  RETURN @val
END
GO

-- 5) 분류자 함수를 리소스 관리자에 지정

ALTER RESOURCE GOVERNOR
  WITH
(CLASSIFIER_FUNCTION = dbo.CLASSIFIER_V1)
GO

-- 6) 위 설정 내용을 리소스 관리자에 반영함

ALTER RESOURCE GOVERNOR RECONFIGURE
GO

5) 여러 작업 수행

이제 SQL Serve에 여러 작업을 수행해야 합니다. 이러한 작업들이 처음엔 자원들 평등하게 사용하지만 리소스 관리자를 사용해 자원 사용을 제어하면서 상황이 달라지게 됩니다. 강좌를 위해서 다음과 같은 내용의 배치 파일을 사용해 서로 다른 로그인 계정으로 작업이 수행되게 했습니다.

@echo off
echo "Press any key to start Marketing workload"
pause
start /MIN sqlcmd -S LONDON -U UserMarketing -P UserMarketingPwd -i "CPU intensive loop_01.sql"
echo "Press any key to start Customer workload"
pause
start /MIN sqlcmd -S LONDON -U UserCustomer -P UserCustomerPwd -i "CPU intensive loop_01.sql"
echo "Press any key to start Report workload"
pause
start /MIN sqlcmd -S LONDON -U UserReport -P UserReportPwd -i "CPU intensive loop_01.sql"

우선 처음 수행된 작업은 UserMarketing 계정으로 수행되었으므로 GroupMarketing 작업 그룹에 포함됩니다. 수행되는 작업의 모습을 Windows의 성능 모니터를 사용해 모니터링 해보았습니다. 아래 [화면10]에서 GroupMarketing 작업 그룹이 CPU 전부를 사용함을 볼 수 있습니다.


[화면10]

두 번째 수행된 작업은 UserCustomer 계정으로 수행되었으므로 GroupCustomer 작업 그룹에 포함됩니다. 아래 [화면11]에서 GroupMarketing 작업 그룹과 GroupCustomer 작업 그룹이 CPU를 절반 씩 사용함을 볼 수 있습니다.


[화면11]

세 번째 수행된 작업은 UserReport 계정으로 수행되었으므로 GroupReport 작업 그룹에 포함됩니다. 아래 [화면12]에서 GroupMarketing, GroupCustomer 그리고 GroupReport 작업 그룹이 CPU3분의 1씩 사용함을 볼 수 있습니다.


[화면12]

6) 리소스풀에 대한 자원 제한

리소스 풀에 대한 CPU 제한을 변경하여 GroupCustomer 작업 그룸이 전체 CPU 50%를 사용하도록 하겠습니다. 아래 스크립트에서 PoolMarketing 리소스 그룹의 CPU 사용량을 50%로 설정하고 있습니다.

-- 8) PoolMarketing 풀이 CPU를 50% 이상 사용하지 못하도록 설정

ALTER RESOURCE POOL PoolMarketing
  WITH (MAX_CPU_PERCENT = 50)
GO

ALTER RESOURCE GOVERNOR RECONFIGURE
GO

이렇게 되면 PoolMarketing 리소스 풀에 포함된 GroupMarketing 작업 그룹과 GroupReport 작업 그룹이 사용할 수 있는 최대 CPU 50%이므로 두 작업 그룹이 25%씩 사용하게 됩니다. 결국 GroupCuatomer 작업 그룹은 이들이 사용하지 않는 CPU 50% 를 전부 사용하게 됩니다. [화면13]은 그 결과를 보여주고 있습니다.


[화면13]

7) 작업 그룹에 대한 중요도 설정

이제는 PoolMarketing 리소스 풀에 포함된 GroupMarketing 작업 그룹과 GroupReport 작업 그룹의 중요도를 설정해 같은 리소스 풀에 포함된 작업 그룹의 자원 사용량이 달라지는 모습을 보겠습니다. 아래 스크립트에서는 GroupReport 작업 그룹의 중요도는 Low로, GroupMarketing 작업 그룹의 중요도는 High로 설정하고 있습니다.

-- 9) 그룹에 대한 중요도 설정

-- GroupAdhoc 그룹에 대한 중요도: Low
ALTER WORKLOAD GROUP GroupReport
  WITH (IMPORTANCE = Low)

-- GroupMarketing 그룹에 대한 중요도: High
ALTER WORKLOAD GROUP GroupMarketing
  WITH (IMPORTANCE = High)

-- 변경 사항 반영
ALTER RESOURCE GOVERNOR RECONFIGURE
GO

이렇게 되면 GroupMarketing 작업 그룹과 GroupReport 작업 그룹이 사용하는 CPU는 [화면14]에서 보는 것과 같이 달라지게 됩니다. 이들 두 작업 그룹이 사용하는 CPU 50%는 변함 없습니다.


[화면14]

지금까지 리소스 관리자에 대한 내용을 간략히 살펴보았습니다. 자세한 내용을 강좌를 통해서 다루기는 힘든것 같습니다. 하지만 개념은 파악이 되었을 것이라 생각됩니다.

맺음말

리소스 관리자는 많는 전문가분들이 기다려 오던 기능입니다. SQL Server 에서 수행되는 프로세스에 대한 리소스 할당을 제어 할 수 있다는 사실이 놀랍습니다. 하지만 리소스 관리자를 사용하기 위한 리소스 풀과 작업 그룹, 그리고 분류자 함수에 대한 정확한 이해를 가지지 않고 무작정 사용하려고 하면 더 큰 문제가 발생 할 수 있습니다. 그러므로 사전에 많은 계획과 테스트가 필요하다고 생각합니다. 아뭏든 리소스 관리자는 SQL Server 2008의 새로운 기능 중 대표적이라 생각됩니다.

 

반응형
반응형

이번 강좌에서는 Management Studio의 스크립트 디버깅 기능을 살펴보겠습니다. 디버깅 기능은 SQL Server 2000 버전에 등장했던 기능입니다. 하지만 SQL Server 2005 버전에서 누락이 되었습니다. 그리고 SQL Server 2008에 다시 등장한 기능인데, 예전의 기능과는 비교도 안되는 강력한(?) 기능을 가지고 있습니다. 정말 놀라운 기능이 아닐 수 없습니다. 자, 한번 볼까요?^^

1. 사전 준비 작업

우선 다음 스크립트를 수행해 몇몇 테이블과 저장프로시저와 트리거를 만들겠습니다.

USE AdventureWorks
GO

IF OBJECT_ID('T1', 'U') IS NOT NULL
   DROP TABLE T1
GO

IF OBJECT_ID('T2', 'U') IS NOT NULL
   DROP TABLE T2
GO

CREATE TABLE T1 (
   col1 int,
   col2 int
)
GO

CREATE TABLE T2 (
   col1 int,
   col2 int
)
GO

CREATE PROC TestProc01
   @col1 int
AS
   SET @col1 +=10
   DECLARE @col2 int = @col1 * 10
   EXEC TestProc02 @col1, @col2
GO

CREATE PROC TestProc02
   @col1 int,
   @col2 int
AS
   INSERT INTO T1 VALUES(@col1, @col2)
GO

CREATE TRIGGER TestTrg01
   ON T1
   AFTER INSERT
AS
   INSERT INTO T2 SELECT * FROM Inserted
GO

위 스크립트를 간단히 살펴보겠습니다. T1, T2 두 개의 테이블을 만들었습니다. TestProc01 저장 프로시저를 호출 할 떄 매개변수 값을 전달하면 이 매개변수 값이 @col1에 대인된 후 10이 더해지고, @col1 매개변수에 10을 곱한 값이 @col2에 대입됩니다. 그리고 TestProc02를 이들 두 변수를 매개변수로 하여 호출합니다. TestProc02에서는 전달 받은 매개변수 값을 T1 테이블에 INSERT 합니다. T1 테이블에는 INSERT문에 대한 트리거가 있어서 T1에 INSERT 된 내용이 T2에 그대로 INSERT 됩니다. 이렇듯 TestProc01를 호출하면 연쇄적인 처리가 진행되는 상황입니다.

이 상황에서 TestProc01을 호출하면서 어떤 단계로 처리가 수행되는지 디버깅을 해보고자 합니다.

2. 디버깅 진행

우선 아래 [화면1]에서 TestProc01에 매개변수 값으로 10을 전달하면서 호풀하는 문장을 입력했습니다.이 상태에서 디버깅(D) 메뉴에서 디버깅 시작(S)를 선택하거나 Alt+F5를 누르거나, 아니면 실행(X) 버튼 옆의 삼각형 버튼을 누르면 디버깅이 시작됩니다.


[화면1]

이제 부터는 [화면2]와 같이 화면 우측 상단의 한 단계씩 코드 실행 버튼을 누르거나 F11 키를 눌러 한 단계식 디버깅을 진행합니다.


[화면2]

아래 [화면3] 에서는 TestProc01 저장 프로시저 내부로 들어가 디버깅이 진행됨을 보여줍니다. 좌측 하단의 지역 부분에는 @col1, @col2 변수가 표시되고 이들에 항당되는 값이 보입니다. 현재는 @col1 변수에 10이 매개변수로 전달되어 대입된 모습니다. 디버깅을 진행하면 10이 더해질 겁니다. 한 단계씩 코드 실행을 계속 수행합니다.


[화면3]

아래 [화면4]에서는@col1 변수의 값에 10이 더해져 20이 되었다고 빨간색으로 값의 변경 여부를 알려줍니다. 한 단계씩 코드 실행을 계속 수행합니다.


[화면4]

아래 [화면5]에서는 TestProc02 호출 부분까지 진행이 된 상황입니다. 계속 디버깅을 진행하면 TestProc02 저장 프로시저 내부로 디버깅이 진행될 것입니다. @col2 변수는 200 이 할당되었습니다. 한 단계씩 코드 실행을 계속 수행합니다.


[화면5]

아래 [화면6]에서는 디버깅이 TestProc02 내부로 옮겨진 것을 보여줍니다. 화면 우측 하단의 호출 스택 부분에는 현재 어느 모듈 어느 부분에 대한 디버깅 중인지 표시됩니다. 아래에서 위로 읽어가면 됩니다. 한 단계씩 코드 실행을 계속 수행합니다.


[화면6]

아래 [화면7] 에서는 디버깅이 TestTrg01 트리거 안으로 이동됨을 보여줍니다. 트리거도 디버깅이 되다니 정말 놀랍습니다.


[화면7]

아래 [화면8]은 디버깅이 종료된 상태를 보여줍니다.


[화면8]

3. 고려사항

실제 업무 환경에서는 디버깅 작업을 자제하는것이 당연 할 겁니다. 원격 서버에 대한 디버깅에는 약간의 문제가 있을 수 있다고 합니다.

맺음말

SQL Server 2008에 추가된 스크립트 디버깅 기능은 상당히 강력합니다. 트리거 단계까지 디버깅이 되므로 예측 할 수 없는 데이터 변경에 대한 추적이 가능해졌습니다. 위에서 보여드린 기능이 디버깅의 전부가 아닙니다. 그 일부만을 보여준것 뿐입니다. 시스템 저장 프로시저도 디버깅이 되더군요. 이렇게 해서 지난 강좌에 이번 강좌를 통해 Management Studio의 향상된 기능을 살펴보았습니다.

 

반응형
반응형

이번 강좌에서는 Management Studio의 향상된 기능 몇가지를 살펴보겠습니다. Management Studio는 SQL Server 2005 버전부터 등장한 관리 기능과 개발 기능이 통합된 중요 도구입니다. SQL Server 2000 버전의 Enterprise Manager와 Query Analyzer를 이 도구가 대신하게 됩니다. SQL Server 2008에서도 Management Studio의 외형에는 변화가 없습니다. 하지만 편리한 기능들이 추가되어 사용자들에게 개발과 운영의 효율성을 경험하게 합니다. 이번 강좌에서는 이 기능 중에서 대표적인 IntelliSense 기능을 살펴보겠습니다.

1. IntelliSense 기능이란?

퀴리문을 작성하다 보면 다음과 같은 욕심이 생깁니다.

- SELECT 문 작성 시 테이블 목록이 보여지고 선택 할 수 있었으면 좋겠다.
- 선언된 변수 사용시 변수 목록이 보여지고 선택 할 수 있었으면 좋겠다.
- 구문에 오류가 있으면 해당 부분에 빨간색 라인이 그어저 오류를 미리 인식시켰으면 좋겠다.
- 함수 입력 시 관련된 매개 변수 정보가 표시되면서 무엇을 입려할지 알려주면 좋겠다.

등등입니다. 이러한 기능은 이미 오래전부터 Visual Studio에 도입된 것이며 이러한 기능을 IntelliSense 기능이라고 합니다. 이러한 IntelliSense 기능이 Management Studio애 도입이 된겁니다.

2. IntelliSense 예제 화면

Management Studio에서 IntelliSense 기능이 작동되는 몇가지 화면 예를 보겠습니다.

우선 아래 [화면1]에서는 USE 문을 입력 할 때 데이터베이스 목록이 자동으로 표시되면서 이 중 원하는 데이터베이스를 선택할 수 있게 하고있습니다.


[화면1]

아래 [화면2]에서는 먼저 선언된 변수의 목록이 변수에 값을 지정하는 SET문 다음에 @를 입력하니 자동으로 표시되어 이 중에서 원하는 변수를 선택하게 하고있습니다.


[화면2]

아래 [화면3] 에서는 선언되지 않는 변수 @Num01을 참조하니 빨간색 줄이 그어지면서 오류를 인식시켜줍니다. 빨간색 줄이 그어진 @Num01에 마우스를 가져가면 오류 원인이 표시됩니다. 그리고 SELECT 문의 FROM 절에 Production 스키마를 입력하고 .을 입력하니 Production 스키마에 포함된 테이블 목록이 표시되고 있습니다. 목록이 표시된 상태에서 새로운 문자를 입력하면 그 문자로 시작되는 목록으로 커서가 이동하게 됩니다.


[화면3]

아래 [화면4]에서는 DATEADD 함수를 사용하려고 함수명을 입력하고 ( 를 입력하니 필요한 매개변수의 정보가 하단에 표시되면서 무엇을 입력해야 할지 알려주고 있습니다. 매개변수 순서를 자주 틀리는 저에겐 정말 필요한 기능입니다.^^


[화면4]

아래 [화면5]에서는 오류 목록창을 표시한 예를 보여줍니다. 스크립트 창에서 오류가 있는 부분의 내용이 하단 리스트에 표시가 됩니다. 리스트에서 특정 행을 선택하면 관련 스크립트 부분으로 커서가 이동하게 됩니다.


[화면5]

3. IntelliSense 기능 비활성화 하기

만일 IntelliSense 기능이 필요가 없는 상황에서는 [화면6]과 같이 쿼리(Q) 메뉴에서 IntelliSense 사용(I) 부분을 선택해 비활성 시킬 수 있고, 필요할 때 다시 활성화 하면 됩니다. 도구(T) 메뉴의 옵션 창에서도 제어가 가능합니다.


[화면6]

4. 고려사항

IntelliSense 기능에 익숙하지 않은 상황에서는 이 기능이 불편하게 느껴질 수 있습니다. 이러한 경우는 위에서 설명한 방법으로 IntelliSense 기능을 취소하면 됩니다. 하지만 쉽게 적응이 되는 기능입니다. 이 기능에 적용하는 것이 효율적인 개발을 위해 필요하지 않을까 생각합니다.

맺음말

SQL Server 2008에 추가된 IntelliSense 기능은 저도 오래전부터 기다려왔던 기능입니다. Visual Studio에 비하면 아직 부족한 부분이 있다고 할 수 있지만 현재 제공되는 기능만으로도 저는 만족 할 수 있을것 같습니다. 테이블 목록이 표시된다거나 함수의 매개변수 정보가 표시되는 기능이 더욱 그러합니다. 다음 강좌에서는 Management Studio의 스크립트 디버깅 기능을 살펴보겠습니다.

 

반응형
반응형

본 강좌에서 다루는 내용은 Microsoft SQL Server 2008 CTP, February 2008 을 기준으로 한 것이므로 정식 버전이 출시된 경우 실제 내용과 본 강좌의 내용이 서로 다를 수 있음을 유의하시기 바랍니다.

이번 강좌에서는 SQL Server 2008에서 새로 선보인 데이터베이스 암호화(Database Encryption)에 대해 살펴보겠습니다. SQL Server 2005에서는 중요 데이터를 암호화 할 수 있는 방법이 제공되고 있습니다. 하지만 데이터를 암호화하는 함수를 INSERT문, UPDATE문에 사용해야 하고, 암호화된 데이터를 읽기 위해서는 복호화하는 함수를 SELECT문에 사용해야 합니다. 이러한 이유로 데이터를 액세스하는 응용 프로그램 수정이 불가피합니다.

하지만 SQL Server 2008의 암호화는 데이터를 액세스하는 응용 프로그램 수정이 필요가 없습니다. 데이터 암호화와 복호화를 SQL Server가 자동으로 수행해주기 때문입니다. 이런 이유로 SQL Server 2008의 암호화 방법을 투명한 데이터 암호화(Transparant Data Encryption)라고 부릅니다.

데이터베이스 암호화를 설정하기 위해서는 다음과 같은 네 단계의 작업이 수행되어야 합니다.

① 마스터 키 만들기
② 마스터키로 보호되는 인증서 만들기
③ 인증서를 사용해 DEK(Database Encryption Key:데이터베이스 암호화 키) 만들기
④ 암호화를 사용하도록 데이터베이스 설정

위 네 단계의 작업 중에서 ①과 ② 단계는 master 데이터베이스에서 수행해야 하며, ③과 ④ 단계는 해당 사용자 데이터베이스에서 수행해야 합니다. ③과 ④ 단계를 한번에 수행 할 수 있는 사용자 인터페이스가 제공됩니다.

1. 마스터 키 만들기

먼저 아래 스크립트와 같이 master 데이터베이스에서 마스터 키를 만듭니다. 이 마스터 키는 다음 단계에서 인증서를 만들 때 암호화를 위해 사용됩니다.

USE master
GO

CREATE MASTER KEY
    ENCRYPTION BY PASSWORD =
'Pa$$w0rd'
GO

2. 인증서 만들기

아래 스크립트와 같이 master 데이터베이스에서 인증서를 만듭니다. 이 인증서는 다음 단계에서 데이터베이스 암호화 키를 만드는데 사용됩니다.

USE master
GO

CREATE CERTIFICATE ServerCertificate
    WITH SUBJECT = 'Server level certificate'
GO

3. 데이터베이스 안호화 키 만들기

아래 스크립트와 같이 암호화 하고자 하는 데이터베이스(본 강좌의 경우 Adventureworks) 에서 데이터베이스 암호화 키를 만듭니다. 이때 앞의 단계에서 만든 인증서를 사용하게 됩니다.

USE AdventureWorks
GO

CREATE DATABASE ENCRYPTION KEY
    WITH ALGORITHM = AES_128
    ENCRYPTION BY SERVER CERTIFICATE ServerCertificate
GO

4. 데이터베이스 안호화 설정

아래 스크립트와 같이 해당 데이터베이스 속성에서 암호화 여부를 ALTER DATABASE 문을 사용해 설정 합니다. 데이터베이스 암호화 키가 존재하는 데이터베이스만 암호화 설정을 할 수 있습니다.

USE master
GO

ALTER DATABASE AdventureWorks
    SET ENCRYPTION ON
GO

위 3 단계와 4단계는 [화면1]과 같이 해당 데이터베이스의 단축 메뉴에서 데이터베이스 암호화 관리 메뉴를 사용하면 쉽게 할 수 있습니다.


[화면1]

[화면2]는 데이터베이스 암호화 관리 창을 보여줍니다. 암호화 알고리즘서버 인증서를 선택하고 데이터베이스 암호화 설정 부분을 체크하면 됩니다.


[화면2]

5. 데이터베이스 암호화 설정 여부 확인

데이터베이스 암호화 설정 여부는 해당 데이터베이스 속성 창에서 옵션 부분을 통해 확인할 수 있습니다. [화면3]는 암호화 사용 부분이 True로 설정되어 있는 모습을 보여줍니다. 이 값을 변경해 암호화를 취소 할 수 있습니다.


[화면3]

6. 다른 서버에서 복원 불가

일반적으로 데이터베이스 백업 파일은 다른 서버에서 쉽게 복원이 됩니다. 만일 중요한 데이터베이스 백업이 외부로 유출되었다고 가정한다면 이 유출된 백업 파일은 손쉽게 외부의 서버에서 복원되어 중요 정보가 유출되는 결과를 가져옵니다. 하지만 암호화된 데이터베이스의 백업 파일은 다른 서버에서 복원될 수 없습니다. 이를 통해 중요 데이터에 대한 심층 보안이 가능해집니다. 만일 다른 서버에서 복원이 시도되면 [화면4]와 같은 오류가 발생합니다.


[화면4]

7. 다른 서버에서 데이터베이스 파일 연결 불가

암호화된 데이터베이스 파일을 다른 서버에 복사해 연결하려고 하면 [화면5]와 같은 오류가 발생하면서 연결을 알 수 없게 됩니다.


[화면5]

8. 고려사항

암호화된 데이터베이스의 데이터 액세스는 SQL Server에 의해 자동으로 암호화 및 복호화가 수행됩니다. 이로인해 암호화 되지 않은 데이터베이스와 비교해 더 많은 CPU가 사용 될 것이며 암호화 및 복화화 단계가 수행됨으로해서 성능 역시 저하 될 수 있습니다. 그러므로 데이터베이스 암호화 정책 수립 시 이에 대한 충분한 고려가 필요합니다. 또한 암호화된 데이터베이스를 앙호화 하기 위해 데이터베이스 암호화 키를 만들때 사용된 인증서가 제대로 관리되지 않으면 정작 필요한 경우 데이터베이스 복원이 불가능할 수 있습니다. 데이터베이스 백업 정책과 함께 인증서에 대한 백업 정책도 같이 수립되어야 합니다.

맺음말

SQL Server 2008에 추가된 데이터베이스 암호화 기능은 중요한 데이터를 한층 더 안전하게 보호 할 수 있는 방법이라 생각됩니다. 하지만 이로 인해 발생하게 되는 CPU의 오버헤드와 성능 저하 가능성에 대한 부분이 해당 업무에 영향을 많이 주게 된다면 신중히 선택해야 하는 기능일 것입니다. 또한 서버 이관 및 확장 등을 위해 정책적으로 데이터베이스를 다른 서버로 옮기고자 하는 경우 다소 번거로운 과정이 필요할 수 있습니다. 이 부분도 유념하고 있어야 할 부분이라 생각합니다. 데이터베이스 암호화 방법은 데이터베이스를 통째로 암호화하는 기능입니다. 일부 데이터만을 암호화하고자 한다면 이전 버전부터 제공되는 암호화 함수를 이용해야 할 것입니다.

 

반응형
반응형

본 강좌에서 다루는 내용은 Microsoft SQL Server 2008 CTP, February 2008 을 기준으로 한 것이므로 정식 버전이 출시된 경우 실제 내용과 본 강좌의 내용이 서로 다를 수 있음을 유의하시기 바랍니다.

이번 강좌에서는 SQL Server 2008에서 새로 선보인 정책 기반 관리(Policy Based Management)에 대한 내용을 살펴보고자 합니다. 여러 대의 SQL Server를 사용하는 기업 환경에서 모든 SQL Server에 대한 일관된 관리 작업은 결코 쉽지 않습니다. 예를 들어 SQL Server의 옵션을 어떻게 설정되어야 하는지, 데이터베이스는 어떤 기준을 가지고 만들어져야 하는지 등등 다양한 정책이 기업에 존재합니다. 하지만 이러한 정책이 모든 SQL Server에 일관되게 적용되는지를 감시하고 제어하기가 쉽지 않습니다. SQL Server 2008에서는 다중 서버의 일관된 관리를 가능하게 하는 정책 기반의 관리 방법이 제공되고 있습니다.

1. 정책 관리 구성

[화면1]과 같이 Management Studio에서 관리부분을 확장하면 정책, 조건, 패싯 세 항목이 표시됩니다. 이 세 항목을 이용해 정책 기반 관리를 수행하게 됩니다.


[화면1]

2. 패싯(Facet) 확인

패싯 부분을 확장하면 [화면2]와 같이 다양한 항목이 표시됩니다.


[화면2]

패싯은 정책을 통해 관리 할 수 있는 SQL Server의 다양한 기능이라고 이야기 할 수 있습니다. 예를 들어 데이터베이스에 대한 정책을 정의 할 수 있는 데이터베이스 패싯이 있으며 테이블에 대한 정책을 정의 할 수 있는 테이블 패싯이 있습니다. [화면2]에 표시된 패싯을 더블 클릭하거나 속성을 표시해서 해당 패싯에 포함된 다양한 속성을 확인할 수 있습니다. [화면3]은 저장 프로시저 패싯의 속성을 확인한 예입니다.


[화면3]

3. 조건(Condition) 만들기

정책을 정의하기 위해서는 먼저 조건을 정의해야 합니다. 조건은 [화면2]에서 확인한 다양한 패싯 중에서 어떤 패싯에 대한 어떤 속성을 어떻게 설정 할지를 정의한 것입니다. 예를 들어 저장 프로시저이름은 'usp'로 시작되게 하겠다면 저장 프로시저 패싯의 Name 속성을 'usp'로 시작하도록 정의하는 것이 조건입니다. [화면4]와 같이 조건 항목을 우측 클릭해 새 조건(C)를 선택하여 조건을 만들 수 있습니다.


[화면4]

[화면5]는 표시된 새 조건 만들기 창에서 저장 프로시저 이름을 'usp'로 시작하도록 정의하는 화면의 예를 보여줍니다. 조건 이름저장 프로시저 이름 조건 이라고 했으며, 패싯(F) 부분에서 저장 프로시저를 선택했음을 알 수 있습니다. 또한 필드 부분에서 @Name을 선택하고 연산자LIKE를 선택했으며 에는 'usp%'를 입력해 저장 프로시저 이름이 'usp'로 시작됨을 정의한 것입니다.


[화면5]

[화면6]은 저장 프로시저 이름 조건 조건이 만들어진 결과를 보여줍니다.


[화면6]

4. 정책(Policy) 만들기

조건을 정의했으면 이제 정책을 정의하면 됩니다. 정책은 어떤 조건을 어떤 대상에 적용할 지 설정하는 것입니다. 예를 들면 저장 프로시저 이름 조건을 AdventureWorks 데이터베이스에 적용하겠다면 이에 대한 정책을 정의하면 됩니다. [화면7]과 같이 정책을 우측 클릭해 새 정책(N)을 선택하여 정책을 만들 수 있습니다.


[화면7]

[화면8]은 저장 프로시저 이름 정책을 정의하는 과정을 보여줍니다. 조건 확인(H) 부분에 적용할 조건(강좌의 경우 저장 프로시저 이름 조건) 을 선택합니다.


[화면8]

[화면9]는조건이 선택된 결과를 보여줍니다. 적용 대상(A) 부분을 보면 수준 매 Database로 표시되어 있습니다. 이는 모든 데이터베이스에 적용하겠다는 의미입니다. Every로 번역한것입니다.


[화면9]

모든 데이터베이스가 대상이 아니라면 [화면10]과 같이 새 조건을 선택해 새로운 조건을 즉시 만들어 선택할 수 있습니다.


[화면10]

[화면11]은 새 조건 만들기 창에서 AdventureWorks 데이터베이스 조건 이라는 이름의 조건을 정의하는 과정을 보여줍니다. 패싯(F) 부분에 데이터베이스 패싯을 선택했으며, @Name 필드에 대해 'AdventureWorks' 값을 지정하고 있습니다. 즉 데이터베이스 이름이 AdventureWorks인 데이터베이스만 고려하겠다는 의미입니다.


[화면11]

[화면12]는 [화면11]에서 AdventureWorks 데이터베이스 조건을 정의하고 확인 버튼을 눌러 원래의 새 정책 만들기 화면으로 돌아온 결과를 보여줍니다.


[화면12]

[화면13]은 실행 모드(E)부분에서 정의된 정책을 어떤 방법으로 실행할 지 선택하는 과정을 보여줍니다. 정의된 정책은 자동으로 실행되게 할 수도 있고 필요 시 수동으로 평가 하도록 할 수도 있습니다.


[화면13]

요청 시 실행 모드는 정책을 자동으로 실행하지 않고 필요 시 수동으로 평가하도록 하는 방법입니다. 평가에 대한 결과는 로그로 기록됩니다.

예약 시 실행 모드는 일정을 설정해 예약하면 해당 일정으로 정책에 대한 평가가 실행되도록 하는 방법입니다. 평가에 대한 결과를 로그로 기록됩니다.

변경 시 - 로그만 실행 모드는 개체에 대한 변경(생성 및 변경) 작업에 대해 실시간으로 정책이 평가되어 정책에 위배되는 경우 이에 대한 내용을 로그로 남기도록 하는 방법입니다.

변경 시 - 방지 실행 모드는 개체에 대한 변경(생성 및 변경) 작업에 대해 실시간으로 정책이 평가되어 정책에 위배되는 경우 해당 작업이 수행되지 못하도록 방지하는 방법입니다. 물론 로그도 기록됩니다.

[화면14]는 실행 모드(E) 부분에 변경 시 - 방지를 선택하고 사용(E) 부분을 체크해 실시간으로 평가되어 정책 위반 시 해당 작업이 수행되지 못하도록 하겠다고 설정한 예입니다.


[화면14]

[화면15]는 좌측의 페이지 선택에서 설명을 선택해 정책에 대한 추가적인 정보를 입력하는 화면을 보여줍니다. 이곳에 입력한 정보는 정책 위반시 표시되는 오류 메세지에 포함되어 표시됩니다. 확인 버튼을 누르면 정책 정의가 완료됩니다.


[화면15]

[화면16]은 조건과 정책이 만들어진 결과를 보여줍니다. 정책을 만드는 과정에서 정의한 AdventureWorks 데이터베이스 조건이 조건 부분에 포함되어 있음을 알 수 있습니다.


[화면16]

5. 정책 실행 여부 테스트

앞의 과정에서 만든 조건과 정책을 통해 AdventureWorks 데이터베이스에 만들어지는 모든 저장 프로시저는 'usp'로 시작되어야 합니다. 만일 이 정책에 위배되는 저장 프로시저만 만들어지면 저장 프로시저 만드는 작업은 실패하게 됩니다. [화면17]은 쿼리 창에서 AdventureWorks 데이터베이스에 GetProductInfo라는 이름의 저장 프로시저를 만드는 과정을 보여줍니다. 모든 저장 프로시저는'usp'로 시작되어야 한다는 정책에 위배되므로 오류가 표시되면서 작업이 실패 했음을 알 수 있습니다.


[화면17]

6. 수동으로 정책 평가

[화면18]과 같이 정책을 우측 클릭해 평가(V)를 선택해 해당 정책에 대한 평가를 수동으로 실행 할 수 있습니다.


[화면18]

[화면19]는 정책 평가가 실행된 결과를 보여줍니다. AdventureWorks 데이터베이스의 모든 저장 프로시저는 'usp'로 시작되어 있으므로 녹색 아이콘이 표시되고 있음을 알 수 있습니다.


[화면19]

[화면20]은 [화면19]에서 맨 아래 대상 정보 부분의 자세히 열에서 보기 링크를 클릭한 결과를 보여줍니다.


[화면20]

맺음말

이전 버전에서는 DDL 트리거를 사용해 개체에 대한 모니터링 및 제어를 할 수 있습니다. 하지만 DDL 트리거를 코딩해야 하는 번거로움을 생각하지 않을 수 없습니다. SQL Server 2008에서는 이러한 코딩에 대한 번거로움을 정책 기반 관리 기능을 통해 해소해주고 있습니다. 갈수록 증가되는 기업의 SQL Server를 정책 기반 관리를 통해 효율적으로 관리 할 수 있을 것으로 보입니다. 한 서버에서 만들어진 정책은 다른 서버, 또는 서버 그룹에 쉽게 반영 할 수 있는 정책 가져오기와 내보내기 기능이 제공되어 이러한 효율성을 더욱 증가될 것입니다. 또한 정책이 평가된 결과로 기록되는 로그를 기반으로 Agent 서버스를 이용한 Alert를 정의하여 정책에 대한 모니터링도 쉽게 할 수 있어 관리가 더더욱 쉬워질 것입니다.

 

반응형
반응형

본 강좌에서 다루는 내용은 Microsoft SQL Server 2008 CTP, February 2008 을 기준으로 한 것이므로 정식 버전이 출시된 경우 실제 내용과 본 강좌의 내용이 서로 다를 수 있음을 유의하시기 바랍니다.

이번 강좌에서는 SQL Server 2008에서 새로 선보인 구성 서버(Configuration Server)에 대한 내용을 살펴보고자 합니다. 특정 서버를 구성 서버로 등록하고 이 구성 서버에 서버 그룹들을 등록한 후 서버 그룹에 서버들을 추가해 관리하면 좀더 효율적인 서버 관리가 가능해집니다. 기본적으로 제공되는 로컬 서버 그룹과 형태가 비슷해 보이지만 로컬 서버 그룹은 Windows 사용자 계정에 종속된 기능이어서 Windows 사용자 간에 공유 될 수 없는 그룹 정보입니다. 즉 Windows 사용자가 개별적으로 등록해 사용해야 하는 그룹 정보입니다. 하지만 구성 서버는 서버 그룹에 대한 정보가 구성 서버의 msdb 시스템 데이터베이스에 저장되어 권한이 있는 사용자가 같은 서버 그룹 정보를 공유 할 수 있습니다.

1. 구성 서버 등록

우선 Management Studio에서 등록된 서버창을 표시합니다. 만일 등록된 서버창이 보이지 않으면 Ctrl + Alt + G를 누르거나 보기(V) 메뉴에서 Registered Servers를 선택합니다. [화면1]과 같이 구성 서버를 우측 클릭 한 후 구성 서버 등록(C)를 선택합니다.


[화면1]

잠시 후 [화면2]와 같이 새 서버 등록 창이 표시되면 구성 서버로 사용할 서버 이름을 서버 이름(S) 부분에 입력하고 인증 방법(A)을 선택합니다. 만일 인증 방법을 SQL Server 인증으로 선택했으면 적절한 계정과 암호를 입력합니다. 등록된 서버 이름(N)에 화면에 표시될 이름을 입력합니다. 실제 서버 이름과 달라도 됩니다. 이 강좌에서는 LONDON 서버를 구성 서버로 선택 했으며, Windows 인증을 선택 하고 등록된 서버 이름을 변경하지 않았습니다. 저장(V) 버튼을 눌로 설정을 완료합니다.


[화면2]

[화면3]에서는 LONDON 서버가 구성 서버로 등록된 결과 화면을 보여줍니다.


[화면3]

2. 서버 그룹 등록

구성 서버에서 관리할 서버를 직접 등록해도 되지만 좀더 효율적인 관리를 위햐서는 우선 서버 그룹을 등록 한 후 이 서버 그룹에 서버를 등록하도록 합니다. [화면4]와 같이 구성 서버를 우측 클릭 한 후 새 서버 그룹(G)를 선택합니다.


[화면4]

잠시후 [화면5]와 같이 새 서버 그룹 속성 창이 표시되면 그룹 이름(N) 부분과 그룹 설명(D) 부분에 정보를 입력합니다. 그룹 이름은 되도록 의미 있는 이름을 입력하도록 합니다. 이 강좌에서는 그룹 이름을 MyGroup이라고 입력했으며 설명은 입력하지 않았습니다. 확인 버튼을 눌러 입력을 완료합니다.


[화면5]

[화면6]은 MyGroup 서버 그룹이 입력된 결과를 보여줍니다.


[화면6]

3. 서버 그룹에 서버 등록

자, 이제 서버 그룹에 서버를 등록합니다. [화면7]과 같이 등록된 서버 그룹을 우측 클릭해 새 서버 등록(S)를 선택합니다.


[화면7]

잠시 후 [화면8]과 같이 새 서버 등록 창이 표시되면 추가할 서버 정보를 입력합니다. 단 인증(A) 부분을 변경 할 수 없습니다. Windows 인증만 사용할 수 있습니다. 이 강좌에서는 서버 이름(S)에 로컬에 설치된 명명된 인스턴스인 LONDON\SQLINSTANCE2 를 입력했습니다. 실제 업무에서는 다른 곳에 위치한 실 서버가 될 것입니다. 정보를 입력 한 후 저장(V) 버튼을 눌러 등록을 완료합니다.


[화면8]

[화면9]는 MyGroup 서버 그룹에 LONDON\SQLINSTANCE2, LONDON\SQLINSTANCE3 두 개의 명명된 인스턴스를 등록한 결과를 보여줍니다.


[화면9]

구성 서버를 사용할 권한이 있는 사용자가 Management Studio에서 구성 서버에 LONDON을 동록하면 [화면9]와 같은 동일한 그룹 정보를 사용하게 됩니다. 또한 권한이 있는 사용자(sysadmin 역할 멤버 또는 ServerGroupAdministratorRole 역할 멤버)는 구성 서버에 포함된 그룹 정보를 수정 할 수도 있습니다. 이러한 그룹 관련 정보 공유가 구성 서버가 주는 가장 큰 이점이라 할 수 있습니다.

4. 다중 서버 쿼리 수행

SQL Server 2008에서는 구성 서버 기능과 함께 다중 서버 쿼리 기능을 선보이고 있습니다. 여러 서버를 대상으로 동일한 쿼리를 동시에 수행하는 기능입니다. 다중 서버 쿼리 기능은 로컬 서버 그룹에 대해서도 수행 할 수 있는 기능입니다. 구성 서버만을 위한 기능이 아닙니다.

[화면10]과 같이 쿼리를 수행할 그룹을 우측 클릭 한 후 새 쿼리(D)를 선택합니다.


[화면10]

[화면11]과 같이 쿼리 창이 표시됩니다. 그리고 해당 쿼리 창의 아래 부분의 상태 표시줄의 색깔이 분홍색으로 표시됨으로써 다중 쿼리 창임을 알려줍니다. 옵션 변경을 통해 색상을 바꿀 수도 있습니다.다중 쿼리를 테스트 하기 위해 [화면11]과 같이 쿼리 창에 쿼리문을 입력합니다. 이 강좌에서는

SELECT * FROM sys.databases

를 입력해 수행해 보았습니다. 결과 창에 등록 된 두 서버에서 가져온 결과가 통합되어 표시됨을 알 수 있습니다. 그리고 표시된 결과 집합에 서버 이름 열이 추가되어 표시됨을 알 수 있습니다.


[화면11]

다중 서버 쿼리의 결과 집합이 표시되는 형태를 변경 할 수 있습니다. [화면12]와 같이 도구(T) 메뉴에서 옵션(O) 메뉴를 선택합니다.


[화면12]

잠시 후 [화면13]과 같이 옵션 변경 창이 표시됩니다. 좌측의 트리에서 쿼리 결과 - SQL Server - 다중 서버 결과를 선택합니다. 그리고 우측에 표시되는 세 가지 항목에 대해 원하는 값을 선택하면 됩니다. [화면13]은 기본적으로 설정된 값을 보여줍니다.


[화면13]

기능을 테스트 하기 위해 [화면14]와 같이 항목의 값을 변경해 보았습니다. 결과 병합False로, 결과에 로그인 이름 추가True로 변경하였습니다. 그리고 앞의 과정에서 수행한 쿼리를 다시 수행해 보았습니다.


[화면14]

[화면15]와 같이 결과 창에 표시된 형태가 바뀌었습니다. 두 서버에서 가져온 결과가 분리되어 표시되었으며 로그인 열이 추가되어 표시됨을 알 수 있습니다.


[화면15]

맺음말

여러 서버를 대상으로 관리 업무를 진행해야 하는 경우 구성 서버는 편리함을 제공합니다. 기본의 로컬 서버 그룹 기능을 활용 할 수 있지만 서버 그룹을 공유 할 수 있는 구성 서버 기능을 사용하는게 좋을 것으로 생각됩니다. 그리고 구성 서버에 등록된 서버에 대한 연결은 Windows 인증만 가능하므로 인증 정보가 별도로 관리되지 않습니다. 또한 모든 사용자가 사용할 수 있는게 아니라 msdb에 포함된 ServerGroupAdministratorRoleServerGroupReaderRole에 속한 사용자만 구성 서버를 괸리 하거나 사용 할 수 있어 보안적인 부분까지 고려됨을 알 수 있습니다.

 

반응형

+ Recent posts

반응형