728x90

2장

  • 인덱스 탐색 과정은 ___ 탐색과 ___ 탐색 두 단계로 이루어진다.
  • 인덱스는 큰 테이블에서 __ 데이터를 검색할 때 사용한다.
  • 인덱스 튜닝의 두 가지 핵심요소
    • 인덱스 __ 과정에서 발생하는 비효율 줄이는 것 = 인덱스 스캔 효율화 튜닝
    • 테이블 엑세스 횟수를 줄이는 것 = __ 엑세스 최소화 튜닝
  • ___, ____, ___, Prefetch, Batch I/O 등의 기능들의 본질은 랜덤 I/O의 최소화이다.
  • __ 조인이 대량 조인할 때 느린 이유도 랜덤I/O 때문이다. 이를 위해 ____ 조인과 __ 조인이 개발되었다.
  • DBMS는 일반적으로 _____ 인덱스를 사용한다.
  • ____가 위쪽에 있고, ______를 거쳐 맨 아래 _____가 있다.
  • Root와 Branch 블록에 있는 각 레코드는 하위 블록에 대한 _____을 갖는다.
  • Root와 Branch 블록에는 주소값을 갖지 않는 _____라는 특별한 레코드가 있다.
  • Leaf 블록에 저장된 각 레코드는 _____ 순으로 정렬되어 있을 뿐만 아니라 테이블 레코드를 가리키는 주소값, 즉 _____를 갖는다.
    • ROWID = _____ + _____
    • 데이터 블록 주소 = _____ + _____
    • 블록 번호 : _____ 내에서 부여한 상대적 순번
    • 로우 번호 : _____ 내 순번
  • 인덱스 _____ 탐색 : 인덱스 스캔 시작지점을 찾는 과정 = 인덱스 레코드 중 조건을 만족하는 첫 번째 레코드를 찾는 과정이다. 즉, 인덱스 스캔 시작지점을 찾는 과정이다.
  • 인덱스 _____ 탐색 : 데이터를 찾는 과정 = 찾고자 하는 데이터가 더 안 나타날 때까지 인덱스 리프 블록을 수평적으로 스캔한다.
    • 조건절에 만족하는 데이터를 모두 찾기 위해서, ROWID를 얻기 위해서 수평적 탐색을 수행한다.
  • 인덱스 선두 컬럼을 모두 _____ 조건으로 검색할 때는 어느 컬럼을 인덱스 앞쪽에 두든 블록 I/O 개수가 같으므로 성능도 똑같다. 즉 , 선두 컬럼이 모두 _____ 조건이면 수직적 탐색 과정의 비용이 같다는 것이다.
  • B*Tree 인덱스에서 어떤 값으로 탐색하더라도 인덱스 루트에서 리프 블록에 도달하기까지 읽는 블록 수가 _____.
  • 인덱스 기본 사용법은 인덱스를 _____ Scan 하는 방법을 의미한다.
  • 인덱스 컬럼(정확히는 _____ 컬럼)을 가공하지 않아야 인덱스를 정상적으로 사용할 수 있다. '인덱스를 정상적으로 사용한다'는 표현은 리프 블록에서 스캔 시작점을 찾아 거기서부터 스캔하다가 중간에 멈추는 것을 의미한다. 즉, 리프 블록 _____만 스캔하는 Index _____ Scan을 의미한다.
  • 인덱스 컬럼을 가공해도 인덱스를 사용할 수는 있지만, 스캔 시작점을 찾을 수 없고 멈출 수도 없어 리프 블록 _____를 스캔해야만 한다. 즉, 일부가 아닌 전체를 스캔하는 Index _____ Scan 방식으로 작동한다.
  • Index _____ Scan은 인덱스에서 일정 범위를 스캔한다는 뜻이다. 일정 범위를 스캔하려면 시작지점과 끝지점이 있어야 한다. 인덱스 컬럼을 가공했을 때 인덱스 스캔 시작점을 찾을 수 없기 때문에 정상적으로 사용할 수 없다.
  • OR 조건식을 SQL 옵티마이저가 ________ 형태로 변환할 수 있는데 이를 'OR Expansion'이라고 한다.
  • __________ 힌트를 통해 'OR Expansion'를 유도할 수 있다.
  • IN 조건은 ___ 조건을 표현하는 다른 방식일 뿐이다. IN 조건절에 대해서는 SQL 옵티마이저가 IN-List Iterator 방식을 사용한다. IN-List 개수만큼 Index Range Scan을 반복하는 것이다. UNION ALL 방식으로 변환한 것과 같은 효과를 얻을 수 있다.
  • 인덱스를 Range Scan 하기 위한 가장 첫 번째 조건은 인덱스 선두 컬럼이 조건절에 있어야 한다는 사실이다. 가공하지 않은 상태로 말이다.
  • 문제는, 인덱스를 Range Scan 한다고 해서 항상 성능이 좋은 것은 아니라는 것이다.
  • '인덱스를 탄다'라는 표현은 '인덱스를 ______ 한다'와 같은 의미인 셈이다. 그런데 인덱스를 정말 잘 타는지는 인덱스 리프 블록에서 스캔하는 양을 따져봐야 알 수 있다.
  • 인덱스 컬럼을 가공해도 인덱스를 사용할 순 있지만, 찾고자 하는 데이터가 전체 구간에 _____ 있기 때문에 Range Scan이 불가능하거나 비효율이 발생한다.
  • 옵티마이저는 SQL에 ______가 있어도 정렬 연산을 따로 수행하지 않는다. 인덱스를 스캔하면서 출력한 결과 집합은 어차피 인덱스 컬럼 순으로 정렬되기 때문이다. 따라서 실행계획에 ____연산이 없다.
  • 조건절이 아닌 ______ 또는 ________에서 컬럼을 가공함으로 인해 인덱스를 정상적으로 사용할 수 없는 경우도 종종 있다.
  • 인덱스를 이용해 정렬 연산 없이 최소 또는 최대값을 빠르게 찾을 때 인덱스 리프블록의 왼쪽(MIN) 또는 오른쪽(MAX)에서 레코드 하나(FIRST ROW)만 읽고 멈춘다.
  • 인덱스는 문자열 기준으로 정렬돼 있는데 이를 __값으로 바꾼 값 기준으로 최소 또는 최대값, ORDER BY를 요구하면 정렬 연산을 생략할 수 없다.
  • 오라클은 자동으로 ____ 처리해준다.
  • 오라클에서 __형과 __형이 만나면 __형이 이긴다. 숫자형 컬럼 기준으로 문자형 컬럼을 변환한다는 뜻이다. 연산자가 __ 일 때는 다르다. LIKE 자체가 문자열 비교 연산자이므로 이때는 문자형 기준으로 숫자형 컬럼이 변환된다. __형 컬럼을 LIKE 조건으로 검색하면 자동 형변환이 발생해 인덱스 ____조건으로 사용되지 못한다.
  • 날짜형과 문자형이 만나면 __형이 이긴다.
  • DECODE(A, B, C, D)를 처리 할 때 'A = B' 이면 C를 반환하고, 아니면 D를 반환한다. 이때 반환값의 데이터 타입은 __에 의해 결정된다. 세 번째 인자가 NULL 값이면 네 번 째 인자가 숫자형이어도 문자열로 변환하고, 문자열 기준으로 MAX() 를 수행하게 된다. 950보다 5000이 크다고 판단하게 된다.
  • SQL 성능 원리를 잘 모르는 개발자는 TO_CHAR, TO_DATE, TO_NUMBER 같은 형변환 함수를 의도적으로 생략하곤 한다. 이들 함수를 생략하면 연산횟수가 줄어 성능이 더 좋지 않을까 생각하기 때문이다. SQL 성능은 그런 데서 결정되는 게 아니라 블록 I/O를 줄일 수 있느냐 없느냐에서 결정된다. 형변환 함수를 생략한다고 연산 횟수가 주는 것도 아니다. 개발자가 형변환 함수를 생략해도 옵티마이저가 자동으로 생성한다.
  • Index Range Scan 이외에도 Index __ Scan, Index __ Scan, Index __ Scan, Index __ Full Scan 등이 있다.
  • Index Range Scan은 인덱스 루트에서 리프 블록까지 수직적으로 탐색한 후에 필요한 범위만 스캔한다.
  • 선두 컬럼을 ________ 상태로 조건절에 사용하면 Index Range Scan이 무조건 가능하다. 그러므로 성능은 인덱스 스캔 범위, 테이블 엑세스 횟수를 얼마나 줄일 수 있느냐로 결정된다.
  • Index __ Scan은 __적 탐색없이 인덱스 리프 블록을 처음부터 끝까지 __적으로 탐색하는 방식이다.
  • Index __ Scan은 대개 데이터 검색을 위한 최적의 인덱스가 없을 때 차선으로 선택된다.
  • 대용량 테이블이어서 옵티마이저는 Table Full Scan을 고려하지만, 대용량 테이블의 경우 Table Full Scan이 부담스러워서 옵티마이저는 인덱스 활용을 고려하지 않을 수 없다.
  • /*+ ________ */ 힌트를 사용하면 옵티마이저가 모드를 바꿔 인덱스 Full Scan을 고려한다. 이는 인덱스를 활용하므로 Sort 연산 생략 효과를 가져온다.
  • Index __ Scan은 수직적 탐색만으로 데이터를 찾는 스캔 방식으로서 Unique 인덱스를 '=' 조건으로 탐색하는 경우에 작동한다.
  • __ 인덱스가 존재하는 컬럼은 중복 값이 입력되지 않게 DBMS가 데이터 정합성을 관리한다. 따라서 해당 인덱스 키 컬럼을 모두 '=' 조건으로 검색할 때는 데이터를 한 건 찾는 순간 더 이상 탐색할 필요가 없다.
  • Unique __ 인덱스에 대해 일부 컬럼으로만 검색할 때도 Index Range Scan이 나타난다.
  • 옵티마이저는 Table Full Scan보다 I/O를 줄일 수 있거나 정렬된 결과를 쉽게 얻을 수 있다면, Index __ Scan을 사용하기도 한다.
  • 오라클은 인덱스 선두 컬럼이 조건절에 없어도 인덱스를 활용하는 새로운 스캔 방식이 Index __ Scan이다. 이 스캔 방식을 조건절에 빠진 인덱스 __ 컬럼의 Distinct Value 개수가 적고 __ 컬럼의 Distinct Value 개수가 많을 때 유용하다. 예를 들어 고객 테이블에서 Distinct Value가 적은 컬럼을 '성별'이라고 할 수 있다.
  • Index __ Scan은 루트 또는 브랜치 블록에서 읽은 컬럼 값 정보를 이용해 조건절에 부합하는 레코드를 포함할 '가능성이 있는' 리프 블록만 골라서 엑세스하는 스캔 방식이다.
  • 인덱스 선두 컬럼이 없을 때만 Index Skip Scan이 작동하는 것은 아니다.
  • 선두 컬럼에 대한 조건절은 있고, __ 컬럼에 대한 조건절이 없는 경우에도 Skip Scan을 사용할 수 있다.
  • /*+ ________(A 일별업종별거래_PK) */
  • Index Skip Scan을 사용한다면, 업종유형코드 = '01'인 구간에서 기준일자가 '20080501'보다 크거나 같고 '20080531'보다 작거나 같은 레코드를 '포함할 가능성이 있는 리프 블록만' 골라서 엑세스할 수 있다.
  • 선두컬럼이 부등호, BETWEEN, LIKE 같은 범위검색 조건일 때도 Index __ Scan을 사용할 수 있다.
  • Index Range Scan을 사용한다면, 기준일자 BETWEEN 조건을 만족하는 인덱스 구간을 모두 스캔해야 한다. Index __ Scan을 사용한다면, 기준일자 BETWEEN 조건을 만족하는 인덱스 구간에서도 업종유형코드 = '01'인 레코드를 포함할 가능성이 있는 리프 블록만 골라서 엑세스할 수 있다. 이처럼 Index Range Scan이 불가능하거나 효율적이지 못한 상황에서 Index __ Scan이 종종 빛을 발한다.
  • 인덱스는 기본적으로 최적의 Index Range Scan을 목표로 설계해야 하며, 수행 횟수가 적은 SQL을 위해 인덱스를 추가하는 것이 비효율적일 때 이들 스캔 방식을 차선책으로 활용하는 전략이 바람직하다.
  • Index __ __Scan이 Index __ Scan 보다 빠른 이유는, 논리적인 인덱스 트리 구조를 무시하고 인덱스 세그먼트 전체를 ______ I/O 방식으로 스캔하기 때문이다. 관련 힌트 : index_ffs, no_index_ffs
  • Index Fast Full Scan은 물리적으로 ____에 저장된 순서대로 인덱스 리프 블록들을 읽어들인다.
  • 결과집합이 인덱스 키 순서대로 정렬되지 않는다. 쿼리에 사용한 컬럼이 __ 인덱스에 포함돼 있을 때만 사용할 수 있다는 점도 기억할 필요가 있다.
  • Index Range Scan 또는 Index Full Scan과 달리, 인덱스가 ____ 돼 있지 않더라도 병렬 쿼리가 가능한 것도 중요한 특징 중 하나이다. 병렬 쿼리 시에는 ______ I/O 방식을 사용하기 때문에 속도가 더 빨라진다.
  • Table Full Scan은 읽어야 할 익스텐트 목록을 익스텐트 맵에서 얻는다. Index Fast Full Scan도 똑같은 방식을 사용한다.
  • Index Fast Full Scan 특징
    • ____ 전체를 스캔
    • ____ 순서 보장 안 됨
    • ________ I/O
    • ____ 가능
    • 인덱스에 포함된 컬럼으로만 조회할 때 사용 가능
  • Index Full Scan 특징
    • 인덱스 (논리적) 구조를 따라 스캔
    • 결과집합 순서 보장
    • ______ I/O
    • (____ 돼 있지 않다면) 병렬스캔 불가
    • 인덱스에 포함되지 않은 컬럼 조회시에도 사용 가능
  • Index Range Scan _______은 인덱스를 뒤에서부터 앞쪽으로 스캔하기 때문에 내림차순으로 정렬된 결과집합을 얻는다는 점만 다르다.
  • 내림차순 Order by나 MAX 값을 구하고자 하는 경우 등에 사용된다.
728x90
728x90

3장 인덱스 튜닝

  • SQL이 참조하는 컬럼을 인덱스가 모두 포함하는 경우가 아니면, 인덱스를 스캔한 후에 반드시 테이블을 엑세스한다. TABLE ACCESS BY INDEX ROWID라고 표시된 부분이 여기에 해당한다.
  • 인덱스 ROWID 는 물리적 주소보다 논리적 주소에 가깝다. 물리적으로 직접 연결되지 않고 테이블 레코드를 찾아가기 위한 논리적 주소 정보를 담고 있기 때문이다.
  • 프로그래밍 언어에서 포인터는 메모리 주소값을 담는 변수를 말한다. 물리적으로 직접 연결된 구조나 다름없다. 메모리 상에서의 위치 정보인 포인터를 생각하며 인덱스 ROWID를 물리적 주소로 이해했다면 잘못 이해한 것이다. 인덱스 ROWID는 포인터가 아니다. 인덱스는 ROWID는 논리적 주소다. 디스크 상에서 테이블 레코드를 찾아가기 위한 위치 정보를 담는다. 포인터가 아니며, 테이블 레코드와 물리적으로 직접 연결된 구조는 더더욱 아니다.
  • 메인 메모리 DB(MMDB)는 데이터를 모두 메모리에 로드해 놓고 메모리를 통해서만 I/O를 수행하는 DB라고 할 수 있다.
  • 잘 튜닝된 OLTP성 데이터베이스 시스템이라면 버퍼캐시 히트율이 99% 이상이지만 그래도 MMDB만큼 빠르지는 않다. MMDB의 경우 인스턴스 가동 시 디스크에 저장된 데이터를 버퍼캐시로 로딩하고 이어서 인덱스를 생성한다. 이 때 인덱스는 디스크 상의 주소가 아닌 메모리상의 주소정보, 즉 포인터를 갖는다. 따라서 인덱스를 경유해 테이블을 엑세스하는 비용이 오라클에 비해 현저히 낮다. 
  • 오라클은 테이블 블록이 수시로 버퍼캐시에서 밀려났다가 캐싱되며, 그때마다 다른 공간에 캐싱되기 때문에 인덱스에서 포인터로 직접 연결할 수 없는 구조다. 메모리 주소 정보(포인터)가 아닌 디스크 주소 정보(DBA, Data Block Address)를 이용해 해시 알고리즘으로 버퍼 블록을 찾아간다. 
  • 읽고자 하는 DBA를 해시 함수에 입력해서 해시 체인을 찾고 거기서 버퍼 헤더를 찾는다. 거기서 얻은 포인터로 버퍼 블록을 찾아간다. 
  • 인덱스로 테이블 블록을 엑세스할 때는 리프 블록에서 얻은 ROWID를 분해해서 DBA 정보를 얻고, 테이블을 Full Scan할 때는 익스텐트 맵을 통해 읽을 블록들의 DBA 정보를 얻는다.
  • 모든 데이터가 캐싱돼 있더라도 테이블 레코드를 찾기 위해 매번 DBA 해싱과 래치 획득 과정을 반복해야 한다. 동시 엑세스가 심할 때는 캐시 버퍼 체인 래치와 버퍼 Lock에 대한 경합까지 발생한다. 이처럼 인덱스 ROWID를 이용한 테이블 엑세스는 생각보다 고비용 구조이다.
  • 클러스터링 팩터(Clustering Factor, CF)는 특정 컬럼을 기준으로 같은 값을 갖는 데이터가 서로 모여있는 정도를 의미한다. CF가 좋은 컬럼에 생성한 인덱스는 검색 효율이 매우 좋다. 즉, 테이블 엑세스량에 비해 블록 I/O가 적게 발생한다.
  • 인덱스 ROWID로 테이블을 엑세스할 때, 오라클은 래치 획득과 해시 체인 스캔 과정을 거쳐 어렵게 찾아간 테이블 블록에 대한 포인터(메모리 주소값)를 바로 해제하지 않고 일단 유지한다. 이를 '버퍼 Pinning' 이라고 한다. 이 상태에서 다음 인덱스 레코드를 읽었는데, 마침 직전과 같은 테이블 블록을 가리킨다. 그러면 래치 획득과 해시 체인 스캔 과정을 생략하고 바로 테이블 블록을 읽을 수 있다. 논리적인 블록 I/O 과정을 생략할 수 있는 것이다. 
  • 인덱스 ROWID를 이용한 테이블 엑세스는 생각보다 고비용 구조다. 따라서 읽어야 할 데이터가 일정량을 넘는 순간, 테이블 전체를 스캔하는 것보다 오히려 느려진다.
  • 추출 건수가 늘면서 인덱스 스캔량이 느는 데서도 영향을 받지만, 테이블 랜덤 엑세스가 미치는 영향에 비교할 바가 아니다.
  • 인덱스를 이용한 테이블엑세스가 Table Full Scan보다 더 느려지게 만드는 핵심 요인 두가지는 다음과 같다.
    • Table Full Scan은 스퀀셜 엑세스인 반면, 인덱스 ROWID를 이용한 테이블 엑세스는 랜덤 엑세스 방식이다.
    • Table Full Scan은 Multiblock I/O인 반면, 인덱스 ROWID를 이용한 테이블 엑세스는 Single Block I/O 방식이다.
  • 인덱스 사용 시 손익분기점 개념은 온라인 트랜잭션을 처리하는 프로그램과 DW/OLAP/배치 프로그램 튜닝의 특징을 구분 짓는 핵심 개념이다.
  • 온라인 프로그램은 보통 소량 데이터를 읽고 갱신하므로 인덱스를 효과적으로 활용하는 것이 무엇보다 중요하다. 조인도 대부분 NL 방식을 이용한다. NL조인은 인인덱스를 이용하는 조인 방식이다.
  • 대량 데이터를 읽고 갱신하는 배치(batch) 프로그램은 항상 전체범위 처리 기준으로 튜닝해야 한다. 대량 데이터를 빠르게 처리하려면, 인덱스와 NL 조인보다 Full Scan과 해시 조인이 유리하다.
  • 대량 배치 프로그램에선 인덱스보다 Full Scan이 효과적이지만, 초대용량 테이블을 Full Scan 하면 상당히 오래 기다려야 하고 시스템에 주는 부담도 적지 않다. 따라서 배치 프로그램에서는 파티션 활용 전략이 매우 중요한 튜닝 요소이고, 병렬 처리까지 더할 수 있으면 금상첨화다.
  • 관리적 측면을 배제하고 성능 측면에서만 보면 테이블을 파티셔닝하는 이유는 결국 Full Scan을 빠르게 처리하기 위해서다. 모든 성능 문제를 인덱스로 해결하려 해선 안 된다. 큰 테이블에서 아주 적은 일부 데이터를 빨리 찾고자 할 때 주로 사용한다.
  • 테이블 엑세스 단계 필터 조건에 의해 버려지는 레코드가 많을 때, 기존 인덱스에 WHERE절에 있는 컬럼을 추가하는 것만으로 큰 효과를 얻을 수 있따. 인덱스 스캔량은 줄지 않지만, 테이블 랜덤 엑세스 회수를 줄일 수 있다.
  • 인덱스만 읽어서 처리하는 쿼리를 Covered 쿼리하고 부르며 그 쿼리에 사용한 인덱스를 Covered 인덱스라고 부른다.
  • SQL Server 2005 버전에 추가된 Include 인덱스 기능이 있다. 인덱스 키 외에 미리 저장한 컬럼을 리프 레벨에 함께 저장하는 기능이다. 인덱스를 생성할 때 include (col) 옵션을 추가해주면 된다. 테이블 랜덤 엑세스 횟수를 줄이는 용도로만 사용한다.
  • IOT(Index-Organized Table)는 테이블을 인덱스 구조로 생성한 것이다. MS-SQL Server는 Clustered Index라고 부른다.
  • 일반 테이블은 힙 구조 테이블이며 대개 생략하지만 테이블 생성 시 organization heap 옵션을 명시할 수도 있다. 반면, IOT는 인덱스 구조 테이블이므로 정렬 상태를 유지하며 데이터를 입력한다. IOT는 인위적으로 클러스터링 팩터를 좋게 만드는 방법 중 하나다.
  • 예를 들어, 어떤 회사에 영업사원이 100명이라고 가정하자. 영원사업들의 일별 실적을 집계하는 테이블이 있는데 한 블록에 100개 레코드가 담긴다. 그러면 매일 한 블록씩 1년이면 365개 블록이 생긴다. 사번+일자로 SUM을 수행하는 쿼리가 자주 수행된다고 했을 때 사번 기준의 클러스터링 팩터가 안 좋으므로 조회건 수 만큼 블록 I/O가 발생하게 된다. 이 때 사번이 첫 번째 정렬 기준이 되도록 IOT를 구성해주면 적은 블록만 읽고 처리할 수 있다. oraganization index 옵션을 사용하여 테이블을 생성하면 된다.
  • 클러스터 테이블에는 인덱스 클러스터와 해시 클러스터 두 가지가 있다.
  • 인덱스 클러스터 테이블은 클러스터 키 값이 같은 레코드를 한 블록에 모아서 저장하는 구조다.
  • 여러 테이블 레코드를 같은 블록에 저장할 수도 있는데, 이를 '다중 테이블 클러스터'라고 부른다. 클러스터에 테이블을 담기 전에 클러스터 인덱스를 반드시 정의해야 한다. 클러스터 인덱스는 데이터 검색 용도로 사용할 뿐만 아니라 데이터가 저장될 위치를 찾을 때도 사용하기 때문이다.
  • 일반  테이블에 생성한 인덱스 레코드는 테이블 레코드와 1:1 대응 관계를 갖지만, 클러스터 인덱스는 테이블 레코드와 1:M 관계를 갖는다. 따라서 클러스터 인덱스의 키값은 항상 Unique하다.(=중복값이 없다.)
  • 클러스터에 도달해서는 시퀀셜 방식으로 스캔하기 때문에 넓은 범위를 읽더라도 비효율이 없다는 게 핵심원리다.
  • 해시 클러스터는 인덱스를 사용하지 않고 해시 알고리즘을 사용해 클러스터를 찾아간다는 점만 다르다.

 

3.2 부분범위 처리 활용

  • DBMS가 클라이언트에게 데이터를 전송할 때 일정량씩 나누어 전송한다. 전체 결과집합 중 아직 전송하지 않은 분량이 많이 남아있어도 서버 프로세스는 클라이언트로부터 추가 Fetch Call을 받기 전까지 그대로 멈춰 서서 기다린다.
  • DBMS가 데이터를 모두 읽어 한 번에 전송하지 않고 먼저 읽는 데이터부터 일정량(Array Size)을 전송하고 멈추기 때문이다. 데이터를 전송하고 나면 서버 프로세스는 CPU를 OS에 반환하고 대기 큐에서 잠을 잔다. 다음 Fetch Call을 받으면 대기 큐에서 나와 그다음 데이터부터 일정량을 읽어서 전송하고 또다시 잠을 잔다. 이처럼 전체 쿼리 결과집합을 쉼 없이 연속적으로 전송하지 않고 사용자로부터 Fetch Call이 있을 때마다 일정량씩 나누어 전송하는 것을 이른바 '부분범위 처리'라고 한다. 데이터를 전송하는 단위인 Array Size는 클라이언트 프로그램에서 설정한다. JAVA에서는 기본적값이 10이며, Statement 객체 setFetchSize 메소드를 통해 설정을 변경할 수 있다.
  • rs.next() 호출 시 Fetch Call을 통해 전송받은 데이터 10건을 클라이언트 캐시에 저장하고 이후 캐시에 저장한 데이터를 모두 소진한 후 rs.next()가 호출되면 추가 Fetch Call을 통해 10건을 전송받는다. 따라서 rs.next() 사이에 있는 JAVA 프로그램 로직이 오래 걸릴 이유는 없다. 
  • order by가 있고 해당 컬럼이 선두인 index가 없으면 정렬을 마치고서야 클라이언트에게 데이터 전송을 시작한다. 즉 전체범위 처리다. 정렬 컬럼이 선두인 index가 있으면 부분범위 처리가 가능하다. 인덱스는 항상 정렬된 상태를 유지하므로 전체 데이터를 정렬하지 않고도 정렬된 상태의 결과집합을 바로 전송할 수 있기 때문이다.
  • 대량 데이터를 파일로 내려받는다면 어차피 데이터를 모두 전송해야 하므로 가급적 Array Size를 크게 설정해야한다. 그러면 Fetch Call을 줄일 수 있다. 앞쪽 일부 데이터만 Fetch하다가 멈추는 프로그램이라면 Array Size를 작게 설정하는 것이 유리하다.
  • Fetch Call의 특성을 이용해 중간에 멈췄다가 사용자의 추가 요청이 있을 때마다 데이터를 가져오도록 구현하고 안 하고는 클라이언트 프로그램을 개발하는 개발자의 몫이다.
  • OLTP 시스템은 말 그대로 온라인 트랜잭션을 처리하는 시스템을 말한다., 온라인 트랜잭션은 일반적으로 소량 데이터를 읽고 갱신한다.
  • OLTP 시스템이 수천수만 건을 조회하는 경우도 있다. 이 때 부분범위처리로 성능 개선을 할 수 있다. 문제는 앞쪽 일부만 출력하고 멈출 수 잇는가이다. 토드나 오렌지같은 쿼리 툴은 이미 그렇게 구현돼 있다. 이들처럼 클라이언트 프로그램이 DB 서버에 직접 접속하는 2-Tier 환경에서는 그렇게 구현할 수 있다. 그런데 클라이언트와 DB 사이에 WAS, AP 서버 등이 존재하는 n-Tier 아키텍처에서는 클라이언트가 특정 DB 커넥션을 독점할 수 없다. 단위 작업을 마치면 DB 커넥션을 곧바로 커넥션 풀에 반환해야 하므로 그 전에 SQL 조회 결과를 클라이언트에게 '모두' 전송하고 커서(cursor)를 닫아야 한다.(JAVA에서 Statement, ResultSet 객체). 그래서 구현하기 어렵다. 그래도 여전히 유효한데 5장에서 자세한 사항을 배운다.
  • 부분범위 처리 원리를 활용해 상위 N개 집합을 빠르게 출력하도록 구현할 수 있다면, 인덱스로 엑세스할 전체 대상 레코드가 아무리 많아도 빠른 응답속도를 낼 수 있다. 그러기 위해선 인덱스를 이용해 소트 연산을 생략할 수 있어야 한다. 배치 I/O는 읽는 블록마다 건건이 I/O Call을 발생시키는 비효율을 줄이기 위해 고안한 기능이다. 이 기능이 작동하면 테이블 블록에 대한 디스크 I/O Call을 미뤘다가 읽을 블록이 일정량 쌓이면 한꺼번에 처리한다.
  • 배치 I/O 기능이 작동하면 인덱스를 이용해서 출력하는 데이터 정렬 순서가 매번 다를 수 있다는 사실에 주목해야 한다. 실제 배치 I/O가 작동할 때는 데이터 출력 순서가 인덱스 정렬 순서와 다를 수 있다. 소트 생략 가능한 인덱스를 사용하더라도 배치 I/O 기능이 작동하면 데이터 정렬 순서를 보장할 수 없다. 애초에 인덱스로 소트 연산을 생략할 수 없거나 SQL에 ORDER BY가 없으면, 랜덤 I/O 성능을 향상하는 이 기능을 옵티마이저가 기본적으로 사용하여 배치 I/O를 선택한다. 따라서 인덱스를 믿고 ORDER BY를 쓰지 않는 쿼리는 사용하지 않아야 한다. 옵티마이저가 Batch I/O를 채택할 수 있기 때문이다.

3.3 인덱스 스캔 효율화

  • 운영환경에서 가능한 일반적인 튜닝 기법은 인덱스 컬럼 추가다.
  • 인덱스 엑세스 조건은 인덱슼 스캔 범위를 결정하는 조건절이다. 인덱스 수직적 탐색을 통해 스캔 시작점을 결정하는 데 영향을 미치고 인덱스 리프 블록을 스캔하다가 어디서 멈출지를 결정하는 데 영향을 미치는 조건절이다.
  • 인덱스 필터 조건은 테이블로 엑세스할지를 결정하는 조건절이다.
  • 인덱스를 이용하든, 테이블을 Full Scan하든, 테이블 엑세스 단계에서 처리되는 조건절은 모두 필터 조건이다. 테이블 필터 조건은 쿼리 수행 다음 단계로 전달하거나 최종 결과집합에 포함할지를 결정한다.
  • 옵티마이저의 비용 계산 원리
    • 비용 = 인덱스 수직적 탐색 비용 + 인덱스 수평적 탐색 비용 + 테이블 랜덤 엑세스 비용 = 인덱스 루트와 브랜치 레벨에서 읽는 블록 수 + 인덱스 리프 블록을 스캔하는 과정에 읽는 블록 수 + 테이블 엑세스 과정에 읽는 블록 수
  • 선행 컬럼이 모두 '=' 조건인 상태에서 첫 번째 나타나는 범위검색 조건이 인덱스 스캔 범위를 결정한다. 이들 조건이 인덱스 엑세스 조건이다. 나머지 인덱스 컬럼 조건은 모두 인덱스 필터 조건이다.
  • 아래 몇 가지 케이스를 제외하면, 인덱스 컬럼에 대한 조건절은 모두 엑세스 조건에 표시된다. 첫 번째 나타나는 범위검색 조건 이후 조건절 컬럼은 스캔 범위를 줄이는 데 큰 역할을 못 하는데도 말이다.
    • 좌변 컬럼을 가공한 조건절
    • 왼쪽 '%' 또는 양쪽 '%' 기호를 사용한 like 조건절
    • 같은 컬럼에 대한 조건절이 두 개 이상일 때, 인덱스 엑세스 조건으로 선택되지 못한 조건절
    • OR Expansion 또는 INLIST ITERATOR로 선택되지 못한 OR 또는 IN 조건절
  • 따라서, 실행 계획과 상관없이 첫 번째 범위검색 조건까지가 인덱스 엑세스 조건이고 나머지는 필터 조건이라고 이해하자.
  • BETWEEN 조건을 IN-LIST로 전환하면 IN-LIST 개수만큼 UNION ALL 브랜치가 생성되고 각 브랜치마다 모든 컬럼을 '=' 조건으로 검색하므로 앞서 선두 컬럼에 BETWEEN을 사용할 때와 같은 비효율이 사라진다. 
  • Index Skip Scan 방식으로 유도해도 비슷한 효과를 얻을 수 있다.
  • IN-List 값들을 코드 테이블로 관리하고 있을 때는 NL방식 조인이나 서브쿼리로 구현하면 된다. NL 조인을 학습하고 나면 이해됨(4장)
  • BETWEEN 조건 때문에 리프 블록을 많이 스캔하는 비효율보다 IN-List 개수만큼 브랜치 블록을 반복 탐색하는 비효율이 더 클 수도 있다. 따라서 인덱스 스캔 과정에 선택되는 레코드들이 서로 멀리 떨어져 있을 때만 유용하다는 사실을 기억해야 한다. BETWEEN 조건 때문에 인덱스를 비효율적으로 스캔하더라도 블록 I/O 측면에서는 대개 소량에 그치는 경우가 많다. 인덱스 리프 블록에는 테이블 블록과 달리 매우 많은 레코드가 담기기 때문이다.
  • Index Skip Scan을 활용하면 BETWEEN 조건을 굳이 조건절을 바꿔가며 IN-List 조건으로 변환하지 않아도 같은 효과를 낼 수 있다.
  • IN조건은 '='가 아니다. IN 조건이 '='이 되려면 IN-List Iterator 방식으로 풀려야만 한다. 그렇지 않으면 IN 조건은 필터 조건이다. IN조건이 엑세스 조건으로서 의미있는 역할을 하려면, 해당 컬럼의 데이터가 아주 많아야 한다. 그렇지 않은 상황에서는 필터 방식으로 처리되는 것이 오히려 낫다.
  • num_index_keys 힌트를 사용하여 IN-List를 엑세스 조건 또는 필터 조건으로 유도할 수 있다. 세 번째 인자 'n'은 n번째 컬럼까지만 엑세스 조건으로 사용하라는 의미이다.
  • LIKE와 BETWEEN은 둘 다 범위 검색 조건으로서, 비효율 원리가 똑같이 적용되지만 데이터 분포와 조건절 값에 따라 인덱스 스캔량이 서로 다를 수 있다. LIKE보다 BETWEEN이 낫다.
  • 날짜의 경우 LIKE '2025%' 202500 이 있을까봐, 202513이 있을까봐 더 스캔하게 되므로 BETWEEN으로 좀 더 범위를 제한하는 것이 낫다.
  • 옵티마이저에 의한 OR Expansion 쿼리 변환이 기본적으로 작동하지 않으므로 인덱스 선두 컬럼에 대한 옵션 조건에 (is null)OR 조건을 사용해선 안된다. 옵션 조건 컬럼을 선두에 두고 인덱스를 구성해도 이를 사용할 수 없다. 이 방식의 유일한 장점은 옵션 조건 컬럼이 NULL 허용 컬럼이더라도 결과집합을 보장한다는 것 뿐이다. (옵션조건이란 필수값이 아닌 조회조건을 의미한다. 즉 사용자 입력에 따라 파라미터가 null일 수도 있는 조건)
SELECT * 
FROM 고객 
WHERE (:cust_id IS NULL OR 고객ID = :cust_id) 
AND 거래일자 BETWEEN :dt1 AND :dt2;
-- 이 경우 고객ID에 null이 들어오면 table full scan을 할 가능성이 높다.
  • 즉, 이 얘기는 인덱스 선두 컬럼에 OR 조건을 사용하면 안 좋다는 이야기지. OR 조건을 쓸 때 성능 개선을 위해 어떻게 해야하는가에 대한 이야기가 아니다. 이런 경우에는 거래일자가 선두인 인덱스가 있는 게 좋다.
  • 인덱스에 포함되지 않은 컬럼에 대한 옵션 조건은 어차피 테이블에서 필터링할 수 박에 없으므로 그럴 때는 OR 조건을 사용해도 무방하다. OR 조건을 활용한 옵션 조건 처리를 정리하면 다음과 같다.
    • 인덱스 엑세스 조건으로 사용 불가
    • 인덱스 필터 조건으로도 사용 불가
    • 테이블 필터 조건으로만 사용 가능
    • 단, 인덱스 구성 컬럼 중 하나 이상이 Not Null 컬럼이면(모든 테이블 레코드가 인덱스에 저장되어 있음이 보장), 18c 부터 인덱스 필터 조건으로 사용 가능

 

728x90
728x90


It is a Type-Pool which has all of the TYPES statements for the ALV. You can specify its use by adding the TYPE-POOLS statement to your program.

 

SLIS를 type-pool로 선언하여 프로그램에서 사용하겠다는 의미. TYPE-POOL이란 SAP에서 제공하는 각종 타입 및 Constants가 선언되어 있는 그룹이다. 

 

 

TABLES *table_wa.

This statement declares an additional table work area *table_wa, whose data type, like that of the regular TABLES statement with its flat structured data type table_wa, is taken from ABAP Dictionary.

The additional table work area can be used just like the regular table work area. This applies in particular to obsolete database accesses.

 

PARAMETERS : -----   MATCHCODE OBJECT [Search Help명]

For many entry fields, but not all, SAP provides a search function called a Matchcode. Matchcodes allow you to select a value from a list or search for a value by categories of data if there are many possibilities.

EX) Search Help SD_DEBI (Customers: SD collective search help)

 

MEMORY ID

when u are passing on values from one program to another program then we can use memory ID's

Program1

EXPORT EXCH1 TO MEMORY ID 'EXCH'.

Program2

IMPORT EXCH1 FROM MEMORY ID 'EXCH'.

EXCH1 is the variable name whose value u are exporting and importing from..

This is only valid till the particular session,

dont forget to award points if found helpful

 

 

NO-EXTENSION : Multiple 입력가능한 화살표 버튼 제거. NO INTERVALS : SELECT-OPTIONS의 간격 기능 제거

 

 

 

*  MODIF ID
*  - 화면 구성요소의 속성(입력 가능, 불가능 등)을 일괄적으로 변경하기 위해
*    해당 변수들을 GROUP으로 묶어줌
*  - AT SELECTION-SCREEN OUTPUT 에서 사용

 

 

 

AT SELECTION-SCREEN OUTPUT · 실행 시점: 선택 화면이 사용자에게 처음 표시되기 직전에 실행됨

The assignments to input fields in the event block AT SELECTION-SCREEN OUTPUT always affect the selection screen and overwrite the user input from previous displays of the same selection screen. Assignments in the event blocks LOAD-OF-PROGRAM or INITIALIZATION, on the other hand, only have an effect the first time the program starts.

 

difference between at selection screen and at selection screen output.

AT SELECTION SCREEN:

when user enters the values in the fields of the selection screen and clicks on execution button,this event gets triggered.this event is basically for checking the value entered by the user for the field of the selection screen i.e data validity checking.this event is for entire selection screen.

AT SELECTION SCREEN OUTPUT:

This event is executed at PBO of the selection screen every time the user presses

ENTER - in contrast to INITIALIZATION . Therefore, this event is not suitable for setting selection screen default values.

Also, since AT SELECTION-SCREEN OUTPUT is first executed after the variant is imported (if a variant is used)

and after adopting any values specified under SUBMIT in the WITH clause, changing the report parameters

or the selection options in AT SELECTION-SCREEN OUTPUT would destroy the specified values.

Here, however, you can use LOOP AT SCREEN or MODIFY SCREEN to change the input/output attributes of selection screen fields.

Example

Output all fields of the SELECT-OPTION NAME highlighted:

SELECT-OPTIONS NAME FOR SY-REPID MODIF ID XYZ.

....

AT SELECTION-SCREEN OUTPUT.

LOOP AT SCREEN.

CHECK SCREEN-GROUP1 = 'XYZ'.

SCREEN-INTENSIFIED = '1'.

MODIFY SCREEN.

ENDLOOP.

The addition MODIF ID XYZ to the key word SELECT-OPTIONS

assigns all fields of the selection option NAME to a group you can read in the field SCREEN-GROUP1 .

At PBO of the selection screen, all these fields are then set to highlighted

 

=====================================================================

 

SY-REPID : Current Program, 현재 프로그램의 ID 

SY-CPROG : Calling Program, 현재 프로그램(Function, Class method 등)을 호출한 프로그램의 ID

 

 

SLIS_LAYOUT_ALV: syructure layout of the report

It is used to for defining the layout of your ALV report

like

ls_layout TYPE slis_layout_alv.

and we define different paramters of this layout like

ls_layout-group_change_edit = 'X'.

ls_layout-colwidth_optimize = 'X'.

ls_layout-zebra = 'X'.

ls_layout-detail_popup = 'X'.

ls_layout-get_selinfos = 'X'.

ls_layout-max_linesize = '200'.

 

 

SLIS_T_FIELDCAT_ALV

This internal table contains the field attributes. This internal table can be populated automatically by using ‘REUSE_ALV_FIELDCATALOG_MERGE’.

It is nothing but a type group to declare fieldcat table for alv display. The fieldcat is nothing but the output display properties of each field in the alv report.

 

 

SLIS_T_EVENT: This is to set the events (for display the header data).

 

SLIS_T_LISTHEADER

slis_t_listheader gives the heading of a list.It is composed of the list title in the title bar and of any column headings. These can be maintained as part of the text elements of an ABAP program for the standard page header.

 

 

BSID : 고객에 대한 미결(채권)을 관리하는 테이블.(반제가 되면 삭제되고, BSAD로 이관)

BSAD : 고객에 대한 반제를 관리하는 테이블

728x90
728x90

 

SELECT A~VBELN " SALES ORDER DOCUMENT
     , A~POSNR " SALES ORDER ITEM
     , C~VBELN " DEVLIVERY DOCUMENT
     , C~POSNR " DELIVERY ITEM
     , E~VBELN " BILLING DOCUMENT
     , E~POSNR " BILLING ITEM
     , F~FKSTO " BILLING CANCEL FLAG
     , F~SFAKN " CANCELED BILLING DOC
  FROM            VBAP AS A
  LEFT OUTER JOIN VBFA AS B ON  B~VBELV   = A~VBELN
                            AND B~POSNV   = A~POSNR
                            AND B~VBTYP_N = 'J'
  LEFT OUTER JOIN LIPS AS C ON  C~VBELN = B~VBELN
                            AND C~POSNR = B~POSNN
  LEFT OUTER JOIN VBFA AS D ON  D~VBELV = C~VBELN
                            AND D~POSNV = C~POSNR
                            AND ( D~VBTYP_N = 'M' OR D~VBTYP_N = 'N' )
  LEFT OUTER JOIN VBRP AS E ON  E~VBELN = D~VBELN
                            AND E~POSNR = D~POSNN
  LEFT OUTER JOIN VBRK AS F ON  F~VBELN = E~VBELN  
  
  WHERE
  • VBFA 매핑 테이블을 통해 SD 문서의 Sales Order , Delivery, Billing 연결
  • 다만, 매출조정이 Sales Order 에서 Delivery 없이 Billing 처리되거나 반품오더 처럼 Billing을 Order 기준으로 하는 경우는 가져오지 못함
  • FKSTO, SFAKN으로 취소 빌링들 필터링 가능
  • Sales Order 진행 상황 등을 확인하기 위해 사용가능
728x90
728x90

친절한 SQLP 튜닝

2장

  • 인덱스 탐색 과정은 수직적 탐색과 수평적 탐색 두 단계로 이루어진다.
  • 인덱스는 큰 테이블에서 소량 데이터를 검색할 때 사용한다.
  • 인덱스 튜닝의 두 가지 핵심요소
    • 인덱스 스캔 과정에서 발생하는 비효율 줄이는 것 = 인덱스 스캔 효율화 튜닝
    • 테이블 엑세스 횟수를 줄이는 것 = 랜덤 엑세스 최소화 튜닝
  • IOT, 클러스터, 파티션, Prefetch, Batch I/O 등의 기능들의 본질은 랜덤 I/O의 최소화이다.
  • NL 조인이 대량 조인할 때 느린 이유도 랜덤I/O 때문이다. 이를 위해 소트머지 조인과 해시 조인이 개발되었다.
  • DBMS는 일반적으로 B*Tree 인덱스를 사용한다.
  • Root가 위쪽에 있고, Branch를 거쳐 맨 아래 Leaf가 있다.
  • Root와 Branch 블록에 있는 각 레코드는 하위 블록에 대한 주소값을 갖는다.
  • Root와 Branch 블록에는 주소값을 갖지 않는 LMC(Leftmost Child)라는 특별한 레코드가 있다.
  • Leaf 블록에 저장된 각 레코드는 키값 순으로 정렬되어 있을 뿐만 아니라 테이블 레코드를 가리키는 주소값, 즉 ROWID를 갖는다.
    • ROWID = 데이터 블록 주소 + 로우 번호
    • 데이터 블록 주소 = 데이터 파일 번호 + 블록 번호
    • 블록 번호 : 데이터 파일 내에서 부여한 상대적 순번
    • 로우 번호 : 블록 내 순번
  • 인덱스 수직적 탐색 : 인덱스 스캔 시작지점을 찾는 과정 = 인덱스 레코드 중 조건을 만족하는 첫 번째 레코드를 찾는 과정이다. 즉, 인덱스 스캔 시작지점을 찾는 과정이다.
  • 인덱스 수평적 탐색 : 데이터를  찾는 과정 = 찾고자 하는 데이터가 더 안 나타날 때까지 인덱스 리프 블록을 수평적으로 스캔한다.
    • 조건절에 만족하는 데이터를 모두 찾기 위해서, ROWID를 얻기 위해서 수평적 탐색을 수행한다.
  • 인덱스 선두 컬럼을 모두 등치(=) 조건으로 검색할 때는 어느 컬럼을 인덱스 앞쪽에 두든 블록 I/O 개수가 같으므로 성능도 똑같다. 즉 , 선두 컬럼이 모두 등치 조건이면 수직적 탐색 과정의 비용이 같다는 것이다.
  • B*Tree 인덱스에서 어떤 값으로 탐색하더라도 인덱스 루트에서 리프 블록에 도달하기까지 읽는 블록 수가 같다. 

 

  • 인덱스 기본 사용법은 인덱스를 Range Scan 하는 방법을 의미한다.
  • 인덱스 컬럼(정확히는 선두 컬럼)을 가공하지 않아야 인덱스를 정상적으로 사용할 수 있다. '인덱스를 정상적으로 사용한다'는 표현은 리프 블록에서 스캔 시작점을 찾아 거기서부터 스캔하다가 중간에 멈추는 것을 의미한다. 즉, 리프 블록 일부만 스캔하는 Index Range Scan을 의미한다.
  • 인덱스 컬럼을 가공해도 인덱스를 사용할 수는 있지만, 스캔 시작점을 찾을 수 없고 멈출 수도 없어 리프 블록 전체를 스캔해야만 한다. 즉, 일부가 아닌 전체를 스캔하는 Index Full Scan 방식으로 작동한다.
  • Index Range Scan은 인덱스에서 일정 범위를 스캔한다는 뜻이다. 일정 범위를 스캔하려면 시작지점과 끝지점이 있어야 한다. 인덱스 컬럼을 가공했을 때 인덱스 스캔 시작점을 찾을 수 없기 때문에 정상적으로 사용할 수 없다.
  • OR 조건식을 SQL 옵티마이저가 UNION ALL 형태로 변환할 수 있는데 이를 'OR Expansion'이라고 한다.
  • use_concat 힌트를 통해 'OR Expansion'를 유도할 수 있다.
  • IN 조건은 OR 조건을 표현하는 다른 방식일 뿐이다. IN 조건절에 대해서는 SQL 옵티마이저가 IN-List Iterator 방식을 사용한다. IN-List 개수만큼 Index Range Scan을 반복하는 것이다. UNION ALL 방식으로 변환한 것과 같은 효과를 얻을 수 있다.
  • 인덱스를 Range Scan 하기 위한 가장 첫 번째 조건은 인덱스 선두 컬럼이 조건절에 있어야 한다는 사실이다. 가공하지 않은 상태로 말이다.
  • 문제는, 인덱스를 Range Scan 한다고 해서 항상 성능이 좋은 것은 아니라는 것이다.
  • '인덱스를 탄다'라는 표현은 '인덱스를 Range Scan한다'와 같은 의미인 셈이다. 그런데 인덱스를 정말 잘 타는지는 인덱스 리프 블록에서 스캔하는 양을 따져봐야 알 수 있다.
  • 인덱스 컬럼을 가공해도 인덱스를 사용할 순 있지만, 찾고자 하는 데이터가 전체 구간에 흩어져 있기 때문에 Range Scan이 불가능하거나 비효율이 발생한다.
  • 옵티마이저는 SQL에 ORDER BY가 있어도 정렬 연산을 따로 수행하지 않는다. 인덱스를 스캔하면서 출력한 결과 집합은 어차피 인덱스 컬럼 순으로 정렬되기 때문이다. 따라서 실행계획에 SORT 연산이 없다.
  • 조건절이 아닌 ORDER BY 또는 SELECT-LIST에서 컬럼을 가공함으로 인해 인덱스를 정상적으로 사용할 수 없는 경우도 종종 있다.
  • 인덱스를 이용해 정렬 연산 없이 최소 또는 최대값을 빠르게 찾을 때 인덱스 리프블록의 왼쪽(MIN) 또는 오른쪽(MAX)에서 레코드 하나(FIRST ROW)만 읽고 멈춘다.
  • 인덱스는 문자열 기준으로 정렬돼 있는데 이를 숫자값으로 바꾼 값 기준으로 최소 또는 최대값, ORDER BY를 요구하면 정렬 연산을 생략할 수 없다.
  • 오라클은 자동으로 형변환 처리해준다.
  • 오라클에서 숫자형과 문자형이 만나면 숫자형이 이긴다. 숫자형 컬럼 기준으로 문자형 컬럼을 변환한다는 뜻이다.연산자가 LIKE 일 때는 다르다. LIKE 자체가 문자열 비교 연산자이므로 이때는 문자형 기준으로 숫자형 컬럼이 변환된다. 숫자형 컬럼을 LIKE 조건으로 검색하면 자동 형변환이 발생해 인덱스 엑세스 조건으로 사용되지 못한다.
  • 날짜형과 문자형이 만나면 날짜형이 이긴다.
  • DECODE(A, B, C, D)를 처리 할 때 'A = B' 이면 C를 반환하고, 아니면 D를 반환한다. 이때 반환값의 데이터 타입은 세 번째 인자 C에 의해 결정된다. 세 번째 인자가 NULL 값이면 네 번 째 인자가 숫자형이어도 문자열로 변환하고, 문자열 기준으로 MAX() 를 수행하게 된다. 950보다 5000이 크다고 판단하게 된다.
  • SQL 성능 원리를 잘 모르는 개발자는 TO_CHAR, TO_DATE, TO_NUMBER 같은 형변환 함수를 의도적으로 생략하곤 한다. 이들 함수를 생략하면 연산횟수가 줄어 성능이 더 좋지 않을까 생각하기 때문이다. SQL 성능은 그런 데서 결정되는 게 아니라 블록 I/O를 줄일 수 있느냐 없느냐에서 결정된다. 형변환 함수를 생략한다고 연산 횟수가 주는 것도 아니다. 개발자가 형변환 함수를 생략해도 옵티마이저가 자동으로 생성한다.
  • Index Range Scan 이외에도 Index Full Scan, Index Uinque Scan, Index Skip Scan, Index Fast Full Scan 등이 있다.
  • Index Range Scan은 인덱스 루트에서 리프 블록까지 수직적으로 탐색한 후에 필요한 범위만 스캔한다.
  • 선두 컬럼을 가공하지 않은 상태로 조건절에 사용하면 Index Range Scan이 무조건 가능하다. 그러므로 성능은 인덱스 스캔 범위, 테이블  엑세스 횟수를 얼마나 줄일 수 있느냐로 결정된다.
  • Index Full Scan은 수직적 탐색없이 인덱스 리프 블록을 처음부터 끝까지 수평적으로 탐색하는 방식이다.
  • Index Full Scan은 대개 데이터 검색을 위한 최적의 인덱스가 없을 때 차선으로 선택된다.
  • 대용량 테이블이어서 옵티마이저는 Table Full Scan을 고려하지만, 대용량 테이블의 경우 Table Full Scan이 부담스러워서 옵티마이저는 인덱스 활용을 고려하지 않을 수 없다.
  • /*+ first_rows */ 힌트를 사용하면 옵티마이저가 모드를 바꿔 인덱스 Full Scan을 고려한다. 이는 인덱스를 활용하므로 Sort 연산 생략 효과를 가져온다.
  • Index Unique Scan은 수직적 탐색만으로 데이터를 찾는 스캔 방식으로서 Unique 인덱스를 '=' 조건으로 탐색하는 경우에 작동한다.
  •  Unique 인덱스가 존재하는 컬럼은 중복 값이 입력되지 않게 DBMS가 데이터 정합성을 관리한다. 따라서 해당 인덱스 키 컬럼을 모두 '=' 조건으로 검색할 때는 데이터를 한 건 찾는 순간 더 이상 탐색할 필요가 없다.
  • Unique 결합 인덱스에 대해 일부 컬럼으로만 검색할 때도 Index Range Scan이 나타난다.
  • 옵티마이저는 Table Full Scan보다 I/O를 줄일 수 있거나 정렬된 결과를 쉽게 얻을 수 있다면, Index Full Scan을 사용하기도 한다.
  • 오라클은 인덱스 선두 컬럼이 조건절에 없어도 인덱스를 활용하는 새로운 스캔 방식이 Index Skip Scan이다. 이 스캔 방식을 조건절에 빠진 인덱스 선두 컬럼의 Distinct Value 개수가 적고 후행 컬럼의 Distinct Value 개수가 많을 때 유용하다. 예를 들어 고객 테이블에서 Distinct Value가 적은 컬럼을 '성별'이라고 할 수 있다.
  • Index Skip Scan은 루트 또는 브랜치 블록에서 읽은 컬럼 값 정보를 이용해 조건절에 부합하는 레코드를 포함할 '가능성이 있는' 리프 블록만 골라서 엑세스하는 스캔 방식이다.
  • 인덱스 선두 컬럼이 없을 때만 Index Skip Scan이 작동하는 것은 아니다.
  • 선두 컬럼에 대한 조건절은 있고, 중간 컬럼에 대한 조건절이 없는 경우에도 Skip Scan을 사용할 수 있다.
  • /*+ INDEX_SS(A 일별업종별거래_PK) */
  • Index Skip Scan을 사용한다면, 업종유형코드 = '01'인 구간에서 기준일자가 '20080501'보다 크거나 같고 '20080531'보다 작거나 같은 레코드를 '포함할 가능성이 있는 리프 블록만' 골라서 엑세스할 수 있다.
  • 선두컬럼이 부등호, BETWEEN, LIKE 같은 범위검색 조건일 때도 Index Skip Scan을 사용할 수 있다.
  • Index Range Scan을 사용한다면, 기준일자 BETWEEN 조건을 만족하는 인덱스 구간을 모두 스캔해야 한다. Index Skip Scan을 사용한다면, 기준일자 BETWEEN 조건을 만족하는 인덱스 구간에서도 업종유형코드 = '01'인 레코드를 포함할 가능성이 있는 리프 블록만 골라서 엑세스할 수 있다. 이처럼 Index Range Scan이 불가능하거나 효율적이지 못한 상황에서 Index Skip Scan이 종종 빛을 발한다.
  • 인덱스는 기본적으로 최적의 Index Range Scan을 목표로 설계해야 하며, 수행 횟수가 적은 SQL을 위해 인덱스를 추가하는 것이 비효율적일 때 이들 스캔 방식을 차선책으로 활용하는 전략이 바람직하다.
  • Index Fast Full Scan이 Index Full Scan 보다 빠른 이유는, 논리적인 인덱스 트리 구조를 무시하고 인덱스 세그먼트 전체를 Multiblock I/O 방식으로 스캔하기 때문이다. 관련 힌트 : index_ffs, no_index_ffs 
  • Index Fast Full Scan은 물리적으로 디스크에 저장된 순서대로 인덱스 리프 블록들을 읽어들인다.
  • 결과집합이 인덱스 키 순서대로 정렬되지 않는다. 쿼리에 사용한 컬럼이 모두 인덱스에 포함돼 있을 때만 사용할 수 있다는 점도 기억할 필요가 있다.
  • Index Range Scan 또는 Index Full Scan과 달리, 인덱스가 파티션 돼 있지 않더라도 병렬 쿼리가 가능한 것도 중요한 특징 중 하나이다. 병렬 쿼리 시에는 Direct Path I/O 방식을 사용하기 때문에 속도가 더 빨라진다.
  • Table Full Scan은 읽어야 할 익스텐트 목록을 익스텐트 맵에서 얻는다. Index Fast Full Scan도 똑같은 방식을 사용한다.
  • Index Fast Full Scan 특징
    • 세그먼트 전체를 스캔
    • 결과집합 순서 보장 안 됨
    • Multiblock I/O
    • 병렬스캔 가능
    • 인덱스에 포함된 컬럼으로만 조회할 때 사용 가능
  • Index Full Scan 특징
    • 인덱스 (논리적) 구조를 따라 스캔
    • 결과집합 순서 보장
    • Single Block I/O
    • (파티션 돼 있지 않다면) 병렬스캔 불가
    • 인덱스에 포함되지 않은 컬럼 조회시에도 사용 가능
  • Index Range Scan Descending은 인덱스를 뒤에서부터 앞쪽으로 스캔하기 때문에 내림차순으로 정렬된 결과집합을 얻는다는 점만 다르다.
  • 내림차순 Order by나 MAX 값을 구하고자 하는 경우 등에 사용된다.

 

728x90
728x90

 

SHOW DATABASES;

CREATE DATABASE company;

USE <DATABSENAME>

 

SELECT DATABASE();

 

 

  • MySQL 에서는 databse와 schema가 같은 뜻
    • CREATE DATABSE company = CREATE SCHEMA comapny
    • i.g.) PostgreSQL에서는 SCHEMA가 DATABASE의 namespace를 의미
    • 오라클에서는 유저가 소유한 논리적 데이터 구조,즉, 스키마 오브젝트(테이블,인덱스, 뷰 등)의 집합, 유저=SCHEMA라고 할 수 있다. 서로 다른 유저가 같은 스키마 오브젝트들을 공유할 수 있음.

 

DATA TYPE

숫자타입 종류 설명 사이즈 MySQL Type
정수 정수를 저장할 때 사용 1 byte TINYINT
2 byte SMALLINT
3 byte MEDIUMINT
4 byte INT or INTEGER
8 byte BIGINT
부동 소수점 방식(floating-point) - 실수(real number)를 저장할 때 사용
- 고정 소수점 방식에 비해 정확하지 않다.
4 byte FLOAT
8byte DOUBLE or DOUBLE PRECISION
고정 소수점 방식(fixed-point) - 실수를 정확하게 저장할 때 사용 (precision, scale)
- DECIMAL(5, 2) =>
[-999.99 ~ 999.99]
variable DECIMAL OR NUMERIC
  • SQL 표준에서 DECIMAL과 NUMERIC의 차이
    • DECIMAL : 5자리 넘어서도 저장
    • NUMERIC : 5자리 넘어서면 저장하지 않음
    • 하지만 MYSQL은 둘 다 엄격하게 그 자릿수만 저장(표준의 NUMERIC 방식)
문자열 타입 종류 설명 MySQL
고정 크기 문자열 - 최대 몇 개의 문자를 가지는 문자열을 저장할 지를 지정
- 저장될 문자열 길이가 최대 길이보다 작으면 나머지는 space로 채움
CHAR(n)
(0 <= n <= 255)
가변 크기 문자열 - 최대 몇 개의 문자를 가지는 문자열을 저장할 지를 지정
- 저장될 문자열의 길이 만큼만 저장
VARCHAR(n)
(0 <= n <= 65,535)
사이즈가 큰 문자열 - 사이즈가 큰 문자열을 저장할 때 사용 TINYTEXT
TEXT
MEDIUMTEXT
LONGTEXT

 

날짜, 시간 타입 종류 설명 MySQL
날짜 - 년, 월, 일을 저장 DATE
'1000-01-01' ~ '9999-12-31'
시간 - 시,분,초를 저장 TIME
'-838:59:59' ~ '838:59:59'
날짜와 시간 - 날짜와 시간을 같이 표현
- TIMESTAMP는 time-zone 반영됨
DATETIME
TIMESTAMP

 

기타 타입 설명 MySQL
byte-string byte string을 저장, 보안 용도 등으로 사용 BINARY
VARBINARY
BLOB type
boolean - true, false를 저장
- MySQL에는 없음
TINYINT로 대체
위치 위치 관련 정보 저장 GEOMETRY
etc
JSON - json 형태의 데이터를 저장 JSON

 

Constraints(제약 조건)

  • PRIMARY KEY : table의 tuple을 식별하기 위해 사용, 하나 이상의 attribute로 구성
    • Primary key는 중복된 값을 가질 수 없으며, NULL도 값으로 가질 수 없다.(PK가 가지는 속성 하나라도 NULL이면 안됨)
  • UNIQUE : 중복된 값을 가질 수 없다.
    • 단, NULL은 중복을 허용할 수도 있다(RDBMS 마다 다름)
  • NOT NULL
    • 해당 속성은 NULL 값을 가질 수 없다.
  • DEAFULT : 기본값을 정의할 때 사용
    • 새로운 TUPLE 저장 시 해당 attribute 의 값이 없다면 이 기본값을 저장
  • CHECK 
    • attribute 의 값을 제한할 때 사용
  • FOREIGN KEY
    • attribute가 다른 table의 primary keyunique key를 참조할 때 사용
    • Options
      • CASCADE : 참조값의 삭제/변경을 그대로 반영
      • SET NULL : 참조값이 삭제/변경 시 NULL로 변경
      • RESTRICT : 참조값이 삭제/변경되는 것을 금지
      • NO ACTION : RESTRICT와 유사 (MySQL 미지원)
      • SET DEFAULT : 참조값이 삭제/변경 시 DEFAULT값으로 변경 (MySQL 미지원)
  • CONTRAINT 이름 명시
    • 이름을 붙이면 어떤 CONTRAINT를 위반했는 지 쉽게 파악 가능
    • CONSTRAINT를 삭제하고 싶을 때 해당 이름으로 삭제 가능
  • ALTER TABLE로 스키마 변경하기
    • ADD, RENAME COLUMN, MODIFY COLUMN, RENAME TO, ADD PRIMARY KEY.. 등 여러 옵션있음
    • 이미 서비스 중인 TABLE의 SCHEMA를 변경하는 것이라면 변경 작업 때문에 서비스의 백엔드에 영향이 없을지 검토한 후에 변경하는 것이 중요.

 

CREATE TABLE
    DEPARTMENT (
        ID INT PRIMARY KEY,
        NAME VARCHAR(20) NOT NULL UNIQUE,
        LEADER_ID INT
    );
    
    
    CREATE TABLE EMPLOYEE (
    ID         INT PRIMARY KEY,
    NAME       VARCHAR(30) NOT NULL,
    BIRTH_DATE DATE,
    SEX        CHAR(1) CHECK(SEX IN ('M','F')),
    POSITION   VARCHAR(10),
    SALARY     INT DEFAULT 50000000,
    DEPT_ID    INT,
    
    FOREIGN KEY (DEPT_ID) REFERENCES DEPARTMENT(ID)
        ON DELETE SET NULL ON UPDATE CASCADE,
    CHECK (SALARY >= 50000000)
);


CREATE TABLE PROJECT (
    ID         INT PRIMARY KEY,
    NAME       VARCHAR(20) NOT NULL UNIQUE,
    LEADER_ID  INT,
    START_DATE DATE,
    END_DATE   DATE,
    
    FOREIGN KEY (LEADER_ID) REFERENCES EMPLOYEE(ID)
        ON DELETE SET NULL ON UPDATE CASCADE,
    CHECK (START_DATE < END_DATE)
);

CREATE TABLE WORKS_ON (
    EMPL_ID    INT,
    PROJ_ID    INT,
    PRIMARY KEY(EMPL_ID, PROJ_ID),
    FOREIGN KEY(EMPL_ID) REFERENCES EMPLOYEE(ID)
        ON DELETE CASCADE ON UPDATE CASCADE,
    FOREIGN KEY(PROJ_ID) REFERENCES PROJECT(ID)
        ON DELETE CASCADE ON UPDATE CASCADE
);

-- DEPARTMENT의 LEADER_ID FOREIGN KEY 추가
ALTER TABLE DEPARTMENT ADD FOREIGN KEY (LEADER_ID)
    REFERENCES EMPLOYEE(ID)
        ON UPDATE CASCADE
        ON DELETE SET NULL;

 

 

* 데이터베이스 구조를 정의할 때는 구축하는 서비스의 스펙과 데이터 일관성, 편의성, 확장성 등을 종합적으로 고려하여 DB 스키마를 적절하게 정의해야 한다.

728x90
728x90

 

 

  • Demultiplexing : Internet Layer로부터 받은 segment나 datagram에 있는 payload를 적절한 socket으로 전달하는 것
  • Multiplexing : Internet Layer로 socket들로부터 데이터를 수집해서 각각 segment나 datagram으로 만든 후 보내는 것
  • Internet Layer의 TCP/UDP 정보가 있어 해당 정보를 확인하여 각 Transport Layer 양식에 맞춰 Transport Layer에 보내줌
  • Transport가 어떤 프로세스(Socket)으로 데이터를 보내야 하는지 어떻게 판단할까?
    • port로 구분
      • UDP : Destination Port로 보내면 끝
      • TCP
        • syn flag가 1이면 listening socket으로 보내고
        • 이미 연결된 Connection이면 TCP, IP Address, Port 다 같은 곳으로 보냄

 

 

*Layer는 

728x90
728x90
  • 데이터베이스 시스템의 데이터 모델 중 가장 많이 사용되는 것이 관계 데이터 모델이다.
  • 관계 데이터 모델은 테이블 형태로 데이터 간의 관계를 표현한다.

Relation

  • relation은 row와 column으로 구성된 table을 말한다.

  • 위 테이블은 고객의 주문 내역 정보를 담고 있는 릴레이션이다.
  • 이 릴레이션은 주문번호, 고객코드, 제품코드, 수량, 가격이라는 5개의 집합으로 구성되어 있다.
  • 각 집합의 값은 각각 다음과 같이 원소를 가지고 있다.
    • 주문번호 = { ABCDE00001, ABCDE00002 }
    • 고객코드 = { 123456, 654321 }
    • 제품코드 = { 1002, 1004 }
    • 수량 = { 10, 4 }
    • 가격 = { 500000, 40000 }
  • 첫 번째 행은 5개의 집합에서 각각 원소 1 개씩 선택하여 만들어졌는데, 이것이 원소들이 맺은 관계(relationship)이다.
  • 릴레이션 스키마는 릴레이션의 데이터 구조를 뜻하며 저장된 데이터는 인스턴스라고 표현한다.

  • 릴레이션 스키마의 요소
    • 속성(attribute) ; 릴레이션 스키마의 열
    • 도메인(domain) : 속성이 가질 수 있는 값의 집합, 더 나눌 수 없는 값(atomic)
      • 수량, 가격, 단가는 원화 표시이기 때문에 둘다 ‘자연수’라는 도메인을 같는다.
      • 즉, 이 릴레이션에서 수량, 가격, 단가는 ‘자연수’를 도메인으로 하는 attribute이다.
      • 속성은 그 column의 원소들이 나타내는 값의 의미를 나타내고 도메인은 어떤 값들이 그 원소가 될 수 있는 지를 정의한다.
    • 차수(degree) : 속성의 개수
    • 투플(tuple) : 릴레이션의 행
    • 카디날리티(cardinality) : 투플의 수
    • 릴레이션(relation) : 투플의 집합(set of tuples)

  • 릴레이션 용어는 일반적으로 잘 쓰이지 않는다. 같은 의미로 통용되는 다른 단어가 주로 쓰인다.
    • table, row, column, header, data, record, field 정도의 용어가 자주 쓰인다.
    • 위 표에 나온 정의대로 쓰인다기 보다는 table의 header, table의 field, table의 record 처럼 파일시스템 용어를 섞어 쓰기도 한다.

릴레이션의 특징

  • 속성은 단일 값을 가진다.
  • 속성은 서로 다른 이름을 가진다.(하지만 도메인은 같을 수 있다.)
  • 한 속성의 값은 모두 같은 도메인 값을 가진다. (= 도메인이라는 집합에 있는 값을 가져야 한다)
  • 속성의 순서는 상관없다. 속성의 순서가 달라도 릴레이션 스키마는 같은 것으로 본다. (주문번호, 고객번호) 와 (고객번호, 주문번호)는 같은 스키마이다.
  • 릴레이션 내의 중복된 투플은 허용하지 않는다.
    • 유니크한 키 값이 없으면 테이블 내에 같은 데이터를 가진 행이 존재할 수 있다.
    • 하지만 이 경우에도 저장된 데이터들은 사용자가 입력하지 않았더라도 시스템 내부에선 각각 다른 index 번호를 가지기 때문에 사실은 다른 투플이라고 볼 수 있다.
  • 투플의 순서가 달라도 같은 릴레이션이다. (즉, 정렬과 상관없다.)

Super Key

  • relation에서 tuples를 unique하게 식별할 수 있는 attributes set(즉, 속성 하나여도 되고, 여러 개의 속성의 집합이어도 된다는 것)
  • 위 테이블에선 ‘주문번호’가 super key가 될 수 있다.
  • 마찬가지로 (주문번호, 고객코드) 또한 super key가 될 수 있다.
  • 다만 키를 구성하는 속성이 많으면 그만큼 관계 표현이 복잡해지므로 최소한의 속성 집합에 관심을 갖는다.

Candidate Key

  • 후보키(candidate key)는 투플을 유일하게 식별할 수 있는 속성의 최소 집합. 즉 위 테이블에선 ‘주문번호’만 해당된다. (주문번호, 고객코드)는 고객코드가 없어도 되기 때문에 ‘주문번호’가 유일하게 하나의 속성으로 투플을 식별할 수 있는 속성이므로 후보키가 될 수 있다.

Primary Key

  • 투플들을 unique하게 식별하기 위해 채택된 candidate key
  • 위에서는 ‘주문번호’만 될 수 있다.

Unique Key = Alternate key

  • Primary Key가 아닌 candidate key

Foreign Key

  • 다른 relation의 PK(primary key)를 참조하는 attributes set
  • 위 주문정보 테이블에서 ‘고객코드’는 고객 정보 테이블이 있고 고객코드가 그 테이블의 PK라면 foreign key에 해당한다.

Constraints(제약조건)

  • domain constraints : attribute의 value는 해당 attribute의 domain에 속한 value여야 한다.
    • ‘가격’의 도메인은 자연수이기 때문에 ‘10.25’와 같은 값이 올 수 없다.
  • key constraints : 서로 다른 tuples은 같은 value의 key를 가질 수 없다.
    • 즉 주문정보 테이블에서 ‘주문번호’가 같을 수 없다.
  • NULL value constraint : attribute가 NOT NULL로 명시되어 있다면 NULL을 가질 수 없다.
  • Entity integriry contraint : PK는 value에 NULL을 가질 수 없다.
    • NULL이 가능하다면 투플을 식별할 수가 없게 되기 때문이다.
  • Referential integrity contraint : FK와 PK와 도메인이 같아야 하고 PK에 없는 values를 FK가 값으로 가질 수 없다.
    • 즉, ‘주문정보’ 테이블의 ‘고객코드’는 ‘고객’ 테이블의 ‘고객코드’와 도메인이 같아야하고 ‘고객’ 테이블에 있는 ‘고객코드‘값만을 참조할 수 있다.
728x90

+ Recent posts