< C, JAVA >
for(i=0; i<100; i++){ -- outer loop
for(j=0; j<100; j++){ -- inner loop
// Do Anything ...
}
}
begin
for outer in (select deptno, empno, rpad(ename, 10) ename from emp)
loop -- outer 루프
for inner in (select dname from dept where deptno = outer.deptno)
loop -- inner 루프
dbms_output.put_line(outer.empno||' : '||outer.ename||' : '||inner.dname);
end loop;
end loop;
end;
-- 아래 쿼리와 100% 같은 순서로 데이터를 액세스 하고 출력 순서도 같음
[예제] Oracle
select /*+ ordered use_nl(d) */ e.empno, e.ename, d.dname
from emp e, dept d
where d.deptno = e.deptno
select /*+ leading(e) use_nl(d) */ e.empno, e.ename, d.dname
from dept d, emp e
where d.deptno = e.deptno
[예제] SQL Server
select e.empno, e.ename, d.dname
from emp e inner loop join dept d on d.deptno = e.deptno
option (force order)
select e.empno, e.ename, d.dname
from emp e, dept d where d.deptno = e.deptno
option (force order, loop join)
select /*+ ordered use_nl(e) */ e.empno, e.ename, d.dname, e.job, e.sal
from dept d, emp e
where e.deptno = d.deptno ............... ①
and d.loc = 'SEOUL' ............... ②
and d.gb = '2' ............... ③
and e.sal >= 1500 ............... ④
order by sal desc
* pk_dept : dept.deptno
* dept_loc_idx : dept.loc
* pk_emp : emp.empno
* emp_deptno_idx : emp.deptno
* emp_sal_idx : emp.sal
Execution Plan
---------------------------------------------------
0 SELECT STATEMENT
1 0 SORT ORDER BY
2 1 NESTED LOOPS
3 2 TABLE ACCESS BY INDEX ROWID DEPT
4 3 INDEX RANGE SCAN DEPT_LOC_IDX
5 2 TABLE ACCESS BY INDEX ROWID EMP
6 5 INDEX RANGE SCAN EMP_DEPTNO_IDX
StmtText
-------------------------------------------------------------
|--Sort(ORDER BY:([e].[sal] DESC))
|--Filter(WHERE:([emp].[sal] as [e].[sal]>=(1500)))
|--Nested Loops(Inner Join, OUTER REFERENCES:([Bmk1003]))
|--Nested Loops(Inner Join, OUTER REFERENCES:([d].[deptno]))
| |--Filter(WHERE:([dept].[gb] as [d].[gb]='2'))
| | |--Nested Loops(Inner Join, OUTER REFERENCES:([Bmk1000]))
| | |--Index Seek(OBJECT:([dept].[dept_loc_idx] AS [d]), SEEK:([loc]='CHICAGO') )
| | |--RID Lookup(OBJECT:([dept] AS [d]), SEEK:([Bmk1000]=[Bmk1000]) )
| |--Index Seek(OBJECT:([emp].[emp_deptno_idx]), SEEK:([e].[deptno]=[dept].[deptno]))
|--RID Lookup(OBJECT:([emp] AS [e]), SEEK:([Bmk1003]=[Bmk1003]) LOOKUP ORDERED FORWARD)
[예제] Oracle
select /*+ ordered use_merge(e) */ d.deptno, d.dname, e.empno, e.ename
from dept d, emp e
where d.deptno = e.deptno
Execution Plan
-------------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=11 Card=654 Bytes=35K)
1 0 MERGE JOIN (Cost=11 Card=654 Bytes=35K)
2 1 SORT (JOIN) (Cost=6 Card=654 Bytes=14K)
3 2 TABLE ACCESS (FULL) OF 'DEPT' (Cost=2 Card=654 Bytes=14K)
4 1 SORT (JOIN) (Cost=5 Card=327 Bytes=11K)
5 4 TABLE ACCESS (FULL) OF 'EMP' (Cost=2 Card=327 Bytes=11K)
[예제] SQL Server
select d.deptno, d.dname, e.empno, e.ename
from dept d, emp e
where d.deptno = e.deptno
option (force order, merge join)
StmtText -------------------------------------------------------------
|--Merge Join(Inner Join, MANY-TO-MANY MERGE:([d].[deptno])=([e].[deptno]))
|--Sort(ORDER BY:([d].[deptno] ASC)) |
|--Table Scan(OBJECT:([SQLPRO].[dbo].[dept] AS [d]))
|--Sort(ORDER BY:([e].[deptno] ASC))
|--Table Scan(OBJECT:([SQLPRO].[dbo].[emp] AS [e]))
Outer 집합(정렬된 dept)에서 첫 번째 로우 o를 가져온다.
Inner 집합(정렬된 emp)에서 첫 번째 로우 i를 가져온다.
loop
양쪽 집합 중 어느 것이든 끝에 도달하면 loop를 빠져나간다.
if o = i 이면
조인에 성공한 로우를 리턴한다.
inner 집합에서 다음 로우 i를 가져온다.
else if o < i 이면
outer 집합에서 다음 로우 o를 가져온다.
else (즉, o > i 이면)
inner 집합에서 다음 로우 i를 가져온다.
end if
end loop
[예제] Oracle
select /*+ ordered use_hash(e) */ d.deptno, d.dname, e.empno, e.ename
from dept d, emp e
where d.deptno = e.deptno
Execution Plan
-------------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=5 Card=654 Bytes=35K)
1 0 HASH JOIN (Cost=5 Card=654 Bytes=35K)
2 1 TABLE ACCESS (FULL) OF 'DEPT' (Cost=2 Card=654 Bytes=14K)
3 1 TABLE ACCESS (FULL) OF 'EMP' (Cost=2 Card=327 Bytes=11K)
[예제] SQL Server
select d.deptno, d.dname, e.empno, e.ename
from dept d, emp e
where d.deptno = e.deptno
option (force order, hash join)
StmtText
-------------------------------------------------------------
|--Hash Match(Inner Join, HASH:([d].[deptno])=([e].[deptno]))
|--Table Scan(OBJECT:([SQLPRO].[dbo].[dept] AS [d]))
|--Table Scan(OBJECT:([SQLPRO].[dbo].[emp] AS [e]))
해시 함수
Hash Join
1. 수행 빈도가 낮고
2. 쿼리 수행
3. 대용량 테이블을 조인할 때(배치, DW, OLAP)
select empno, ename, sal, hiredate
, (select d.dname from dept d where d.deptno = e.deptno) dname
from emp e where sal >= 2000
select /*+ ordered use_nl(d) */ e.empno, e.ename, e.sal, e.hiredate, d.dname
from emp e right outer join dept d on d.deptno = e.deptno
where e.sal >= 2000
-- 출력 값 : d.dname
-- 입력 값 : e.empno
select empno, ename, sal, hiredate
, (select d.dname from dept d where d.deptno = e.deptno) dname
from emp e where sal >= 2000
select d.deptno, d.dname, avg_sal, min_sal, max_sal
from dept d right outer join (select deptno, avg(sal) avg_sal, min(sal) min_sal, max(sal) max_sal from emp group by deptno) e
on e.deptno = d.deptno where d.loc = 'CHICAGO'
select d.deptno, d.dname
, (select avg(sal), min(sal), max(sal) from emp where deptno = d.deptno)
from dept d where d.loc = 'CHICAGO'
select d.deptno, d.dname
, (select avg(sal) from emp where deptno = d.deptno) avg_sal
, (select min(sal) from emp where deptno = d.deptno) min_sal
, (select max(sal) from emp where deptno = d.deptno) max_sal
from dept d where d.loc = 'CHICAGO'
[예제] Oracle
select deptno, dname
, to_number(substr(sal, 1, 7)) avg_sal
, to_number(substr(sal, 8, 7)) min_sal
, to_number(substr(sal, 15)) max_sal
from ( select d.deptno, d.dname ,(select lpad(avg(sal), 7) || lpad(min(sal), 7) || max(sal)
from emp where deptno = d.deptno) sal from dept d where d.loc = 'CHICAGO' )
[예제] SQL Server
select deptno, dname
, cast(substring(sal, 1, 7) as float) avg_sal
, cast(substring(sal, 8, 7) as int) min_sal
, cast(substring(sal, 15, 7) as int) max_sal
from ( select d.deptno, d.dname
, (select str(avg(sal), 7, 2) + str(min(sal), 7) + str(max(sal), 7) from emp where deptno = d.deptno) sal
from dept d where d.loc = 'CHICAGO' ) x