SQL의 구문분석 절차

1. 동일한 SQL의 수행여부 확인

2. 문법/절차 확인

3. SEMANTIC확인

4. 권한 확인


1. 동일한 SQL의 수행여부확인

 - Soft Parsing : 동일한 SQL이 이전에 수행되었고, 실행계획등의 실행정보가 메모리에 저장되어있다.

 - Hard Parsing : 실행정보가 존재하지않아 구문 분석을 새로 시작해야한다.


2. 문법/ 절차 확인

 - 문법이 맞는지 확인. 틀리면 SYNTAX 에러 메시지 발생


3. SEMANTIC

 - Database Resolution단계, SQL에 사용된 테이블과 테이블 컬럼이 실제 DB에 존재하는지 확인


4. 권한 확인

  - 해당 SQL을 실행 할수 있는지 권한 확인


Query Transformation

- 구문 분석을 수행하는 중간단계에서 SQL을 변경하는 단계(Query Transformation)가 발생
- 옵티마이저가 단독으로 수행하는 과정
- 결과적으로 Query Tranformation은 처리범위를 감소시키는 조건을 찾아내어 처리범위를 감소시키는(SQL의 성능을 최적화 하기위한) 옵티마이저의 노력

- Transitivity
- View Merging
- Sub query Merging
- Or Expansion
- Query Rewrite

Transitivity

- 논리적으로 이상이 없는 조건을 추가하여 처리범위를 감소시켜 성능을 최적화 하고자 하는 옵티마이저의 노력
- WHERE절에 존재하는 A테이블의 상수 조건에 대해 논리적으로 타당하다면 B테이블에도 해당 상수 조건을 옵티마이저가 추가시켜 주는것

특징

- 점(=) 조건은 가능
- 선분 조건은 불가능
- 조인 조건은 불가능

View Merging

- 말그대로 뷰가 머징되는것
- Query Transformation에서 중요요소이다
- 뷰머징은 말로 표현하기 힘들정도로 심하게 발생한다.
- 인라인뷰는 주 쿼리의 조건을 받으므로, 처리범위를 더욱 감소시킬수 있게된다. 물론 인라인뷰의 추가된 조건이 해당 테이블의 인덱스로 걸려있어야만 처리범위가 감소하게 된다.

View Merging 종류

- 주 쿼리의 조건(WHERE)이 인라인 뷰 안으로 삽인 되는 뷰 머징
- 인라인 뷰 또는 뷰의 SQL이 주 쿼리로 합쳐지는 뷰머징

인라인뷰

인라인 뷰에서 괄호는 표현한부분 부터 연산을 수행하는 수학적인 괄호가 아니다. 단지 문법적인 구분의 역할만을 수행하게 된다. 그렇기 때문에 괄호를 사용하는 인라인 뷰는 언제든 변할 수있게 된다.

인라인뷰의 종류

- Mergeable 인라인 뷰 : 뷰가 해체되거나 뷰 안으로 조건이 삽입되는 인라인 뷰
- 주 쿼리의 조건(WHERE)이 인라인 뷰 안으로 삽인 되는 뷰
- 인라인 뷰 또는 뷰의 SQL이 주 쿼리로 합쳐지는 뷰
- Non-Mergeable인라인 뷰 : 뷰가 수학의 괄호처럼 별도로 수행되는 뷰

Non-Mergeable인라인 뷰확인하기

- UNION ALL
- UNION
- DISTINCT
- GROUP BY
- ROWNUM
- 집합 함수

Non-Mergeable 인라인뷰가 될 가능성이 높다.
완벽하게 확인하기 위해서는 SQL의 실행계획을 확인해보아야 한다.






'SQL > SQL 튜닝' 카테고리의 다른 글

클러스터인덱스와 넌클러스터인덱스  (0) 2017.10.24
Nested Loop, Sort Merge, Hash JOIN  (0) 2017.10.23
INDEX 튜닝  (0) 2017.10.23
쿼리튜닝팁  (0) 2017.09.20
쿼리실행순서  (0) 2017.09.01

SQL의 성능은 처리범위에 좌우된다.

이를 인덱스를 잘 설정하면 처리범위를 최소화 할 수 있게 되고, 결국 성능을 향상시킬수 있다.

0. 인덱스를 거는 이유

1. 처리범위의 양을 줄이기위해

2. 랜덤액세스의 양을 줄이기위해

3. 정렬횟수를 줄이기위해

1. 처리범위의 양을 줄이기위해

WHERE 절에서 점조건( IN , =  연산자를 이용한 조건)을 먼저 기술하고, 다름 선분 조건(LIKE, BETWEEN, <, > 등과 같이 점 조건을 제외한 연산자)를 기술 하는것이 처리범위를 감소시키는 방법이다.

단일인덱스 뿐만 아니라 결합컬럼인덱스를 사용하면 처리범위를 더욱 감소시킬수 있다. 결합인덱스를 사용할때 주의할 점은 점 조건과 선분 조건의 순서에 의해서 처리범위가 변한다는 것이다.

1.1. 결합인덱스를 구성하는 컬럼의 순서

1순위 : 컬럼이 사용한 연산자에 의한 인덱스 컬럼 선정
2순위 : 랜덤 액세스를 고려한 인덱스 컬럼 선정
3순위 : 정렬 제거를 위한 인덱스 컬럼 선정
4순위 : 단일 컬럼의 분포도를 고려한 인덱스 컬럼 선정

위의 규칙이 생긴 이유는 디스크 I/O를 최소화하는 가장 최적의 방법이기 때문이다.

2. 랜덤 액세스의 양을 줄이기위해

랜덤 액세스란, 데이터를 저장하는 블록을 한번의 I/O에 대해 여러번 액세스 하는 것이 아니라 한번의 I/O에 대해 하나의 블록만을 액세스 하는방식을 말한다.

레코드간 논리적, 물리적인 순서를 따르지 않고, 한건을 읽기 위해 한 블록씩 접근하는 방식

데이터를 추출하기 위해 한번의 I/O에 대해 여러개의 블록을 액세스 한다면 같은 양의 데이터에 대해 적은 횟수의 디스크 I/O가 발생하기 때문에 성능이 향상 될 수있다.

테이블을 처음부터 끝까지 액세스하는 테이블 전체스캔(Table Full Scan)의 경우에는 한 번의 I/O에 대해 여러 개의 블록을 액세스할 수 있기 때문에 한 번에 여러 블록을 액세스하는 다중 블록 I/O를 수행하게 된다.

2-1. 랜덤 액세스의 종류

2.1.1. 확인 랜덤 액세스 

WHERE, HAVING조건의 컬럼이 인덱스에 존재하지 않아 발생하는 랜덤 액세스

- 점조건, 선분조건으로 인덱스를 걸어준다.


2.1.2 추출 랜덤 액세스 

SELECT절의 컬럼이 인덱스에 존재하지 않아 발생하는 랜덤 액세스
- 매우 자주 사용하는 SQL에 대해서는 추출 랜덤 액세스 제거 고려
- 하나의 인덱스로 많은 SQL에 대해 추출 랜덤 액세스를 제거할 수 있다면 컬럼이 많더라도 인덱스에 컬럼 추가 고려
- 인라인 뷰를 통해 데이터가 감소하는 경우 ROWID 이용하여 추출 랜덤 액세스 감소 고려

※ ROWID?
행을 찾아가는 가장 빠른 방법
인덱스의 활용과도 연관성이 있다.
인덱스는 인덱스 컬럼과 rowid를 가지고 있다.

2.1.3 정렬 랜덤 액세스

ORDER BY, GROUP BY절에 사용될 컬럼이 존재하지 않아 발생하는 랜덤 액세스
- ORDER BY나 GROUP BY절에 있는 컬럼을 인덱스에 추가 해야한다.

2.1.4 요약

종류발생 위치

추출된 데이터의 갯수

확인 랜덤 액세스

WHERE 절/HAVING 절

감소 또는 동일
정렬 랜덤 액세스

ORDER BY 절/GROUP BY 절

동일
추출 랜덤 액세스

SELECT 절

동일
정렬 랜덤액세스나 추출랜덤액세스는 랜덤액세스의 횟수와 추출되는 데이터 건수에는 변화가 없다. 하지만, 확인 랜덤 액세스는 추출되는 데이터의 건수가 감소될수있다(WHERE문 실행후 데이터 필터링되기 때문에)

그러므로, 가장먼저 확인 랜덤 액세스를 줄이는 방법을 고려해야한다.

3. 정렬횟수를 줄이기위해


ORDER BY를 사용하지 않더라도, 인덱스에 의해서 정렬이 되기 때문에, 인덱스를 잘 활용하면 정렬의 양을 줄일수 있다.

DBMS에서 SQL의 정렬을 제거할 수 있다면, SQL의 단순화와 성능 향상이라는 매우 큰 혜택을 얻을 것이다.


테이블 - 데이터가 INSERT되는 순서에 의해 저장되므로 어떤 컬럼에 의해 정렬된 데이터가 저장되지않는다.


인덱스 - 인덱스를 구성하는 컬럼에 의해 정렬된 데이터가 저장된다. 인덱스의 첫번째 컬럼에 의해 정렬되며, 첫번째 컬럼의 값이 동일한 데이터에 대해서는 인덱스의 두번째 컬럼에 의해 정렬된다.



인덱스 조건 : 점조건~ ORDER BY절의 컬럼순서대로 ~ 선분조건~



출처 : http://www.gurubee.net/expert/kwontra


'SQL > SQL 튜닝' 카테고리의 다른 글

클러스터인덱스와 넌클러스터인덱스  (0) 2017.10.24
Nested Loop, Sort Merge, Hash JOIN  (0) 2017.10.23
옵티마이저 이해하기 - 1  (0) 2017.10.23
쿼리튜닝팁  (0) 2017.09.20
쿼리실행순서  (0) 2017.09.01

PL/SQL이란?

PL/SQL 은 Oracle’s Procedural Language extension to SQL 의 약자 이다.

- SQL문장에서 변수정의, 조건처리(IF), 반복처리(LOOP, WHILE, FOR)등을 지원하며,오라클 자체에 내장되어 있는 Procedure Language 이다.

DECLARE문을 이용하여 정의되며, 선언문의 사용은 선택 사항 이다.

PL/SQL 문은 블록 구조로 되어 있고 PL/SQL자신이 컴파일 엔진을 가지고 있다.

PL/SQL의 장점

PL/SQL 문은 BLOCK 구조로 다수의 SQL 문을 한번에 ORACLE DB로 보내서 처리하므로 수행속도를 향상 시킬수 있다.

PL/SQL 의 모든 요소는 하나 또는 두개이상의 블록으로 구성하여 모듈화가 가능하다.

- 보다 강력한 프로그램을 작성하기 위해서 큰 블록안에 소블럭을 위치시킬 수 있다.

VARIABLE, CONSTANT, CURSOR, EXCEPTION을 정의하고, SQL문장과 Procedural 문장에서 사용 한다.

- 단순, 복잡한 데이터 형태의 변수를 선언 한다.

- 테이블의 데이터 구조와 컬럼명에 준하여 동적으로 변수를 선언 할 수 있다.

EXCEPTION 처리 루틴을 이용하여 Oracle Server Error를 처리 한다.

- 사용자 정의 에러를 선언하고 EXCEPTION 처리 루틴으로 처리 가능 하다.



PL/SQL Block Structure

  PL/SQL은 프로그램을 논리적인 블록으로 나누는 구조화 된 블록 언어 이다.

  PL/SQL 블록은 선언부(선택적), 실행부(필수적), 예외 처리부(선택적)로 구성되어 있고, BEGIN과 END 키워드는 반드시 기술해 주어야 한다.

  PL/SQL 블록에서 사용하는 변수는 블록에 대해 논리적으로 선언할 수 있고 사용할 수 있다.

PL/SQL Block Structure
  • DECLARE
    • - Optional
    • - Variables, cursors, user-defined exceptions
  • BEGIN
    • - Mandatory
    • - SQL Statements
    • - PL/SQL Statements
  • EXCEPTION
    • - Actions to perform when errors occur
  • END
    • - Mandatory

Declarative Section(선언부)
  • - 변수, 상수, CURSOR, USER_DEFINE Exception 선언

Executable Section(실행부)
  • - SQL, 반복분, 조건문 실행
  • - 실행부는 BEGIN으로 시작하고 END로 종료된다.
  • - 실행문은 프로그램 내용이 들어가는 부분으로서 필수적으로 사용되어야 한다.

Exception Handling Section(예외처리)
  • - 예외에 대한 처리.
  • - 일반적으로 오류를 정의하고 처리하는 부분으로 선택 사항이다.

PL/SQL 프로그램의 작성 요령

- PL/SQL 블록내에서는 한 문장이 종료할 때마다 세미콜론(;)을 사용 한다.

END뒤에 세미콜론(;)을 사용하여 하나의 블록이 끝났다는 것을 명시 한다.

- PL/SQL 블록의 작성은 편집기를 통해 파일로 작성할 수도 있고, SQL 프롬프트에서 바로 작성 할 수도 있다.

- SQL*PLUS 환경에서는 DECLARE나 BEGIN이라는 키워드로 PL/SQL블럭이 시작하는 것을 알 수 있다.

- 단일행 주석 : --

- 여러행 주석 : /* */

- PL/SQL 블록은 행에 / 가 있으면 종결 된다.



PL/SQL 블럭의 유형

PL/SQL Anonymous BlockPL/SQL Procedure BlockPL/SQL Function Block
[ Anonymous ][ Procedure ][ Function ]

Anonymous Block (익명 블록)

  이름이 없는 블록을 의미 하며, 실행하기 위해 프로그램 안에서 선언 되고 실행시에 실행을 위해 PL/SQL 엔진으로 전달 된다.

  선행 컴파일러 프로그램과 SQL*Plus 또는 서버 관리자에서 익명의 블록을 내장 할 수 있다.

Procedure (프로시저)

  특정 작업을 수행할수 있는 이름이 있는 PL/SQL 블록으로서, 매개 변수를 받을수 있고, 반복적으로 사용할수 있다.

  보통 연속 실행 또는 구현이 복잡한 트랜잭션을 수행하는 PL/SQL블록을 데이터베이스에 저장하기 위해 생성 한다.

Function (함수)

  보통 값을 계산하고 결과값을 반환하기 위해서 함수를 많이 사용 한다.

  대부분 구성이 프로시저와 유사하지만 IN 파라미터만 사용 할 수 있고, 반드시 반환 될 값의 데이터 타입을 RETURN문에 선언해야 한다.

  또한 PL/SQL블록 내에서 RETURN문을 통해서 반드시 값을 반환 해야 한다.




출처 : http://www.gurubee.net/lecture/1039, http://www.gurubee.net/lecture/1342, http://www.gurubee.net/lecture/1343

'SQL > PL_SQL' 카테고리의 다른 글

ORA와 SQL 차이  (0) 2017.10.21

기본 문법

1
2
3
4
5
6
7
8
9
10
11
12
13
14
CREATE OR REPLACE PROCEDURE 프로시저명
(
  변수명         IN   VARCHAR2,
  RC1    OUT SYS_REFCURSOR 
)
IS
 
BEGIN

/*DO SOMETHING*/ 

OPEN RC1 FOR
SELECT * fROM 테이블


EXCEPTION WHEN OTHERS THEN
    RAISE_APPLICATION_ERROR (-20001'프로시저명' || SQLERRM);
 
END 프로시저명;
/
cs


요약설명

REF CURSOR

 - 오라클 9i 하위 버전에 지원하던 커서.

   해당 커서를 사용하기 위해서는 패키지를 선언하고, 패지키에서 선언된,

   REF CURSOR 타입의 인자를 출력 파라메터로 받는 프로시저를 작성해야 함.

   프로시저 내부에서 패키지 작성없이 직접 해당 커서 타입을 이용하는 것은 불가.

 

SYS_REFCURSOR

 - 오라클 9i 버전부터 지원하는 커서.

   이전버전에 지원하던 REF CURSOR의 불편함을 개선한 커서이다.

   하위 버전에서 커서를 반환하기 위해 패키지를 생성해야 했으나, 

   해당 커서의 추가로 인해 패키지 작성없이 프로시저에서 바로 커서를 반환할 수 있게 되었다. 

- 커서를 반환함으로써 SELECT 프로시저를 사용할때 결과테이블을 반환 받을 수 있게 된다.



예외처리

EXCEPTION

WHEN 예외 THEN  ###

WHEN OTHERS THEN @@@


RAISE_APPLICATION_ERROR(사용자 정의 예외)

오류코드 -20000 ~ -20999범위내에서 사용자가 정의한 오류를 실행할수 있음


SQLCODE, SQLERRM

SQLCODE는 실행부에서 발생한 예외에 해당하는 코드를 반환한다. 예를 들어, 0으로 나누면 이에 대한 예외 코드인 ‘-1476’을 반환한다. 만약 에러 없이 성공하면 ‘0’을 반환한다.

SQLERRM은 발생한 예외에 대한 오류 메시지를 반환한다. 이 함수는 매개변수로 예외코드 값을 받는데, 매개변수를 넘기지 않으면 디폴트로 SQLCODE가 반환한 예외코드 값과 연관된 예외 메시지를 반환한다. 

SQLERRM(SQLCODE)로 사용하기도한다.


차이점

MS-SQL

Oracle

CREATE PROCEDURE

CREATE PROCEDURE

ALTER PROCEDURE

CREATE OR REPLACE PROCEDURE

변수 NVARCHAR(20)

변수 IN VARCHAR2

SQL 마지막에 ; 없어도됨

SQL 마지막에  ; 부붙이기

'문자열' + '합치기'

'문자열' || '합치기

END

END; (프로시저의 마지막은 반드시 ;(세미콜론)

DECLERE V_VAR NVARCHAR(20)

V_VAR VARCHAR2(20)

프로시저 안에서는 명시하지않아도 되지만,

프로시저 밖에서는 익명블록으로 만들어야한다.

(DECLARE 변수

 BEGIN

  변수 := @@@;

 END)

CONVERT(SYSDATE(), 112);

TO_DATE(SYSDATE, 'YYYY.MM.DD')

TO_CHAR(SYSDATE, 'YYYY.MM.DD')

MS-SQL의 CAST, CONVERT가 Oracle에서 TO_DATE, TO_CHAR이라고 생각하면된다.

ISNULL('', 0)

NVL('', 0)

EXEC UP_HR_HUANEXPEN_ACCEPT 

파라미터1, 파라미터2, 파라미터3

UP_HR_HUANEXPEN_ACCEPT(파라미터1, 파라미터2, 파라미터3)

프로시저 내에서 다른 프로시저 실행시, EXEC를 명시하지 않아도 된다.

SELECT @COMPANY = COMPANY

  FROM TBL

SELECT COMPANY

   INTO V_COMPANY

  FROM TBL

변수에 값을 대입할때 위처럼 SELECT문 뒤에 INTO를 사용한다.

@P_CD_COMPANY = '1000'

V_CD_COMPANY := '1000' 

(Oracle에서 변수에 값을 대입하려면 := 를 사용한다.)

'' 과 NULL

 둘다 NULL로 인식

 임시테이블 #temp

오라클에서는 임시테이릅을 만들기 상당히 까다롭다

그래서 일반 테이블을 만들어놓고 임시테이블 처럼 사용하기도 한다. 

 PRINT

 DBMS_OUTPUT.PUT_LINE()


'SQL > PL_SQL' 카테고리의 다른 글

PL/SQL 이란  (0) 2017.10.21

SQL Server 2008, SQL Server 2012

 

날짜(Date) 형식의 값을 특정 문자(String) 포맷(Format)으로 바꾸는 방법을 소개한다.

아래는 자주 사용하는 YYYY-MM-DD HH24:MI:SS 으로 변경하는 방법이다.

 

CONVERT( [포맷(길이)], [날짜 값], [변환형식] )

 

select convert(varchar, getdate(), 120)

--결과 : 2014-12-12 19:21:36

;

select convert(varchar(10), getdate(), 120)

--결과 : 2014-12-12

;

select convert(varchar(16), getdate(), 120)

--결과 : 2014-12-12 19:21

 


 

[이외의 날짜 변환형식]

select convert(varchar, getdate(), 100)  --mon dd yyyy hh:miAM (or PM)

select convert(varchar, getdate(), 101)  --mm/dd/yyyy

select convert(varchar, getdate(), 102)  --yyyy.mm.dd

select convert(varchar, getdate(), 103)  --dd/mm/yyyy

select convert(varchar, getdate(), 104)  --dd.mm.yyyy

select convert(varchar, getdate(), 105)  --dd-mm-yyyy

select convert(varchar, getdate(), 106)  --dd mon yyyy

select convert(varchar, getdate(), 107)  --Mon dd, yyyy

select convert(varchar, getdate(), 108)  --hh:mm:ss

select convert(varchar, getdate(), 109)  --mon dd yyyy hh:mi:ss:mmmAM (or PM)

select convert(varchar, getdate(), 110)  --mm-dd-yyyy

select convert(varchar, getdate(), 111)  --yyyy/mm/dd

select convert(varchar, getdate(), 112)  --yyyymmdd

select convert(varchar, getdate(), 113)  --mon yyyy hh:mi:ss:mmm (24h)

select convert(varchar, getdate(), 114)  --hh:mi:ss:mmm (24h)

select convert(varchar, getdate(), 120)  --yyyy-mm-dd hh:mi:ss (24h)

select convert(varchar, getdate(), 121)  --yyyy-mm-dd hh:mi:ss.mmm (24h)

select convert(varchar, getdate(), 126)  --yyyy-mm-ddThh:mi:ss.mmm

select convert(varchar, getdate(), 130)  --dd mon yyyy hh:mi:ss:mmmAM

select convert(varchar, getdate(), 131)  --dd/mm/yyyy hh:mi:ss:mmmAM



출처: http://gent.tistory.com/35 [젠트의 프로그래밍 세상]

'SQL > MS-SQL' 카테고리의 다른 글

단위처리  (0) 2017.11.02
PIVOT/ UNPIVOT  (0) 2017.09.21
제약 조건(Constraint) #2 (기존 테이블에 추가/삭제)  (0) 2017.09.20
제약 조건(Constraint) #1 (의미와 설정)  (0) 2017.09.20
테이블변수  (0) 2017.09.20

PIVOT

PIVOT의 사전적 의미는 축을 중심으로 회전(선회축)한다는 의미이다.

SQL적으로 보면 컬럼을 반시계 방향으로 회전해서 각각의 컬럼으로 사용하겠다는 것을 뜻한다.


그림으로  표현하면 이렇지 않을까?ㅋㅋㅋ


피벗테이블을 사용하는 이유는 다음과 같다.


데이터를 요약, 분석, 탐샙 및 표시하기 위해서

필요한 데이터만 뽑아서 새롭게 테이블을 작성하기 위해서

목록을 단순한 형태로 요약하기 위해서


위의 예는 엑셀에서 피벗테이블을 사용하는 방법인데 SQL에서도 이유는 비슷할것이다.

피벗 문법에서 집계함수를 사용하는것도 이와 같은 이유때문이다.

문법

PIVOT 

집계함수(컬럼명) --오른쪽으로 회전할 컬럼명(같은 그룹끼리 집계)

FOR [위로 올릴 컬럼] IN (FOR에서 사용한컬럼명중 사용할 컬럼 도메인목록) 

) AS 피벗 Alias


집계함수를 사용하는 이유는 위에서 설명했다.

FOR_IN_구문은 FOR은 회전시킬 컬럼 IN 회전시킬 컬럼 도메인정도로 이해하면 된다.

이렇게 회전을 하게되면 집계함수 값들은 한 행이될것이고, 그 이외의 값은 자동으로 GROUP BY되어서 하나의 행이 만들어 진다.



UNPIVOT은 그 반대가 된다. 선택한 컬럼명들이 반대로 회전하겠지? 


UNPIVOT 

컬럼명 --왼쪽으로 회전시킨 컬럼들을 이름 지을 컬럼명

FOR [오른쪽으로 회전한후 이름지을 컬럼명]

IN (회전시킬 컬럼들) 

) AS 피벗 Alias


이제 예를 들어서 살펴보자.

예제

다음과 같은 임시테이블을 사용할 것이다.

이 테이블에서 월별로 합을 구한뒤 이를 PIVOT과 UNPIVOT으로 회전시켜볼것이다.


SUM을 구해보자



이제 PIVOT을 통하여 테이블을 회전시켜 보자


결과는 다음과 같이 나온다. MONTH의 열에 있는 값을 컬럼으로 올리고, SUM을 각 열로 둠으로써 테이블을 회전시켰다.



다음으로 UNPIVOT을 사용하여 한번더 회전 시켜보도록하자.



이처럼 테이블을 시계방향으로 계속돌리면 결과적으론 컬럼이 반대로 나온다.


PIVOT과 UNPIVOT을 동시에 사용할 일을 잘 없지만, 예제를 위해서 작성해 보았다.




참조링크 (동적쿼리와, PIVOT)

'SQL > MS-SQL' 카테고리의 다른 글

단위처리  (0) 2017.11.02
Datetime Fomat  (0) 2017.10.11
제약 조건(Constraint) #2 (기존 테이블에 추가/삭제)  (0) 2017.09.20
제약 조건(Constraint) #1 (의미와 설정)  (0) 2017.09.20
테이블변수  (0) 2017.09.20

0. 들어가기에 앞서...


각 제약조건의 의미와 테이블 생성시 제약조건을 설정하는 방법들에 대해선 "제약 조건 #1" 문서를 참고하자.

위 문서에서 SSMS를 이용하여, 제약조건을 설정하는 방법은 테이블을 만들고 나서도 자유롭게 추가/삭제가 가능하다.
하지만, 위 문서의 T-SQL을 이용한 방법은 테이블을 생성할 때의 내용뿐이었다.

이제 T-SQL을 이용하여 이미 존재하는 테이블, 그것도 대량의 데이터가 들어있는 테이블에 대해
새로이 제약조건을 추가하거나, 기존의 제약조건을 변경, 또는 삭제하는 방법을 알아보도록 하자.

기존의 테이블을 변경하는 기본 구문은 ALTER TABLE이며, 이에 대해선 이 챕터에서 자세히 설명하진 않는다.


1. 제약 조건의 추가

제약조건을 추가하는 기본 구문은 ADD CONTRAINT constraint_name이며,
이는 아래 예제들에서도 반복적으로 나오므로 자연스레 눈에 익을 것이라 생각한다.

ADD CONSTRAINT constraint_name과 각 제약조건의 종류에 따른 키워드들로 제약 조건 추가 구문이 조합된다.


1. PK 제약 조건

기본 키가 지정되어 있지 않던 테이블에 기본키를 추가하는 방법은 아래와 같다.

  1. ALTER TABLE UserTable
  2.     ADD CONSTRAINT PK_UserTable_ID
  3.     PRIMARY KEY(UserID)

    기존 테이블에 PK를 추가하려 할 때, 해당(UserID) 컬럼은 NOT NULL로 설정이 되어 있어야 한다.


    2. FK 제약 조건

    기존 테이블에 FK(외래 키)를 설정하는 방법은 아래와 같다.

    1. ALTER TABLE BuyTable
    2.     ADD CONSTRAINT FK_UserTable_ID
    3.     FOREIGN KEY (ID)                  -- 이 테이블에서 FK를 걸 컬럼명
    4.     REFERENCES UserTable(UserID)      -- 기준 테이블의 참조 컬럼명

    이미 데이터들이 꽤나 많이 차 있는 두 개의 테이블에 FK 의존 관계 설정시 애 먹는 경우가 있다.

    애초 의존 관계를 고려하지 않았기에, 기준 테이블에 존재하지 않는 데이터가 외래 키 테이블에 존재할 수 있기 때문이다.
    헌데, 이미 서비스 중이고, 대량의 데이터가 차 있기에, 모든 데이터의 의존 관계를 완벽하게 조정할 수도 없는 상태이다.

    이런 경우 ALTER TABLE 구문을 WITH NOCHECK 옵션과 함께 사용하면,
    이미 입력되어 있는 데이터의 무결성(의존 관계)를 무시하고 외래 키 관계가 설정된다.

    참고로, 아무 것도 써 주지 않을 경우 기본적으로 WITH CHECK 상태이다.

    1. ALTER TABLE BuyTable WITH NOCHECK
    2.     ADD CONSTRAINT FK_UserTable_ID
    3.     FOREIGN KEY (ID)                  -- 이 테이블에서 FK를 걸 컬럼명
    4.     REFERENCES UserTable(UserID)      -- 기준 테이블의 참조 컬럼명

    WITH NOCHECK 옵션은 FK와 CHECK 제약 조건에서만 설정이 가능하다.

    그리고 WITH CHECK / WITH NOCHECK 옵션은 
    제약 조건을 생성할 때 기존의 데이터를 검사하는 것을 무시하는 것일뿐설정한 후에는 당연히 제약 조건이 작동됨을 혼동하지 말자.


    3. UNIQUE 제약 조건

    기존 테이블에 UNIQUE 제약 조건을 추가하는 방법은 아래와 같다.

    1. ALTER TABLE UserTable
    2.     ADD CONSTRAINT UK_Regdate
    3.     UNIQUE RegDate


    4. CHECK 제약 조건

    기존 테이블에 CHECK 제약 조건을 추가 설정하는 방법은 아래와 같다.

    1. -- 출생년도가 1900년 이후 그리고 현재의 연도 이전
    2. ALTER TABLE UserTable
    3.     ADD CONSTRAINT CK_BirthYear
    4.     CHECK (BirthYear >= 1900 AND BirthYear <= YEAR(GETDATE()))
    5.  
    6. -- 전화번호 국번 체크 (아래 번호들 중에서만 가능)
    7. ALTER TABLE UserTable
    8.     ADD CONSTRAINT CK_Mobile1
    9.     CHECK (Mobile1 IN ('010''011''016''017''018''019'))
    10.  
    11. -- 키는 0 보다 커야 함
    12. ALTER TABLE UserTable
    13.     ADD CONSTRAINT CK_Height
    14.     CHECK (Height > 0)


    5. DEFAULT 정의


    기존에 없던 DEFALUT 정의를 추가하려면 아래와 같이 FOR 문을 함께 사용해 주어야 한다.

    1. ALTER TABLE UserTable
    2.     ADD CONSTRAINT CD_BirthYear
    3.     DEFAULT YEAR(GETDATE()) FOR BirthYear
    4.  
    5. ALTER TABLE UserTable
    6.     ADD CONSTRAINT CD_Addr
    7.     DEFAULT N'서울' FOR Addr
    8.  
    9. ALTER TABLE UserTable
    10.     ADD CONSTRAINT CD_Height
    11.     DEFAULT 172 FOR Height



    2. 제약 조건의 삭제

    기존의 제약 조건을 삭제하는 것은 추가하는 것에 비해 무척이나 간단하다.
    제약 조건의 종류에 관계없이 공통된 구문 하나로 모두 적용이 가능하다.

    1. ALTER TABLE tableName
    2.     DROP CONSTRAINT constraint_name

    만약, 제약 조건만 삭제하는 경우가 아니라, 해당 컬럼을 삭제하려 하고 그 컬럼에 제약 조건이 걸린 경우라면,
    1. 제약 조건을 먼저 삭제하고,
    2. 해당 컬럼을 삭제해야 한다.

    즉, UserTable의 BirthYear에 CHECK 제약 조건이 걸려 있는데, BirthYear 컬럼을 삭제하고 싶으면,
    아래 예제와 같이 CHECK 제약 조건을 먼저 삭제한 후에 컬럼을 삭제해야 하는 것이다.

    1. -- 먼제 BirthYear에 걸려있는 제약 조건을 삭제하고
    2. ALTER TABLE UserTable
    3.     DROP CK_BirthYear
    4.  
    5. -- BirthYear 컬럼을 삭제해야 한다.
    6. -- DROP COLUMN column_name 형식에 주의하라
    7. ALTER TABLE UserTABLE
    8.     DROP COLUMN BirthYear


    3. 제약 조건 활성/비활성화

    위에서 WITH CHECK / WITH NOCHECK 옵션은 
    제약 조건을 생성하는 그 순간에서만 제약 조건 체크를 스킵할 지 여부를 결정하는 것이라 하였다.

    그럼, 이미 만들어진 제약 조건을 잠깐 비활성화하고 싶을 때는 어떻게 해야 하는가?

    CHECK CONSTRAINT / NOCHECK CONSTRAINT 옵션을 사용하면 된다.
    • CHECK CONSTRAINT constraint_name : 특정 제약 조건을 활성화
    • NOCHECK CONSTRAINT constraint_name : 특정 제약 조건을 비활성화
    • NOCHECK CONSTRAINT ALL : 모든 제약 조건을 비활성화

    1. -- FK 일시적으로 비활성화
    2. ALTER TABLE BuyTable
    3.     NOCHECK CONSTRAINT FK_UserTable_ID
    4. GO
    5.  
    6. -- FK 제약 조건에 위배되는 데이터 삽입
    7. INSERT INTO BuyTable VALUES (...)
    8. INSERT INTO BuyTable VALUES (...)
    9. INSERT INTO BuyTable VALUES (...)
    10. GO
    11.  
    12. -- FK 다시 정상적으로 활성화
    13. ALTER TABLE BuyTable
    14.     CHECK CONSTRAINT FK_UserTable_ID
    15. GO

    간혹, 써 먹어야 될 때가 있을 것이다.


    'SQL > MS-SQL' 카테고리의 다른 글

    Datetime Fomat  (0) 2017.10.11
    PIVOT/ UNPIVOT  (0) 2017.09.21
    제약 조건(Constraint) #1 (의미와 설정)  (0) 2017.09.20
    테이블변수  (0) 2017.09.20
    테이블 컬럼변경  (0) 2017.09.20
    1. 제약 조건


    제약 조건(Constraint)이란, 데이터의 무결성을 지키기 위해 제한된 조건을 의미한다.

    즉, 특정 데이터를 입력할 때 무조건적으로 입력되는 것이 아니라, 
    어떠한 조건을 만족했을 때에만 입력되도록 제약을 할 수 있는 것이다.

    예를 들어, 어떠한 쇼핑몰에 가입할 때 이미 가입한 주민등록번호로는 재가입이 되지 않는다.
    그 이유는 주민등록번호 열에 동일한 것이 들어갈 수 없는 "제약 조건"이 설정되어 있기 때문이다.
    이런 경우 주민등록번호에 중복이 불가능한 제약 조건(이 경우 보통 UNIQUE)이 들어 있을 것이다.

    이 외에도 SQLServer는 아래와 같은 제약 조건들을 제공하고 있다.
    • Primary key 제약 조건
    • Foreign key 제약 조건
    • UNIQUE 제약 조건
    • CHECK 제약 조건
    • DEFAULT 정의


    2. PK 제약 조건

    테이블에 존재하는 많은 행의 데이터를 구분할 수 있는 식별자를 "기본 키(Primary key)"라고 부른다.

    기본 키에 입력되는 값은 중복될 수 없으며, NULL 값이 입력될 수 없다.

    기본 키로 생성한 것은 자동으로 클러스터형 인덱스가 생성된다.


    1. CREATE TABLE UserTable
    2. (
    3.     UserID CHAR(8) NOT NULL PRIMARY KEY,
    4.     Name NVARCHAR(10) NOT NULL,
    5.     -- 이하 열 생략
    6. )

    이렇게 설정함으로써 UserID는 UserTable의 기본 키가 되었으며,
    앞으로 입력되는 UserID는 당연히 중복될 수 없고, 비어(NULL) 있을 수도 없다.

    모든 제약 조건은 이름을 가지게 되는데, 위와 같이 UserID 열에 대해 기본 키를 설정하고 나면,
    "PK__UserTable__3214EC27060DEAE8" 등과 같이 알아볼 수 없는 이름을 가지게 된다.
    이는 SQLServer가 제약 조건의 이름을 나름의 규칙을 가지고 알아서 설정해 버리기 때문이다.

    UserTable에 PK가 설정되었다는 것까지는 알겠는데, 위와 같은 네이밍이면 어느 열이 PK인지 알아보기 쉽지 않다.

    기본 키를 설정할 때 이름을 지정하려면 아래와 같이 CREATE TABLE시 작성하면 된다.

    1. CREATE TABLE UserTable
    2. (
    3.     UserID CHAR(8) NOT NULL CONSTRAINT PK_UserID PRIMARY KEY,
    4.     Name NVARCHAR(10) NOT NULL,
    5.     -- 이하 열 생략
    6. )

    UserTable의 기본 키를 UserID 열로 정하되, PK_UserID라는 이름을 가지도록 하라는 것이다.


    또한, 테이블에서 하나 이상의 열을 기본 키로 설정할 수 있다.
    즉, N 개의 열을 묶어 기본 키로 설정할 수 있는 것이다.

    SSMS에서 아래와 같이 복수의 컬럼을 엮어 기본 키로 설정할 수 있으며,



    T-SQL의 CREATE TABLE문은 아래와 같이 작성하여 복수 열을 기본 키로 설정할 수 있다.

    1. CREATE TABLE UserTable
    2. (
    3.     UserID CHAR(8) NOT NULL,
    4.     Name NVARCHAR(10) NOT NULL,
    5.     -- 이하 열 생략
    6.     CONSTRAINT PK_UserID_Name PRIMARY KEY (UserID, Name)  
    7. )


    3. FK 제약 조건

    외래 키(Foreign Key) 제약 조건은 
    두 테이블 사이의 관계를 선언함으로써, 데이터의 무결성을 보장해 주는 역할을 한다.

    외래 키 관계를 설정하게 되면 하나의 테이블(외래 키 테이블)이 다른 테이블(기준 테이블)에 의존하게 된다.

    '외래 키 테이블'에 데이터를 입력할 때는 꼭 '기준 테이블'을 참조해서 입력하므로,
    반드시 '기준 테이블'에 존재하는 데이터만 입력이 가능하다.

    또한, '외래 키 테이블'이 참조하는 '기준 테이블'의 열은 반드시 PK이거나 UNIQUE 제약 조건이 설정되어 있어야 한다.
    이는 조금만 생각해보면, 지극히 당연한 제약이다.
    A가 B를 참조하는 데 B가 여러 개 이면 어느 것을 참조해야 할 지 알 수 없기 때문이다.



    1. CREATE TABLE UserTable
    2. (
    3.     UserID CHAR(8) NOT NULL
    4.     CONSTRAINT PK_UserID PRIMARY KEY,
    5.     Name NVARCHAR(10) NOT NULL,
    6.     -- 이하 열 생략
    7. )
    8.  
    9. -- unnamed
    10. CREATE TABLE BuyTable
    11. (
    12.     UserID CHAR(8) NOT NULL
    13.     FOREIGN KEY REFERENCES UserTable(ID),
    14.     -- 이하 열 생략
    15. )
    16.  
    17. -- named
    18. CREATE TABLE BuyTable
    19. (
    20.     UserID CHAR(8) NOT NULL
    21.     CONSTRAINT FK_UserTable_ID FOREIGN KEY REFERENCES UserTable(ID),
    22.     -- 이하 열 생략
    23. )



    헌데, 외래 키로 지정한 기준의 컬럼의 값이 변경되면 외래 키 테이블의 컬럼 값은 어떻게 처리할 것인가?

    예를 들어, UserTable의 ID(PK) = 'KHD'인 값이 있고, BuyTable에도 ID(FK) = 'KHD'인 데이터들이 있다.
    ID 변경 아이템 등을 사용하여 UserTable의 ID 'KHD'를 'KKHD'로 변경해야 한다면, 
    BuyTable에 이미 존재하는 'KHD'들도 변경이 되어야만 외래 키 제약이 제대로 유지될 수 있다.

    외래 키 옵션 중 ON UPDATE CASCADEON DELETE CASCADE라는 것이 있다.
    • ON UPDATE CASCADE : 기준 테이블의 데이터가 변경되었을 때 외래 키 테이블도 자동으로 변경
    • ON DELETE CASCADE : 기준 테이블의 데이터가 삭제되었을 때 외래 키 테이블의 데이터들도 자동으로 삭제

    외래 키의 위 두가지 옵션에 대해 기본값은 ON UPDATE NO ACTION과 ON DELETE NO ACTION으로 지정되어 있다.
    즉, 기준 테이블의 데이터가 변경/삭제되어도 외래 키 테이블에 자동적으로 변경/삭제를 해주지 않도록 되어 있다.

    이를 변경하는 방법 역시 SSMS를 통한 방법과 T-SQL을 통한 방법 두 가지가 있다.

    먼저 SSMS의 속성은 다음과 같이 변경이 가능하다.



    기본적으로는 동작 안 함이 선택되어 있으며, CASCADE를 설정하고 싶으면 "계단식 배열"을 선택하면 된다.
    (번역 참 구리다)

    그리고 아래와 같이 작성하면 T-SQL을 통해서도 속성 변경이 가능하다.

    1. -- ON UPDATE CASCADE
    2. CREATE TABLE BuyTable
    3. (
    4.     UserID CHAR(8) NOT NULL
    5.     FOREIGN KEY REFERENCES UserTable(ID)
    6.     ON UPDATE CASCADE,
    7.     -- 이하 열 생략
    8. )
    9.  
    10. -- ON DELETE CASCADE
    11. CREATE TABLE BuyTable
    12. (
    13.     UserID CHAR(8) NOT NULL
    14.     CONSTRAINT FK_UserTable_ID FOREIGN KEY REFERENCES UserTable(ID)
    15.     ON DELETE CASCADE,
    16.     -- 이하 열 생략
    17. )


    4. UNIQUE 제약 조건

    UNIQUE 제약 조건은 중복되지 않는 유일함을 부여하고 싶을 때 사용하는 제약 조건이다.
    PK와 얼핏 비슷해 보이지만, 네 가지 차이점이 존재한다.
    • UNIQUE는 유일하게 하나의 개체에 대해서만 NULL을 허용한다.
    • PK는 테이블에 하나만 존재 가능하나, UNIQUE 제약조건은 여러 개 설정이 가능하다.
    • PK는 자동으로 클러스터형 인덱스가 된다.

    흔한 게임 DB의 유저 정보를 예로 들자면, 유저 식별 번호나 유일한 ID 등은 PK로 잡겠지만,
    사용자별 E-Mail 주소나 닉네임에 대해서는 UNIQUE 제약조건을 부여할 수 있는 것이다.
    이를 T-SQL로 설정하는 방법은 아래와 같다.

    1. -- unnamed
    2. CREATE TABLE UserTable
    3. (
    4.     -- ...
    5.     Email NVARCHAR(50) NOT NULL UNIQUE,
    6.     -- ...
    7. )
    8.  
    9. -- named
    10. CREATE TABLE UserTable
    11. (
    12.     -- ...
    13.     Email NVARCHAR(50) NOT NULL
    14.     CONSTRAINT UK_UserTable_Email UNIQUE,
    15.     -- ...
    16. )


    5. CHECK 제약 조건

    CHECK 제약 조건은 특정 조건들로 이루어진 수식을 통해 입력되는 데이터를 검증할 때 사용한다.

    예를 들어, 음수값은 허용하지 않는다던지,
    출생년도가 1900년 이후이고 현재 시점 이전이어야만 한다던지 등의 조건을 지정할 수 있는 것이다.
    이를 T-SQL로 설정하려면 아래와 같이 코딩한다.

    1. -- unnamed
    2. CREATE TABLE #CKTable
    3. (
    4.     -- ...
    5.     Number INT NOT NULL
    6.     CHECK (Number >= 1 AND Number <= 10)
    7. )
    8.  
    9. -- named
    10. CREATE TABLE #CKTable
    11. (
    12.     -- ...
    13.     Number INT NOT NULL
    14.     CONSTRAINT CK_VALUE CHECK (Number >= 1 AND Number <= 10)
    15. )


    6. DEFAULT 정의

    DEFAULT는 값을 입력하지 않았을 때, 자동으로 입력되는 기본 값을 정의하는 방법이다.

    1. CREATE TABLE #DefaultTable
    2. (
    3.     BirthYear INT NOT NULL   DEFAULT YEAR(GETDATE()),
    4.     Addr NCHAR(2) NOT NULL   DEFAULT N'서울',
    5.     Height SMALLINT NOT NULL DEFAULT 172,
    6.     -- ...
    7. )

    위 예제와 같이 DEFAULT 키워드를 사용하고, 각 컬럼의 데이터 형식에 맞는 기본값을 지정해 주면 된다.


    7. 다음 문서

    이 문서에서 T-SQL을 이용한 제약조건 설정은 모두 테이블을 생성할 때로 한정되어 설명되어 있다.

    DB를 운영하다 보면, 이미 만들어진 테이블에 제약조건을 추가하거나, 삭제 또는 변경해야 할 일도 다반사이다.
    특히 이미 데이터가 들어가 있는 테이블이라면 말이다.


    'SQL > MS-SQL' 카테고리의 다른 글

    PIVOT/ UNPIVOT  (0) 2017.09.21
    제약 조건(Constraint) #2 (기존 테이블에 추가/삭제)  (0) 2017.09.20
    테이블변수  (0) 2017.09.20
    테이블 컬럼변경  (0) 2017.09.20
    SELF JOIN  (0) 2017.09.19

    + Recent posts