- table: db에 데이터를 저장하는 행과 열로 이루어진 기본 저장소
- view: 테이블을 바라보는 db 오브젝트 (데이터 저장 x)
- index: 쿼리의 검색속도를 향상시키는 db 오브젝트
- sequence: 번호를 생성하는 db 오브젝트
- synonym: 동의어
select rowid, empno, ename, sal
from emp;
rowid: 해당 row의 물리적 주소
file번호 + block + row(행) 번호
create index emp_sal
on emp(sal);
index는 테이블과는 별개
💡
create index 인덱스_이름 on 테이블명(컬럼명);
drop index emp_sal;
select ename, sal
from emp
where sal = 1600;
SELECT * FROM table(dbms_xplan.display_cursor(null, null, 'ALLSTATS LAST'));
💡 월급이 1600을 찾기 위해서 EMP 테이블의 SAL을 처음부터 끝까지 다 스캔한게 FULL TABLE SCAN
select ename, sal
from emp
where sal = 1600;
create index emp_sal
on emp(sal);
select ename, sal
from emp
where sal = 1600;
SELECT * FROM table(dbms_xplan.display_cursor(null, null, 'ALLSTATS LAST'));
ㄴ index 사용 후 버퍼 갯수 줄어들음
💡 인덱스의 구조는 컬럼값과 ROWID로 되어있음. 컬럼값이 ASCENDING 하게 정렬되어 있으므로 바로 데이터를 찾을 수 있음 --> FULL TABLE SCAN 보다 속도가 빠름
select sal, rowid
from emp
where sal >= 0;
SELECT * FROM table(dbms_xplan.display_cursor(null, null, 'ALLSTATS LAST'));
select ename, sal, job
from emp
where ename='SMITH';
create index emp_ename
on emp(ename);
select ename, sal, job
from emp
where ename='SMITH';
SELECT * FROM table(dbms_xplan.display_cursor(null, null, 'ALLSTATS LAST'));
ㄴ 버퍼 갯수 줄어들음
select ename, rowid
from emp
where ename > ' ';
SELECT ename, sal
from emp
where ename='SCOTT';
SELECT * FROM table(dbms_xplan.display_cursor(null, null, 'ALLSTATS LAST'));
select ename, hiredate
from emp
where hiredate = to_date('81/12/11', 'RR/MM/DD');
create index emp_hiredate
on emp(hiredate);
select hiredate, rowid
from emp
where hiredate < to_date('9999/12/31', 'RRRR/MM/DD');
select ename, sal
from emp
where sal * 12 = 36000;
💡 sal*12 라고 하면 sal 컬럼을 가공하면 인덱스를 엑세스 하지 못하고 full table scan 하게 됨
select ename, sal
from emp
where sal = 36000/12;
SELECT * FROM table(dbms_xplan.display_cursor(null, null, 'ALLSTATS LAST'));
select ename, job
from emp
where job='SALESMAN';
SELECT * FROM table(dbms_xplan.display_cursor(null, null, 'ALLSTATS LAST'));
create index emp_job
on emp(job);
select ename, job
from emp
where job='SALESMAN';
SELECT * FROM table(dbms_xplan.display_cursor(null, null, 'ALLSTATS LAST'));
select ename, job
from emp
where substr(job, 1, 5)='SALES';
select ename, job
from emp
where job like 'SALES%';
SELECT * FROM table(dbms_xplan.display_cursor(null, null, 'ALLSTATS LAST'));
💡 와일드카드(%) 가 뒤쪽에 있어야 인덱스를 탈 수 있음
select ename, hiredate
from emp
where to_char(hiredate, 'RRRR') = '1981';
select ename, hiredate
from emp
where hiredate between to_date('1981/01/01','RRRR/MM/DD')
and to_date('1981/12/31:23:59:59','RRRR/MM/DD:HH24:MI:SS');
SELECT * FROM table(dbms_xplan.display_cursor(null, null, 'ALLSTATS LAST'));
select ename, job, sal
from emp
where job || sal = 'SALESMAN1250';
select ename, job, sal
from emp
where job ='SALESMAN' and sal = 1250;
SELECT * FROM table(dbms_xplan.display_cursor(null, null, 'ALLSTATS LAST'));
select ename, sal
from emp
order by sal asc;
SELECT * FROM table(dbms_xplan.display_cursor(null, null, 'ALLSTATS LAST'));
💡 대량의 데이터를 order by 하면 그 SQL도 느려지고 다른 sql도 다같이 느려짐
select ename, sal
from emp
where sal >= 0;
SELECT * FROM table(dbms_xplan.display_cursor(null, null, 'ALLSTATS LAST'));
select ename, sal
from emp
order by sal desc;
select /*+ index_desc(emp emp_sal) */ename, sal
from emp
where sal >= 0;
💡
select /*+ 힌트 */
: 힌트는 실행계획을 제어하는 명령어
/+ index_desc(테이블 인덱스이름) /
select ename, hiredate
from emp
order by hiredate desc;
select /*+ index_desc(emp emp_hiredate) */ ename, hiredate
from emp
where hiredate <= to_date('9999/12/31','RRRR/MM/DD');
select ename, hiredate
from emp
where hiredate = (select max(hiredate)
from emp );
SELECT * FROM table(dbms_xplan.display_cursor(null, null, 'ALLSTATS LAST'));
select /*+ index_desc(emp emp_hiredate) */ ename, hiredate
from emp
where hiredate <= to_date('9999/12/31','RRRR/MM/DD') and rownum = 1;
SELECT * FROM table(dbms_xplan.display_cursor(null, null, 'ALLSTATS LAST')) ;
select ename, sal
from emp
where sal = ( select min(sal)
from emp );
SELECT * FROM table(dbms_xplan.display_cursor(null, null, 'ALLSTATS LAST'));
select /*+ index_asc(emp emp_sal ) */ ename, sal
from emp
where sal >= 0 and rownum = 1;
SELECT * FROM table(dbms_xplan.display_cursor(null, null, 'ALLSTATS LAST'));
💡 emp 테이블을 2번 엑세스 하지 않고 한번에 결과를 출력하는 SQL튜닝 방법
인덱스를 아래의 2개의 컬럼중에 1개에만 생성해야함
select ename, age, telecom
from emp21
where telecom = 'SKT' and ename='000';
💡 telecom 보다는 ename 에 인덱스를 거는게 더 성능이 좋음. telecom의 skt는 10건이고 ename의 000은 1건이므로, ename에 인덱스가 있다면 000 1건만 찾으면 끝남
create index emp21_ename
on emp21(ename);
select ename, age, address
from emp21
where age = 27 and address like '서울%' ;
select count(*)
from emp21
where age = 27;
select count(*)
from emp21
where address like '서울%';
select ename, age, address
from emp21
where 1=1
and age = 27 -- 2건
and address like '서울%'; -- 12건
create index emp21_age
on emp21(age);
where 1=1
을 쓰는 이유cmd 창에서 @demo 한번 돌리기
select ename, sal, job, deptno
from emp
where ename ='SCOTT' and deptno = 20;
select ename, sal, job, deptno
from emp
where 1=1
and ename ='SCOTT' -- 1건
and deptno = 20; -- 5건
select count(*)
from emp
where ename='SCOTT'; -- 1
select count(*)
from emp
where deptno = 20; -- 5
create index emp_ename
on emp(ename);
select ename, sal, job, deptno, hiredate
from emp
where job='SALESMAN' and deptno = 30;
select ename, sal, job, deptno, hiredate
from emp
where 1=1
and job='SALESMAN' -- 4건
and deptno = 30; -- 6건
select count(*)
from emp
where job='SALESMAN';
select count(*)
from emp
where deptno = 30;
create index emp_job
on emp(job);
select ename, age
from emp21
where age = (select max(age)
from emp21);
select /*+ index_desc(emp21 emp21_age) */ ename, age
from emp21
where age >= 0 and rownum = 1;
create index emp21_age
on emp21(age);
select deptno, null as job, sum(sal)
from emp
group by deptno
union all
select null as deptno, job, sum(sal)
from emp
group by job
union all
select null as deptno, null as job, sum(sal)
from emp
order by deptno, job;
select deptno, job, sum(sal)
from emp
group by grouping sets(deptno, job, ());
💡 인덱스를 무조건 많이 만든다고 좋은 것인가?
1. 필요한 컬럼에만 인덱스를 생성해줘야됨
2. 인덱스가 많아지면 역효과로 테이블에 입력작업이 느려짐
3. 테이블의 데이터를 지우면 테이블의 데이터는 지워지지만, 인덱스는 바로 지워지지 않음. 나중에 dba 가 db reorg 작업을 하면서 불필요한 leaf들을 정리해줘야 함
🔸 인덱스 구조
root - branch - leaf
select * from user_indexes;
select 'drop index ' || index_name || ';'
from user_indexes;
drop index EMP21_ENAME;
drop index EMP_ENAME;
drop index EMP21_AGE;
drop index EMP_JOB;
drop index EMP_SAL;
drop index EMP_HIREDATE;
alter table EMP_KIND2
drop constraint PK_EMP_KIND2;
alter table members
drop constraint SYS_C008577 cascade;