<스키마> ※ 밑줄친 속성은 기본키(Primary Key)이며 ENROLL의 학번은 STUDENT의 학번을 참조하는 외래키(Foreign Key)이고, ENROLL의 강좌번호는 CLASS의 강좌번호를 참조하는 외래키이다. STUDENT(학번, 학과, 이름) CLASS(강좌번호, 시간, 강좌이름) ENROLL(학번, 강좌번호, 학점) |
---|
①
SELECT 학번,
MIN(이름)
FROM STUDENT S,
ENROLL E
GROUP BY E.학번
HAVING AVG(E.학점) >= 3.0
②
SELECT S.학번,
MIN(이름)
FROM STUDENT S,
ENROLL E
WHERE S.학번 = E.학번
HAVING AVG(E.학점) >= 3.0
③
SELECT S.학번,
MIN(이름)
FROM STUDENT S,
ENROLL E
WHERE S.학번 = E.학번
GROUP BY S.학번
HAVING AVG(E.학점) >= 3.0
④
SELECT 학번,
MIN(이름)
FROM STUDENT S,
ENROLL E
WHERE S.학번 = E.학번
GROUP BY S.학번
HAVING AVG(E.학점) > 3.0
<DEPT>
DEPT | DNAME |
---|---|
10 | RESEARCH |
30 | SALES |
<EMP>
DEPT | DNAME |
---|---|
30 | KING |
40 | WARD |
SELECT D.DEPTNO, E.DEPTNO
FROM DEPT D FULL OUTER JOIN EMP E
ON (D.DEPTNO = E.DEPTNO)
①
DEPTNO | DEPTNO |
---|---|
30 | 30 |
②
DEPTNO | DEPTNO |
---|---|
10 | |
30 | 30 |
③
DEPTNO | DEPTNO |
---|---|
30 | 30 |
40 |
④
DEPTNO | DEPTNO |
---|---|
10 | |
30 | 30 |
40 |
<테이블> 사원 (사번, 이름, 나이) 가족 (이름, 나이, 부양사번) ※ 가족 테이블의 부양사번은 사원 테이블의 사번을 참조하는 외래키(Foreign Key)이다. <SQL 문장> SELECT 이름 FROM 사원 WHERE ㉠ (SELECT * FROM 가족 WHERE ㉡ ) |
---|
① ㉠ : EXISTS ㉡ : 사번 = 부양사번
② ㉠ : EXISTS ㉡ : 사번 <> 부양사번
③ ㉠ : NOT EXISTS ㉡ : 사번 = 부양사번
④ ㉠ : NOT EXISTS ㉡ : 사번 <> 부양사번
create table 주문 (
주문번호 int not null
, 주문자명 varchar(20) null
, 주문금액 money null
, 주문일자 varchar(8) null
)
create unique index 주문_pk on 주문 (주문번호)
create index 주문_x01 on 주문 (주문자명)
create index 주문_x02 on 주문 (주문일자 , 주문금액)
① where 주문번호 between 1 and 10
② where 주문자명 like '%홍길동%'
③ where 주문일자 >= '20100901'
④ where 주문일자 = 20100901
create bitmap index 연도별지역별상품매출_bx1 on 연도별지역별상품매출(크기);
create bitmap index 연도별지역별상품매출_bx2 on 연도별지역별상품매출(색상);
create bitmap index 연도별지역별상품매출_bx3 on 연도별지역별상품매출(출시연도);
■ Distinct Value
크기 = { NULL, SMALL, MEDIUM, BIG }
색상 = { NULL, RED, GREEN, BLUE, BLACK, WHITE }
출시연도 = { NULL, 2001, 2002, 2003, 2004, 2005 2006, 2007, 2008, 2009, 2010 }
■ 데이터 분포는 모두 균일
① select count(*) from 연도별지역별상품매출 where 색상 is null;
② select count(*) from 연도별지역별상품매출
where (크기 = 'SMALL' or 크기 is null)
and 색상 = 'GREEN'
and 출시연도 = '2010';
③ select 색상, count(*) from 연도별지역별상품매출 group by 색상;
④ select sum(판매량), sum(판매금액)
from 연도별지역별상품매출
where 색상 = 'BLUE';
① SELECT 부서번호
FROM 직원 HAVING COUNT(*) > 3;
② SELECT 직원이름
FROM 직원 JOIN 부서
ON (직원.부서번호 = 부서.부서번호)
WHERE 부서명 = '인사과'
③ SELECT 전화번호
FROM 직원
WHERE 전화번호 = '777'
GROUP BY 전화번호
④ SELECT 직원이름
FROM 직원 JOIN 부서
USING (부서.부서번호)
WHERE 부서명 = '인사과'
① SELECT COL1, COL2 FROM TAB1 WHERE 1 = 2 ;
→ 실행 시 에러가 발생한다.
② SELECT NVL(COL1,'X') FROM TAB1 WHERE 1 = 2 ;
→ 실행 결과로 'X'를 반환한다.
③ SELECT NVL(MIN(COL1), 'X') FROM TAB1 WHERE 1 = 2;
→ 실행 결과로 'X'를 반환한다.
④ SELECT COL1, COL2 FROM TAB1 WHERE 1 = 2 ;
→ 실행 결과가 없다.(공집합)
SELECT *
FROM (SELECT ㉠ , EMP_NAME, HIRE_DATE, SAL, DEPT_NO
FROM EMP
WHERE DEPTNO = 30) X
WHERE HIRE_DATE BETWEEN TO_DATE('20100101','YYYYMMDD') AND TO_DATE('20101231','YYYYMMDD')
① TO_DATE(SYSDATE,'YYYYMMDD')
② ROWNUM
③ EMPNO
④ ROWID
고객번호 | 판매일자 | 판매금액 |
---|---|---|
100 | 20090701 | 1000 |
100 | 20090702 | 300 |
100 | 20090702 | 1000 |
200 | 20090701 | 2000 |
200 | 20090701 | 200 |
SELECT CUSTCODE 고객번호,
SALEDATE 판매일자,
SALE_AMT 판매금액,
SUM(SALE_AMT) OVER(PARTITION BY CUSTCODE ORDER BY SALEDATE
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AMT1
FROM SAL_TBL
①
고객번호 | 판매일자 | 판매금액 | AMT1 |
---|---|---|---|
100 | 20090701 | 300 | 2300 |
200 | 20090701 | 200 | 2200 |
②
고객번호 | 판매일자 | 판매금액 | AMT1 |
---|---|---|---|
100 | 20090702 | 1000 | 2300 |
200 | 20090701 | 2000 | 2200 |
③
고객번호 | 판매일자 | 판매금액 | AMT1 |
---|---|---|---|
100 | 20090701 | 1000 | 1000 |
100 | 20090702 | 300 | 1300 |
100 | 20090702 | 1000 | 2300 |
200 | 20090701 | 2000 | 2000 |
200 | 20090701 | 200 | 2200 |
④
고객번호 | 판매일자 | 판매금액 | AMT1 |
---|---|---|---|
100 | 20090701 | 1000 | 1000 |
100 | 20090702 | 300 | 2300 |
100 | 20090702 | 1000 | 2300 |
200 | 20090701 | 2000 | 2200 |
200 | 20090701 | 200 | 2200 |
SELECT SUM(NVL(A.C2, 0))
FROM TAB1 A, TAB2 B
WHERE A.C1 = :V1
① 조인 조건이 없어서 결과 건수가 여러 건이 된다.
② 조인 조건이 없다고 문법 오류가 발생하지는 않는다.
③ SUM(NVL(A.C2, 0))의 처리에 비효율이 존재한다.
④ 조인 조건이 없어서 CARTESIAN PRODUCT이 발생한다.
SELECT JOB,
ENAME,
SAL,
㉠() OVER(ORDER BY SAL DESC) QQ1
FROM EMP
JOB | ENAME | SAL | QQ1 |
---|---|---|---|
PRESIDENT | KING | 5000 | 1 |
ANALYST | FORD | 3000 | 2 |
ANALYST | SCOTT | 3000 | 2 |
MANAGER | JONES | 2975 | 4 |
MANAGER | BLAKE | 2850 | 5 |
MANAGER | CLARK | 2450 | 6 |
SALESMAN | ALLEN | 1600 | 7 |
SALESMAN | TURNER | 1500 | 8 |
CLERK | MILLER | 1300 | 9 |
SALESMAN | WARD | 1250 | 10 |
SALESMAN | MARTIN | 1250 | 10 |
CLERK | ADAMS | 1100 | 12 |
CLERK | JAMES | 950 | 13 |
CLERK | SMITH | 800 | 14 |
select * from 사원 where 직급 = '부장' and 부서 = '영업';
* 직급은 { 부장, 과장, 대리, 사원 }의 집합이다.
* 부서는 { 인사, 총무, 회계, 마케팅, 영업 }의 집합이다.
* 두 칼럼 모두 히스토그램 정보가 수집되어 있지 않다.
① 48
② 50
③ 55
④ 60
create table t ( a number, b char(3), c varchar2(10) )
partition by range(a) (
partition p1 values less than(100)
, partition p2 values less than(200)
, partition p3 values less than(maxvalue)
);
create index t_idx on t( b ) local ;
① Global Prefixed Partition Index
② Global NonPrefixed Partition Index
③ Local Prefixed Partition Index
④ Local NonPrefixed Partition Index
select EmployeeID, LastName, HireDate, Country
from Employees
order by HireDate
EmployeeID LastName HireDate Country
----------- ------------ ----------------------- --------
3 Leverling 1992-04-01 00:00:00.000 USA
1 Davolio 1992-05-01 00:00:00.000 USA
2 Fuller 1992-08-14 00:00:00.000 USA
4 Peacock 1993-05-03 00:00:00.000 USA
5 Buchanan 1993-10-17 00:00:00.000 UK
6 Suyama 1993-10-17 00:00:00.000 UK
7 King 1994-01-02 00:00:00.000 UK
8 Callahan 1994-03-05 00:00:00.000 USA
9 Dodsworth 1994-11-15 00:00:00.000 UK
① select EmployeeID, LastName, HireDate, Country
from (select EmployeeID, LastName, HireDate, Country
, rank () over (order by HireDate) as rnum
from Employees
) a
where a.rnum <= 5
order by EmployeeID
② select EmployeeID, LastName, HireDate, Country
from (select EmployeeID, LastName, HireDate, Country
, row_number() over (order by HireDate) as rnum
from Employees
) a
where a.rnum <= 5
order by EmployeeID
③ select EmployeeID, LastName, HireDate, Country
from Employees a
where HireDate in (select top 5 HireDate
from Employees
order by HireDate )
order by EmployeeID
④ select EmployeeID, LastName, HireDate, Country
from (select EmployeeID, LastName, HireDate, Country
, dense_rank() over (order by HireDate) as rnum
from Employees
) a
where a.rnum <= 5
order by EmployeeID
[Oracle 사례]
SELECT PLAYER_NAME 선수명, POSITION, NVL(POSITION,'없음') 포지션
FROM PLAYER WHERE TEAM_ID = 'K08'
[SQL Server 사례]
SELECT PLAYER_NAME 선수명, POSITION, ISNULL(POSITION,'없음') 포지션
FROM PLAYER WHERE TEAM_ID = 'K08'
[CASE 문장 사례]
SELECT PLAYER_NAME 선수명, POSITION, CASE WHEN ( ㉠ ) THEN '없음'
ELSE POSITION END AS 포지션
FROM PLAYER
WHERE TEAM_ID = 'K08'
가. SELECT DISTINCT 국가명
FROM 국가 x, 수출실적 y
WHERE x.국가코드= y.국가코드
AND y.수출년월 BETWEEN '200001' AND '201012';
나. SELECT 국가명
FROM 국가 x
WHERE EXISTS (SELECT 1
FROM 수출실적 y
WHERE y.국가코드= x.국가코드
AND y.수출년월 BETWEEN '200001' AND '201012');
① '가' SQL은 부분범위 처리가 가능하다.
② '나' SQL이 더 효율적이다.
③ OLTP 환경이냐, DW 환경이냐에 따라 두 SQL의 효율성이 다르다.
④ 두 SQL의 결과가 다르므로 효율성을 판단하는 것은 의미가 없다.
----------------------------- 아 래 --------------------------------------
call count cpu elapsed disk query current rows
------ ------- ------ --------- ------ -------- --------- -------
Parse 50000 0.51 0.51 0 0 0 0
Execute 50000 3.18 2.91 5000 10000 15000 50000
Fetch 0 0.00 0.00 0 0 0 0
------ ------- ------ --------- ------ -------- --------- -------
total 100000 3.70 3.43 5000 10000 15000 50000
--------------------------------------------------------------------------------
col1 col2 col3
=======================
10 20 NULL
15 NULL NULL
50 70 20
① select sum(col2) from tab1 의 결과는 NULL이다.
② select sum(col1 + col2 + col3) from tab1 의 결과는 185 이다.
③ select sum(col2 + col3) from tab1 의 결과는 90 이다.
④ select sum(col2) + sum(col3) from tab1 의 결과는 90 이다.
① 조인을 한 로우씩 차례대로 진행하므로 부분범위 처리에 자주 사용된다.
② 먼저 액세스한 테이블의 처리 범위에 따라 전체 일량이 결정된다.
③ Inner쪽 조인 칼럼에 대한 인덱스 전략이 중요하지만, 조인 칼럼이 모두 포함되지 않은 인덱스라도 사용 가능하다.
④ 선행(Driving) 집합도 반드시 인덱스를 통해 액세스해야 효율적이다.
① 인덱스 구조를 따라 스캔하므로 결과집합의 순서가 보장된다(=일정하다).
② Single Block Read 방식을 사용한다.
③ 파티션 돼 있지 않다면 병렬 스캔이 불가능하다.
④ 필요한 칼럼이 모두 인덱스에 포함돼 있을 때만 사용 가능하다.
SELECT --+ ( ㉠ ) -- ...
FROM TAB1 A, TAB2 B
WHERE A.KEY = B.KEY
① ORDERED USE_NL(B)
② ORDERED USE_NL(TAB2)
③ LEADING(A) USE_NL(B)
④ DRIVING_SITE(A) USE_NL(B)
select *
from t_small a, t_big b
where a.id = b.id
and a.colid = b.colid
and a.number = b.number
and b.name = 'password'
and a.length <= 10
order by a.length DESC
Rows Executes StmtText
----- ----------- ---------------------------------------------------------------
0 1 select *
0 1 |--Sort(ORDER BY:([a].[length] DESC))
0 1 |--Filter(WHERE:([t_small].[length] as [a].[length]<=(10
30 1 |--Nested Loops(Inner Join, OUTER REFERENCES:([Bmk1
30 1 |--Nested Loops(Inner Join, OUTER REFERENCES:(
30 1 | |--Table Scan(OBJECT:([t_big] AS [b]),
30 30 | |--Index Seek(OBJECT:([t_small].[t_small_
30 30 |--RID Lookup(OBJECT:([t_small] AS [a]), SEEK:
(8개 행 적용됨)
① 조인 순서 : t_small → t_big, inner 테이블 조인 횟수 : 30
② 조인 순서 : t_big → t_small, inner 테이블 조인 횟수 : 60
③ 조인 순서 : t_small → t_big, inner 테이블 조인 횟수 : 1
④ 조인 순서 : t_big → t_small, inner 테이블 조인 횟수 : 30
SQL> SELECT ...
2 FROM (SELECT DEII_DT, NEII_VAL, NACT_VAL, NSTD_VAL
3 , NVL(ROUND(AVG(NACT_VAL/NSTD_VAL*100) OVER(), 2), 0.00) AVG_VAL
4 , NVL(ROUND(STDDEV(NACT_VAL/NSTD_VAL*100) OVER(), 2), 0.00) STDDEV_VAL
5 , ROWNUM RN
6 FROM EII_SUMMARY
7 WHERE TO_CHAR(DEII_DT, 'YYYYMMDDHH24MISS') BETWEEN :B3 AND :B4
8 ORDER BY DEII_DT) MR
9 LEFT OUTER JOIN EII_TARGET ET
10 ON ET.DEII_DT BETWEEN TRUNC(MR.DEII_DT, 'MM')
11 AND TRUNC(LAST_DAY(MR.DEII_DT)+1)-1/60/24/24
12 AND ET.NCODE_NO IN ( :B1, :B2 )
13 GROUP BY FLOOR((MR.RN-1)/:B5 ), MR.AVG_VAL, MR.STDDEV_VAL
14 ORDER BY FLOOR((MR.RN-1)/:B5 ) ;
call count cpu elapsed disk query current rows
------- ------ ------- -------- ------ ------- -------- -------
Parse 446 0.00 0.00 0 0 0 0
Execute 7578 0.03 0.12 22 564 2 8
Fetch 13522 128.03 129.16 22 6676902 0 10442
------- ------ ------- -------- ------ ------- -------- -------
total 21546 128.06 129.30 44 6677466 2 10450
Rows Row Source Operation
---- ---------------------------------------------------
2 SORT GROUP BY (cr=7340 pr=7 pw=0 time=221191 us)
240 NESTED LOOPS OUTER (cr=7340 pr=7 pw=0 time=221460 us)
120 VIEW (cr=20 pr=7 pw=0 time=90776 us)
120 FILTER (cr=20 pr=7 pw=0 time=90410 us)
120 WINDOW SORT (cr=20 pr=7 pw=0 time=89447 us)
120 COUNT (cr=20 pr=7 pw=0 time=79490 us)
120 TABLE ACCESS (FULL) OF 'EII_SUMMARY' (TABLE) (cr=20 pr=7 pw=0 time=79250 us)
240 VIEW (cr=7320 pr=0 pw=0 time=74760 us)
240 TABLE ACCESS FULL EII_TARGET (cr=7320 pr=0 pw=0 time=74414 us)
[인덱스 구성]
EII_SUMMARY_X01 : DEII_DT
EII_TARGET_X01 : DEII_DT
① 7번 라인을 아래와 같이 수정한다.
WHERE DEII_DT BETWEEN TO_DATE(:B3, 'YYYYMMDDHH24MISS')
AND TO_DATE(:B4, 'YYYYMMDDHH24MISS')
② 9번 라인의 'LEFT OUTER JOIN'을 'INNER JOIN'으로 변경함으로써
EII_TARGET 테이블이 먼저 드라이빙 될 수 있게 한다.
Row Source를 분석해 보면, Outer 집합에서 Inner 집합으로 조인 시도한
건수만큼 모두 성공하므로 Outer Join은 불필요하다.
③ 10~12번 라인을 위해 EII_TARGET_X01 인덱스를 [NCODE_NO + DEII_DT] 순으로 구성한다.
④ 14번 라인의 ORDER BY는 불필요하므로 제거한다.
가) 3일 이전에 발생한 판매 데이터를 삭제한다.
delete from 일별고객별판매집계 where 판매일시 < trunc(sysdate) - 2;
나) 고객별로 집계한 금일 판매 데이터를 추가한다.
insert into 일별고객별판매집계
select to_char(sysdate, 'yyyymmdd'), 고객번호, sum(판매량), sum(판매금액)
from 판매
where 판매일시 between trunc(sysdate) and trunc(sysdate+1)-1/24/60/60
group by 고객번호;
다) commit;
라) select count(*) from 일별고객별판매집계;
① 가 \-> 나 \-> 다 \-> 라
② 가 \-> 다 \-> 나 \-> 다 \-> 라
③ 나 \-> 가 \-> 다 \-> 라
④ 나 \-> 다 \-> 가 \-> 다 \-> 라
SELECT ...............
FROM TAB1
WHERE COL1 = :VAL1
AND COL2 LIKE :VAL2 || '%'
AND COL3 IN ('1','5')
AND COL4 BETWEEN :DATE1 AND :DATE2
■ 인덱스구성 : COL1 + COL2 + COL3
① COL1 조건이 없었다면 정상적으로 인덱스를 이용할 수 없다.
② COL2 조건은 인덱스 읽는 범위를 줄여주는데 기여하지 못한다.
③ COL3 조건은 인덱스 읽는 범위를 줄여주는데 기여한다.
④ COL4 조건은 테이블에서 필터링된다.
<계약>
주문번호 | 주문일자 | 주문금액 | 부가세 | 주문월 |
---|---|---|---|---|
1 | 2012-01-01 | 500 | 50 | 201001 |
SELECT COUNT(*) CNT, SUM(주문금액) 주문금액
FROM 계약
WHERE 주문월 = '201010'
GROUP BY 주문월
①
CNT | 주문금액 |
---|---|
0 | 0 |
②
CNT | 주문금액 |
---|---|
0 |
③
CNT | 주문금액 |
---|---|
0 |
④
CNT | 주문금액 |
---|---|
(데이터가 존재하지 않음) | (데이터가 존재하지 않음) |
SELECT A.ENAME, A.SAL, B.DNAME
FROM EMP A, DEPT B
WHERE A.DEPTNO = B.DEPTNO
AND B.DEPTNO = 20;
SELECT A.ENAME, A.SAL, B.DNAME
FROM EMP A, DEPT B
WHERE A.DEPTNO = 20
AND B.DEPTNO = 20;
① 두번째 SQL문은 배치쿼리 시 유리하다.
② 두 SQL문의 수행속도는 현저한 차이가 난다.
③ 두 SQL문의 결과는 동일하다.
④ 옵티마이저 모드나 버전에 따라 다른 결과가 나올 수 있다.
① 데이터의 무결성과 일관성을 위해서 사용자 정의 함수를 사용한다.
② 사용자 정의 함수는 절차형 SQL을 로직과 함께 데이터베이스 내에 저장해 놓은 명령문의 집합을 의미하며 다른 SQL문장에 포함되어 실행될 수 있다. 단독적으로 실행되기 보다는 다른 SQL문을 통하여 호출되고 그 결과를 리턴하는 SQL의 보조적인 역할을 한다.
③ 트리거는 특정한 테이블에 INSERT, UPDATE, DELETE와 같은 DML문이 수행되었을 때 데이터베이스에서 자동으로 동작하도록 작성된 프로그램이다.
④ 스토어드 프로시져는 절차형 SQL을 로직과 함께 데이터베이스 내에 저장해 놓은 명령문의 집합을 의미한다.
① 기본 인덱스(Primary Key Index)에 중복된 키 값들이 나타날 수 있다.
② 기본 인덱스에 널 값(Null Value)들이 나타날 수 없다.
③ 보조 인덱스(Secondary Index)에는 고유한 키 값들만 나타날 수 있다.
④ 자주 변경되는 속성은 인덱스를 정의할 좋은 후보이다.
① START WITH절은 계층 구조의 시작점을 지정하는 구문이다.
② 루트 노드의 LEVEL 값은 0이다.
③ 순방향전개란 부모 노드로부터 자식 노드 방향으로 전개하는 것을 말한다.
④ ORDER SIBLINGS BY절은 형제 노드 사이에서 정렬을 지정하는 구문이다.
① 메인 쿼리는 조직 테이블(1)이고, 서브쿼리는 사원 테이블(M)을 사용할 경우, 질의 결과는 M레벨인 사원 레벨로 나온다.
② 메인쿼리에서 서브쿼리 칼럼을 사용할 수 없다.
③ 서브쿼리에서 메인쿼리 칼럼은 사용할 수 있다.
④ 서브쿼리의 연결 연산자로서 '='을 사용할 경우, 서브쿼리 파트의 결과는 1건 이하여야 한다.
① 데이터를 읽고 쓰는 단위는 블록(=페이지)이다.
② 데이터파일에 공간을 할당하는 단위는 익스텐트다.
③ 같은 세그먼트(테이블, 인덱스)에 속한 익스텐트끼리는 데이터파일 내에서 서로 인접해 있다.
④ SQL Server에서는 한 익스텐트에 속한 페이지들을 여러 오브젝트가 나누어 사용할 수 있다.
① 공통화된 슈퍼타입에 대해 데이터가 너무 많거나 칼럼이 너무 많은 경우 수직분할과 수평분할을 적용하여 성능을 향상 시키도록 한다.
② 슈퍼타입, 서브타입 각각에 대해 테이블이 독립적으로 구분되어 1:1관계의 테이블이 유지될 수 있도록 한다.
③ 슈퍼타입과 서브타입을 결합하여 한 개의 테이블로 생성하여 서브타입의 개수만큼 테이블을 생성한다.
④ 전체 테이블을 하나의 테이블에 속성을 모두 포함하도록 한다. 다만 서브타입일 경우 속성마다 Not Null을 지정할 수 있지만 통합되면 개별적인 특징을 반영하지 못할 수 있다.
① 내부적으로 Unique 작업을 실행한다.
② 하나의 로우에 해당하는 결과 건수는 1건 이하여야 한다.
③ Min 또는 Max 함수를 사용해야 한다.
④ 결과 칼럼의 개수는 1개여야 한다.
① 모르는 값을 의미한다.
② 값의 부재를 의미한다.
③ 공백문자(Empty String) 혹은 숫자 0을 의미한다.
④ NULL과의 모든 비교는 알 수 없음(Unknown)을 반환한다.
① Log Buffer = Log Cache
② Online Redo Log = Transaction Log
③ Database Writer = LazyWriter
④ Program Global Area = Procedure Cache
① 바인드 변수를 사용하면 옵티마이저가 칼럼 히스토그램뿐만 아니라 각종 통계정보를 이용하지 못하게 된다.
② 파티션 테이블을 쿼리할 때 파티션 키 칼럼을 바인드 변수로 조회하면 옵티마이저가 파티션 레벨 통계를 활용하지 못한다.
③ 바인드 변수를 사용하면 옵티마이저는 평균 분포를 가정한 실행계획을 수립한다.
④ 바인드 변수 Peeking(또는 Parameter Sniffing) 기능이 도입되면서 칼럼 히스토그램을 사용할 수 있게 되었고, 이 때문에 바인드 변수 사용에 따른 부작용은 거의 해소되었다.
① 두 SQL 결과가 같다면, 수행속도도 별 차이가 없다.
② 옵티마이저에 의해 내부적인 쿼리 변환이 이루어지더라도 결과집합에는 영향을 주지 않는다.
③ 사용자가 제시하는 SQL은 결과에 대한 요구사항일 뿐이며 처리절차는 옵티마이저가 결정한다.
④ 사용자가 부여한 조건은 실행계획에 영향을 주지 않는다.
t1_x01 : c1 + c2 + c3
① select * from t1 where c1 = ? order by c2 desc, c3 ;
② select * from (select * from t1 order by c1, c2, c3) where rownum <= 1 ;
select * from t1 order by c1, c2, c3 ;
③ select * from t1 where c1 = ? order by c2 ;
④ select * from (select * from t1 where c1 = ? and c2 >= ? order by c3) where rownum <= 10 ;
select * from t1 where c1 = ? and c2 >= ? order by c3 ;
[Oracle 기준]
create table t1 ( c1, c2, c3, c4, c5, constraint t1_pk primary key (c1, c2) )
organization index;
create index t1_x02 on t1 ( c3, c4 );
[SQL Server 기준]
create unique clustered index t1_pk on t1 ( c1, c2 )
go
create index t1_x02 on t1 ( c3, c4 )
GO
① select c5 from t1 where c3 = ?
② select count(*) from t1 where c4 >= ?
③ select max(c2) from t1 where c3 between ? and ?
④ select count(*) from t1 where c3 = ? and c4 is null
- 단, 인덱스를 사용했을 때의 테이블 액세스량은 손익분기점 미만이라고 가정
- 데이터 타입
* 주문상태코드 char(1) not null
* 배송상태코드 char(3) not null
* 주문수량 number(5) NULL
① 주문_X01 인덱스 : 주문상태코드 + 배송상태코드
[ 튜닝 전 ] select * from 주문 where 주문상태코드 || 배송상태코드 = 'A038'
[ 튜닝 후 ] select * from 주문 where 주문상태코드 = 'A' and 배송상태코드 = '038'
② 주문_X03 인덱스 : 주문수량
[ 튜닝 전 ] select * from 주문 where nvl(주문수량, 0) >= 100
[ 튜닝 후 ] select * from 주문 where 주문수량 >= 100
③ 주문_X03 인덱스 : 주문수량
[ 튜닝 전 ] select * from 주문 where 주문수량 * 1.1 > 2200
[ 튜닝 후 ] select * from 주문 where 주문수량 > 2200/1.1
④ 주문_X02 인덱스: 주문일자 + 배송상태코드 + 주문상태코드
[ 튜닝 전 ]
select * from 주문
where 주문일자 between '20100901' and '20100930'
and 주문상태코드 || 배송상태코드 in ('A038', 'B045')
[ 튜닝 후 ]
select * from 주문
where 주문일자 between '20100901' and '20100930'
and 주문상태코드 = 'A'
and 배송상태코드 = '038'
union all
select * from 주문
where 주문일자 between '20100901' and '20100930'
and 주문상태코드 = 'B'
and 배송상태코드 = '045'
① 인덱스를 제거했다가 나중에 다시 생성한다.
② 수정 가능 조인 뷰(Updatable Join View)나 Merge문을 활용한다.
③ Oracle이라면 update문을 수행하기 전에 테이블을 nologging 모드로 변경한다.
④ SQL Server라면 최소 로깅(minimal logging) 모드 Insert 기능을 활용한다.
① 실행계획은 SQL 처리를 위한 실행 절차와 방법을 표현한 것이다.
② 실행계획은 조인 방법, 조인 순서, 액세스 기법 등이 표현된다.
③ 동일 SQL문에 대해 실행계획이 다르면 실행 결과도 달라질 수 있다.
④ CBO(Cost Based Optimizer)의 실행계획에는 단계별 예상 비용 및 건수 등이 표시된다.
기본 테이블 T의 컬럼(P,Q,R) 조합으로 X라 불리는 인덱스를 생성한다.
각각은 P(오름차순), Q(내림차순), R(오름차순)으로 정렬한다.
① CREATE INDEX X ON T(P,Q DESC,R);
② CREATE INDEX X ON T(P,R,Q DESC);
③ CREATE INDEX T ON X(P,Q DESC,R);
④ CREATE INDEX T ON X(P,R,Q DESC);
① SELECT * FROM MYTABLE WHERE COLUMN1 IS NOT NULL
② SELECT * FROM MYTABLE WHERE COLUMN1 <> NULL
③ SELECT * FROM MYTABLE WHERE COLUMN1 != NULL
④ SELECT * FROM MYTABLE WHERE COLUMN1 NOT NULL
① Driving Table의 검색범위가 성능에 가장 큰 영향을 주는 조인 방식이다.
② 내부적으로 Function을 사용한다.
③ 조인 조건의 인덱스 유무에 영향을 받지 않지만 Sort가 필요하다.
④ 크기 차이가 나는 두 집합의 조인 시에 유리하며, Sort가 일어나지 않는다.
문제 | 정답 | 문제 | 정답 | 문제 | 정답 |
---|---|---|---|---|---|
1 | ③ | 21 | ①, ③ | 41 | ④ |
2 | ④ | 22 | ④ | 42 | ③ |
3 | ③ | 23 | ③ | 43 | ③ |
4 | ②, ④ | 24 | ② | 44 | ① |
5 | ④ | 25 | ②, ③ | 45 | ① |
6 | ①, ④ | 26 | ④ | 46 | ②, ④ |
7 | ③, ④ | 27 | ③ | ||
8 | ② | 28 | ① | ||
9 | ③ | 29 | ② | ||
10 | ① | 30 | ② | ||
11 | RANK | 31 | ① | ||
12 | ② | 32 | ③ | ||
13 | ④ | 33 | ④ | ||
14 | ② | 34 | ②, ④ | ||
15 | POSITION IS NULL | 35 | ③ | ||
16 | ② | 36 | ④ | ||
17 | 80 | 37 | ①, ④ | ||
18 | ③ | 38 | ①, ④ | ||
19 | ④ | 39 | ②, ③ | ||
20 | ④ | 40 | ① |
문제 | 정답 | 해설 |
---|---|---|
01 | ③ | GROUP BY 절 이후에 조건을 적용하는 경우는 HAVING 절이 사용된 경우이다. 그룹핑된 값에 조건을 적용하는 경우 HAVING 절을 사용한다. JOIN 절에 ALIAS를 사용한 경우, 2개 이상의 테이블에 공통적으로 사용되고 SELECT 절에 사용되는 칼럼에는 ALIAS 접두사를 붙여야 한다. |
02 | ④ | FULL OUTER JOIN은 DEFT와 EMP 사이에서 조인이 성공한 건과 DEPT, EMP 건 중 조인이 실패한 건을 함께 표시한다. |
03 | ③ | '가족들이 없는' 조건 : NOT EXISTS '현재 부양하는 가족들' 조건 : 사번 = 부양사번 |
04 | ②, ④ | 2번은 LIKE 검색 문자열 앞뒤에 모두 '%' 기호를 붙였으므로 정상적인 Index Range Scan이 불가능하다. 4번은 내부적 형변환이 발생하므로 Index Range Scan이 불가능하다. |
v5 | ④ | 1번과 3번은 기존에 Full Table Scan으로 처리됐을 것이므로 비트맵 인덱스를 생성하고 나면 블록 I/O가 크게 감소한다. 3번의 경우 만약 '색상is not null' 조건을 추가하면 인덱스만 읽고 처리할 수 있지만 그렇더라도 B*Tree 인덱스는 비트맵 인덱스에 비해 블록 I/O는 더 많이 발생한다. 2번은 Bitmap Conversion이 발생하지 않는 한, 기존에 두 B*Tree 인덱스 중 어느 하나만 사용되고, 나머지 필터 조건을 처리하기 위해 테이블 랜덤 액세스가 불가피하므로 성능이 매우 안 좋았을 것이다. 반면, 비트맵 인덱스를 생성하고 나면 두 개의 비트맵 인덱스를 동시에 사용할 수 있고, 테이블을 랜덤 액세스도 생략되므로 성능 개선 효과가 클 것이다. 4번은 색상 = 'BLUE'에 해당하는 건수만큼 대량의 테이블 액세스가 불가피하다. 비트맵 인덱스를 생성하고 나면 인덱스 스캔 단계에서 다소 블록 I/O가 감소하겠지만 테이블 랜덤 I/O는 줄지 않으므로 성능 개선 효과가 미미하게 나타난다. |
06 | ①, ④ | ① GROUP BY 절 없이 HAVING을 사용할 수 없다. ④ USING 조건절을 이용한 EQUI JOIN에서도 NATURAL JOIN과 마찬가지로 JOIN 칼럼에 대해서는 ALIAS나 테이블 이름과 같은 접두사를 붙일 수 없다. (부서.부서번호 → 부서번호) |
07 | ③, ④ | 조건절에 해당하는 결과집합이 없다고 에러가 발생하지는 않는다. 그리고 공집합에 NVL 함수를 사용한다고 값이 얻을 수 있는 것은 아니다. |
08 | ② | 뷰(View) 안에 rownum을 사용하면 뷰 머징(View Merging)을 방지하는 효과가 나타난다. |
09 | ③ | 윈도우 함수를 이용한 누적 합계(RUNNING SUMMARY)를 구하는 SQL이다. |
10 | ① | GROUP BY절이 없기 때문에 결과건수는 항상 1건이다. 해당 SQL문에서는 조인 조건의 부재로 결과 건수가 아니라 답 자체가 틀려질 수 있다. 즉, Cartesian Product으로 인해 합계가 틀려질 수 있다. |
11 | RANK | RANK 함수는 ORDER BY를 포함한 QUERY 문에서 특정 항목(칼럼)에 대한 순위를 구하는 함수이다. 이때 특정 범위(PARTITION) 내에서 순위를 구할 수도 있고 전체 데이터에 대한 순위를 구할 수도 있다. 또한 동일한 값에 대해서는 동일한 순위를 부여하게 된다. |
12 | ② | (직급의 Selectivity) × (부서의 Selectivity) × (전체 로우 수) = 1/4 × 1/5 × 1,000 = 50 |
13 | ④ | Local 인덱스이므로 t_idx의 파티션 키는 테이블과 똑같이 a 칼럼이다. 그리고 파티션 키가 인덱스 선두 칼럼이 아니므로 NonPrefixed에 해당한다. |
14 | ② | 1, 4번은 5번째와 6번째 레코드가 rnum으로 똑같이 5를 부여받기 때문에 HireDate 순으로 6개 레코드가 선택된다. 참고로, 7번째 레코드는 각각 7과 6을 부여받는다 3번은 서브쿼리에서 5개 레코드가 선택되지만, 메인쿼리와 조인하고 나면 최종적으로 6개 레코드가 선택된다. 2번은 HireDate 순으로 5개 레코드가 선택된다 |
15 | POSITION IS NULL | 아래 NVL 함수와 ISNULL 함수를 사용한 SQL 문장은 벤더 공통적으로 CASE 문장으로 표현할 수 있다. 본 문제는 CASE 표현 중 SEARCHED_CASE_E-PRESSION에 들어갈 조건을 문의한 것임 CASE WHEN CONDITION THEN RETURN_EXPR ELSE 표현절 END |
16 | ② | 두 SQL의 결과는 동일하며, OLTP 환경이냐 DW 환경이냐를 불문하고 두 번째 SQL이 더 효율적이다. |
17 | 80 | Buffer Cache Hit Ratio = 100 × (1 - disk / (query+current)) |
18 | ③ | 칼럼끼리 연산할 때 null을 포함하면 결과는 null이다. 레코드끼리 연산할 때 null을 포함하면 결과가 null이 아니며, 이유는 null을 연산에서 제외하기 때문이다. |
19 | ④ | 선행(Driving) 집합은 주어진 조건절에 따라 Full Table Scan이 유리할 수도 있다. |
20 | ④ | 필요한 칼럼이 모두 인덱스에 포함돼 있을 때만 사용 가능한 것은 Index Fast Full Scan의 특징이다. |
21 | ①, ③ | 테이블 Alias가 있은 상황에선 반드시 Alias를 사용해야 한다. |
22 | ④ | 실행계획 상 위 쪽에서 아래 쪽으로 조인이 진행된다. NL 조인의 경우 위쪽에 있는 Outer 집합에서 출력된 결과 건수(Rows)만큼 Inner 집합으로 조인 시도가 일어난다. |
23 | ③ | 7번 라인에 대한 Row Source를 보면, 20개 블록을 읽어서 120개 로우를 반환하므로 굳이 인덱스를 사용하도록 튜닝하지 않아도 된다. 9번 라인에 대한 Row Source만 보고 Left Outer Join이 불필요하다고 판단할 수 없다. 10~11번 라인 조인 칼럼에 인덱스가 있는데도 옵티마이저가 이를 사용하지 않고 Full Table Scan으로 처리한 이유는, NCODE_NO 필터링을 위해 다량의 테이블 랜덤 액세스가 발생하기 때문이다. 인덱스 뒤에 NCODE_NO만 추가해도 성능이 많이 개선되겠지만, 순서까지 바꿔 NCODE_NO + DEII_DT 순으로 구성하는 것이 최적이다. 14번 라인의 ORDER BY를 제거하면 결과집합의 출력순서가 달라질 수 있다. |
24 | ② | '가'를 수행하고'다'를 수행하기 전에 commit을 수행하면, '가'에서 삭제된 빈 공간을 '다'에서 재사용하므로 Index Skew 현상을 방지할 수 있다. |
25 | ②, ③ | Like, Between, 부등호 같은 범위검색조건이더라도 선행 칼럼이 누락없이 모두 '=' 조건으로 제공되면 인덱스 스캔 범위를 줄이는데 기여한다. |
26 | ④ | 주어진 조건을 만족하는 건이 없다. 만약 SQL에서 GROUP BY절을 사용하지 않았다면 4번이 정답이다. |
27 | ③ | b.deptno = 20이고 a.deptno = b.deptno이므로 a.deptno = 20이다. 따라서 위 두 SQL의 처리 결과는 동일하다. |
28 | ① | Stored Module(ex: PL/SQL, LP/SQL, T-SQL)로 구현 가능한 기능은 ②,③,④ 세가지이며, ① 데이터의 무결성과 일관성을 위해서 사용자 정의 함수를 사용하는 것은 트리거의 용도이다. |
29 | ② | 기본 인덱스 = 기본키 인덱스(PK)는 UNIQUE & NOT NULL의 제약조건을 가진다. 보조 인덱스는 NON UNIQUE 성격을 가질 수 있다. 자주 변경되는 속성은 인덱스로 적절하지 않다. |
30 | ② | Oracle 계층형 질의에서 루트 노드의 LEVEL 값은 1이다. |
31 | ① | 서브쿼리를 사용한 경우, 질의 결과는 항상 메인쿼리 레벨과 동일하다. |
32 | ③ | 익스텐트 내 블록들은 서로 인접하지만, 익스텐트끼리 서로 인접하지는 않는다. |
33 | ④ | 슈퍼타입과 서브타입을 변환하는 방식에서는 수직분할과 수평분할 방식이 존재하지 않는다. |
34 | ②, ④ | 스칼라 서브쿼리의 결과는 1칼럼의 1건이다. |
35 | ③ | NULL은 공백문자(Empty String) 혹은 숫자 0과 동일하지 않다. |
36 | ④ | Oracle에서 Program Global Area는 프로세스에 종속적인 고유 데이터를 저장해 두는 메모리 공간이며, 다른 말로 Private Global Area, Process Global Area라고도 한다. SQL Server의 Procedure Cache는 SQL과 실행계획, 저장형 함수/프로시저를 캐싱해 두는 메모리 공간이다. |
37 | ①, ④ | 바인드 변수를 사용하더라도 칼럼 히스토그램을 제외한 나머지 통계정보는 활용할 수 있다. 그리고 바인드 변수 Peeking(또는 Parameter Sniffing) 기능은 오히려 부작용이 많아 사용을 꺼리는 추세고, 따라서 바인드 변수 사용에 따른 부작용을 해소하려고 각 DBMS가 아직 노력 중이다. |
38 | ①, ④ | 두 SQL의 결과가 같더라도 SQL 형태에 따라 수행속도가 크게 다를 수 있다. 실행계획에 가장 큰 영향을 미치는 것은 조건절이다. |
39 | ②, ③ | 인덱스는 기본적으로 오름차순(Ascending)으로 정렬된다. 따라서 order by 칼럼 중 일부만 내림차순일 때는 인덱스로 정렬을 대체할 수 없다. 따라서 ①은 정답이 될 수 없다. 인덱스 뒤쪽 칼럼이 order by 절에 사용된 경우, 그 앞쪽 인덱스 칼럼이 모두 '=' 조건이어야 정렬을 대체할 수 있다. 따라서 ④번도 정답이 될 수 없다. |
40 | ① | Oracle 기준 : 인덱스 키와 PK 이외의 칼럼을 참조하면 테이블 액세스가 발생한다. SQL Server 기준 : Non-Clustered Index 키와 Clustered Index 키 이외의 칼럼을 참조하면 테이블 액세스가 발생한다. |
41 | ④ | 인덱스 선두 컬럼이 Like, Between, 부등호 같은 범위검색 조건일 때 SQL을 Union All로 분기하면, 인덱스에서 같은 범위를 2번 스캔하기 때문에 비효율이 2배로 증가한다. |
42 | ③ | {code:SQL} nologging 옵션은 insert 문장에만 효과가 있으며, update나 delete문에 대해 로깅을 남기지 않는 방법은 제공되지 않는다. 오라클에서의 nologging 기능의 올바른 사용방법은 다음과 같다. |
alter table t nologging;
insert --+ append – into t select ... from ...;
또는
insert --+ parallel(t 2) -- into t select ... from ...;
아래와 같이 CTAS 문장으로 테이블을 생성하면서 nologging 옵션을 주어도 같은 효과가 나타난다.
create table t
nologging
as
select ... from ... ;
그 외, SQL*Loader에서 direct=TRUE 옵션을 사용하는 방법도 있다.
참고로, 아래와 같이 힌트를 사용하는 것은 올바른 사용방법이 아니다. (nologging 힌트는 존재하지 않는다.)
insert --+ nologging – into t
select ... from ... ;
아래와 같이 테이블 옆에 nologging을 지정하는 것도 무용지물이며, 이 경우 nologging은 테이블 Alias 역할을 할 뿐이다.
insert into t nologging
select ... from ... ;
DML 성능을 높이기 위해 생각해 볼 수 있는 또 다른 방안은, Target 테이블의 인덱스를 제거했다가 나중에 다시 생성하는 것이다.
왜냐하면, DML 성능이 저하되는 가장 큰 원인 중 하나가 인덱스 관리에 있기 때문이다.
예를 들어, 인덱스가 3개 달린 테이블에 100만 건을 Insert 하려면 테이블 외에 인덱스에도 총 300만 개 레코드를 Insert 해야 한다.
인덱스는 그 특성상 정렬상태를 유지해야 하기 때문에 Freelist에 의해 관리되는 테이블에 Insert 하는 것보다 훨씬 부하가 크다.
따라서 Insert나 Delete의 경우, 전체 인덱스를 Drop(또는 Unusable 상태로 변경)했다가 작업 완료 후 다시 생성하는 것을 고려해 볼 수 있다.
Update의 경우는, Update되는 컬럼을 포함하는 인덱스를 제거했다가 나중에 다시 생성하는 것을 고려해 볼 수 있다.
단, 인덱스를 재생성하는 비용도 만만치 않으므로 전체 테이블 건수 중 Insert/Update/Delete 되는 건수의 비중을 따져본 후 결정해야 한다.
|
| 43 | ③ | 실행계획 즉, 실행방법이 달라진다고 해서 결과가 달라지지는 않는다.
|
| 44 | ① | 인덱스 생성 SQL 문장은 다음 포맷으로 만들어진다.
CREATE INDEX 인덱스명 ON 테이블명 (인덱스로 사용되는 칼럼 LIST);
|
| 45 | ① | NULL 값을 조건절에서 사용하는 경우 IS NULL, IS NOT NULL이란 키워드를 사용해야 한다.
|
| 46 | ②, ④ | 1. Driving Table의 데이터 양이 가장 큰 영향을 주는 조인 방식이다.(NLJ)
NLJ는 조인 테이블의 순서에 따라 일의 양이 변하므로 가장 드라이빙 테이블의 영향을 많이 받는다.
HJ도 데이터 대상이 적은 테이블을 드라이빙 테이블로 사용하는 것이 유리하다.
2. 조인 Key에 인덱스가 아닌 Function을 적용한다.(HJ)
HJ는 조인 Key를 만들기 위해 Hash 함수를 사용하다.
3. 조인 조건의 인덱스의 유무에 영향 받지 않지만 Sort가 필요하다.(SMJ)
SMJ는 조인 조건을 확인하기 위해 데이터 정렬이 꼭 필요하다.
4. 크기 차이가 나는 두 집합의 데이터를 처리하는데 장점이 있고, Sort가 일어나지 않는다.(HJ)
크기가 차이 나는 두 집합의 데이터를 처리하는데 적합하고,
SORT를 할 필요가 없기 때문에 일반적으로 SMJ보다 효과적이다. (HW 자원 사용 많음)
|
* 최초작성자 : [~xsoft]
* 최초작성일 : 2013년 09월 07일
* 이 문서는 [오라클클럽|http://www.gurubee.net] [오라클 성능 트러블슈팅 스터디|2013년 하반기 - 오라클 트러블슈팅 스터디] 모임에서 작성하였습니다.
* {color:blue}{*}이 문서의 내용은 DBGUIDE의 [퀴즈게시판|http://www.dbguide.net/da.db?cmd=snb13_list&movePage=10&boardGroupUid=6&boardConfigUid=81&categoryUid=&search=&boardStep=&boardSummary=&]을 참고하였습니다.*{color}