본문 바로가기

Oracle

22일차 공부 SELF JOIN, ANSI JOIN, SUB QUERY

1 교시

 

** self join

=> 동일한 테이블을 가지고 join

=> 하나의 테이블에 동일한 의미를 갖는 열이 2개 이상 존재하는 경우에 가능

=> 동일한 의미를 갖는 컬럼을 이용해서 다른 정보를 조회하고자 할 때 사용

=> from 절에 동일한 이름의 2개의 테이블이 설정되게 되는데 이렇게 되면 이름에 문제가 발생하기 대문에 이름 뒤에 별칭을 부여해서 테이블을 구분

=> from 절에서 설정한 별명은 이후 모든 절에 적용이 되어야 한다.

=> select 절에서 열 이름에 별명을 부여하는 것은 실제 별명이 아니고 출력하기 위해서 사용하는 개념이라서 별명을 써도 되고 원래의 이름을 입력해도 된다.

=> from 절에서 부여한 별명은 데이터를 구분하기 위해서 부여한 별명이기 때문에 이후에는 별명으로 사용해야 한다.

 

=> emp 테이블 구조

EMPNO(사원번호) - 정수

ENAME(사원이름) - 문자열

JOB(담당업무) - 문자열

MGR(관리자 사원번호) - 정수

HIREDATE(입사일) - 날짜

SAL(급여) - 실수

COMM(상여금)  - 실수

DEPTNO(부서번호) - 정수

 

=> DEPT 테이블

DEPTNO(부서번호) - 정수

DNAME(부서명) - 문자열

LOC(지역) - 문자열

 

EX1) EMP 테이블에서 ENAME이 SCOTT인 사원의 관리자 이름을 조회

SELECT      e2.ename

FROM       EMP e1, EMP e2

WHERE      e1.ENAME = 'SCOTT' and e1.mor = d2.empno;

 

ex2) EMP 테이블에서 EMPNO가 7369인 사원의 ENAME과 관리자의 ENAME 그리고 JOB을 조회

select e1.ename, e2.ename, e2.job

from emp e1, emp e2

where e1.empno = '7369' and e1.mgr = e2.empno;

--샘플데이블 삭제

DROP TABLE sns;

-- 샘플데이터만들기
CREATE TABLE SNS(
id varchar2(50),
friend varchar2(50));


--샘플 데이터 삽입
INSERT INTO SNS values('ggangpae1', 'jessica72');
INSERT INTO SNS values('jessica72', 'hunt12');
INSERT INTO SNS values('jessica72', 'max99');
INSERT INTO SNS values('jessica72', 'ggangpae1');

 

-- 샘플 데이터 확인
SELECT *
FROM SNS;

 

-- 내 친구의 친구 중에서 나를 제외한 데이터를 가져오기

SELECT S2.FRIEND

FROM SNS S1, SNS S2

WHERE s1.id = 'ggangpae1' AND s1.friend = =s2.id AND s1.id != s2.friend;

 

 

 

 

 

 

2~3 교시

 

** ANSI JOIN

=> 미국 표준 협회가 제시한 JOIN 문법

1. CROSS JOIN - Cartesina Product

=> 특별한 조인 조건 없이 2개 테이블의 모든 조합을 만들어 내는 것

 

SELECT 

FROM 테이블이름1 CROSS JOIN 테이블이름2;

 

=> 열의 개수는 앙쪽 테이블의 영 개수의 합이 되고 행의 개수는 앙쪽 테이블 행개수의 곱이 된다.

EMP (8열 14행), DEPT (3열 4행) 

--EMP 테이블과 DEPT 테이블의 CROSS JOIN

SELECT *

FROM EMP CROSS JOIN DEPT;

 

2. ANSI INNER JOIN

=> 양쪽 테이블에 동일한 의미를 갖는 열의 값이 양쪽 테이블 모두에 존재하는 경우에만 결합하는 JOIN

 

SELECT *

FROM 테이블이름1 INNER JOIN 테이블이름2

ON 테이블이름1.열이름 = 테이블이름2.열이름;

 

EMP 테이블의 DEPTNO는 부서번호이고 DEPT테이블의 DEPTNO도 부서번호

--EMP 테이블과 DEPT 테이블을 INNER JOIN

SELECT *

FROM EMP INNER JOIN DEPT

ON EMP.DEPTNO = DEPT.DEPTNO;        -- 이 줄을 USING DEPTNO; 로 작성해도 동일하다

=> 동일한 의미를 갖는 열 이름이 양쪽 테이블에서 동일하다면 ON 대신에 USING 사용 가능

ON 대신 USING(공통된 열이름)

 

=> 양쪽 테이블의 공통된 의미를 갖는 열 이름이 같을 때는 INNER JOIN 대신에 NATURAL JOIN이라고 설정해서 공통된 열 이름을 생략할 수 있다.

--EMP 테이블과 DEPT 테이블을 INNER JOIN

SELECT *

FROM EMP NATURAL JOIN DEPT;

 

 

3. ANSI OUTER JOIN

=> OUTER JOIN : 한쪽 테이블에만 존재하는 데이터도 JOIN에 참여

SELECT *

FROM 테이블이름1[LEFT | RIGHT | FULL] OUTER JOIN 테이블이름2

ON 테이블이름1.열이름 = 테이블이름2.열이름;

 

EMP 테이블에는 DEPTNO가 10,20,30이 있고

DEPT 테이블에는 DEPTNO가 10,20,30,40이 존재

INNER JOIN을 하게 되면 DEPT 테이블의 DEPTNO가 40인 데이터는 조회되지 않는다.

40인 데이터를 조회할려면 OUTER JOIN을 이용해야 한다.

 

--EMP 테이블과 DEPT 테이블의 FULL OUTER JOIN

SELECT *

FROM EMP FULL OUTER JOIN DEPT

ON EMP.DEPTNO = DEPT.DEPTNO;

 

 

** SET OPERATION

=> 동일한 구조를 갖는 테이블 끼리 연산

1. UNION, UNUNION ALL

=> 테이블 2개를 합치는 것

 

2. INTERSACT

=> 양쪽 테이블에 공통으로 존재하는 행만 추출해서 새로운 테이블을 만드는 것

 

3. MINUS

=> 한쪽 테이블에만 존재하는 행만 추출해서 새로운 테이블을 만드는 것

 

 

** JOIN의 단점

=> JOIN은 2개 테이블의 조합이라서 시간이 많이 걸리는 작업이므로 JOIN을 하지 않고 해결할수 있다면 JOIN은 사용하지 않는 것이 좋다.

 

** SUB QUERY

=> QUERY 안에 다른 QUERY가 포함되는 것

=> SQL에서는 SUB QUERY는 반드시 ( ) 안에 포함해야 한다.

=> SUB QUERY는 MAIN QUERY가 수행되기 전에 1번만 실행된다.

=> 보통은 SELECT 구문의 WHERE 절이나 FROM 절에서 사용된다.

 

1. 단일행 서브쿼리

=> 서브 쿼리의 결과가 하나의 행이라서 단일행 연산자를 사용할 수 있는 경우

=> 단일 행 연산자 - =. !=, >, >= , <, <=

 

EX1) EMP 테이블에서 ENAME이 SCOTT인 사원과 동일한 LOC에 근무중인 사원의 ENAME과 SAL의 값을 조회

=> ENAME과 SAL은 EMP 테이블에 존재

=> LOC는 DEPT 테이블에 존재

 

SELECT ENAME, SAL

FROM EMP, DEPT

WHERE EMP.DEPTNO = DEPT.DEPTNO AND ENAME = 'SCOTT';

 

SELECT LOC

FROM EMP, DEPT

WHERE EMP.DEPTNO = DEPT.DEPTNO AND ENAME =  'SCOTT';

 

-- SCOTT인 사원과 부서번호(DEPTNO)가 동일한 부서번호를 가진 사원의 ENAME과 SAL 조회

SELECT ENAME, SAL

FROM EMP

WHERE DEPTNO = (SELECT DEPTNO

                          FROM EMP

                          WHERE ENAME = 'SCOTT');

 

 

SELECT ENAME, SAL
FROM EMP
WHERE DEPTNO = (SELECT DEPTNO
                FROM EMP
                WHERE ENAME = 'SCOTT') AND ENAME != 'SCOTT';  -- SCOTT 빼고 조회

 

 

EX2)EMP 테이블에서 DEPT 테이블의 LOC가 DALLAS인 사원의 ENAME과 DEPTNO를 조회

=> 여러 테이블의 정보를 이용하더라도 조회하고자 하는 열이 하나의 테이블에 전부 존재한다면 서브 쿼리로 해결이 가능하다.

SELECT ENAME, DEPTNO

FROM EMP

WHERE DEPTNO = (SELECT DEPTNO

                          FROM DEPT

                          WHERE LOC = 'DALLAS');

 

 

 

4 교시

 

EX3) EMP 테이블에서 SAL의 값이 1250 이상인 데이터의 ENAME 과 SAL을 조회

SELECT ENAME, SAL

FROM EMP

WHERE SAL >= 1250;

 

EX4) EMP 테이블에서 SAL의 값이 ENAME이 MILLER인 사원의 ENAME 과 SAL을 조회

SELECT ENAME, SAL

FROM EMP

WHERE SAL >= (SELECT SAL

                      FROM EMP

                      WHERE ENAME = 'MILLER');

 

 

EX5) EMP 테이블에서 SAL의 값이 평균이상인 데이터 ENAME 과 SAL을 조회

SELECT ENAME, SAL
FROM EMP
WHERE SAL >= (SELECT AVG(SAL)
                      FROM EMP);

 

EX6) DNAME이 SALES 인 사원의 ENAME과 JOB 그리고 SAL을 조회

=> DNAME은 DEPT 테이블에 존재하고 ENAME과 JOB 그리고 SAL은 EMP 테이블에 존재

=> 테이블 이름이 2개가 나오면 JOIN이나 SUB QUERY로 해결 ( 그 기준은 조회하고자 하는 컬럼이 하나의 테이블에 존재하면 SUB QUREY로 해결 가능, 조회하고자 하는 컬럼이 2개의 테이블에 나누어 존재하면 JOIN으로 해결)

=> 2개의 테이블을 가지고 작업을 할 때는 동일한 의미를 갖는 컬럼을 이용해야 한다.

SELECT ENAME, JOB, SAL
FROM EMP
WHERE DEPTNO = (SELECT DEPTNO 
                          FROM DEPT 
                          WHERE DNAME = 'SALES');

 

 

EX7) ENAME이 SCOTT 인 사원과 동일한 LOC에 근무하는 사원의 ENAME과 SAL을 조회

=> LOC는 DEPT 테이블에 존재

=> EMP 테이블과 DEPT 테이블은 DEPTNO를 같이 소유

 

SELECT ENAME, SAL

FROM EMP

WHERE DEPTNO = (SELECT DEPTNO

                          FROM DEPT

                          WHERE LOC = (SELECT LOC 
                                              FROM DEPT 
                                              WHERE DEPTNO = (SELECT DEPTNO 
                                                                        FROM EMP 
                                                                        WHERE ENAME='SCOTT'))) AND ENAME != 'SCOTT';  

=> SCOTT 자신은 빼야한다.

 

 

 

2) 다중행 서브 쿼리

=> 서브 쿼리의 결과가 0개나 2개 이상이어서 단일 행 연산자로 비교가 불가능한 서브쿼리

=> 이 때는 IN, NOT IN, ANY, ALL 등의 다중행 연산자를 사용해야 한다.

 

EX) LOC가 DALLAS이거나 CHICAGO인 곳에서 근무하는 사원들의 ENAME과 JOB을 조회

SELECT ENAME, JOB

FROM EMP

WHERE DEPTNO = (SELECT DEPTNO

                           FROM DEPT

                           WHERE LOC = 'DALLAS' OR LOC = 'CHICAGO');    --에러

이렇게 작성하면 DEPTNO가 2개 리턴되서 =로 비교할 수 없다고 에러가 발생

=> 서브쿼리를 이용해서 = 나 != 로 비교할 때 2개 이상의 행이 리턴되면 = 대신에 IN, != 대신에 NOT IN으로 바꿔야 한다.

 

EX2) EMP 테이블에서 DEPTNO가 30인 사원들 전체보다 SAL이 더 많은 사원의 ENAME과 SAL을 조회

SELECT ENAME, SAL

FROM EMP

WHERE SAL > (SELECT SAL

                    FROM EMP

                    WHERE DEPTNO = 30);  --이렇게 쓰면 서브쿼리의 결과가 2개 이상이라서 에러

 

SELECT ENAME, SAL
FROM EMP
WHERE SAL > ALL(SELECT SAL
                        FROM EMP
                        WHERE DEPTNO = 30);  


SELECT ENAME, SAL
FROM EMP
WHERE SAL > (SELECT MAX(SAL)
                    FROM EMP
                    WHERE DEPTNO = 30);

같은 결과가 나온다.

         

 

 

5 교시

 

** DDL(Data Definition Language)

=> 데이터 구조와 관련된 SQL - 관리자의 언어

1. 오라클의 기본 자료형

1) 숫자 : number(전체자릿수, 소수자릿수)

=> 소수 자릿수는 생략하면 0 

=> 전체 자릿수를 생략하면 38

 

2) 문자열

=> char(자릿수) : 고정된 크기의 문자열

=> varchar2(자릿수) : 크기가 변하는 문자열

=> clob : 긴 문자열

=> 한글 1글자는 3자리를 확보해야함

=> 자릿수를 한번 정하면 이 자릿수보다 큰 데이터는 저장이 안됨

 

3) 날짜 및 시간

=> date

 

4) 파일의 내용 : BLOB

 

 

 

2. 테이블 생성

create table 테이블이름(

              열이름 자료형 제약조건,

               ...

               );

 

ex1) 게시판 테이블

=> 글번호 - 정수

=> 제목 - 문자열

=> 작성자 - 문자열

=> 작성일 - 날짜

=> 조회수 - 정수

 

create table board(

          boardnum number(10),

          boardtitle varchar2(300),

          boarddate date,

          boardreadcnt number(10));

 

 

 

=> sub query를 이용한 테이블 생성

create table 테이블이름

as 

select 구문

 

ex2) emp 테이블의 데이터를 복사해서 새로운 emp01 테이블을 생성

create table emp01

as

select *

from emp;

 

--확인

select *

from emp;

 

 

ex3) emp 테이블의 구조만 복사해서 emp02 테이블을 생성

=> 구조만 복사할 때는 where 절에 무조건 거짓인 조건을 설정해서 데이터가 1개도 조회되지 않도록 한다.

create table emp02

as

select *

from emp

where 1=0;

 

--확인 - emp 테이블의 데이터가 하나도 보이지 않아야 함

select *

from emp02

 

 

 

 

6 교시

 

3. 테이블의 구조 변경

=> 테이블에 열을 추가하거나 열을 삭제하거나 열의 자료형을 변경하는 것

1) 열을 추가

alter table 테이블이름

add(컬럼이름 자료형 제약조건);

 

2) 열의 자료형을 변경

alter table 테이블이름

modify(컬럼이름 자료형 제약조건);

=> 기존의 데이터가 존재하는 경우 자료형이나 크기를 변경하는 것은 기존 데이터에 손실이 발생할 수 있다.

 

3) 열 삭제

alter table 테이블이름

drop column 열이름;

=> 문법적으로 이상이 없는 경우에도 삭제가 안되는 경우가 있을 수 있다.

다른 테이블에서 이 열의 값을 참조하는 경우에는 삭제가 안된다.

 

ex1) 기존 board 테이블에 문자 한글 100자를 저장할 수 있는 boardcontent라는 열을 추가

alter table board

add(boardcontent varchar2(300));

 

--확인

select *

from board;

 

 

 

 

ex2) 기존 board 테이블의 boardcontent 라는 열의 자료형을 char(300)로 수정

alter table board

modify(boardcontent char(300));

 

 

ex3) board 테이블에서 boardcontent 열을 삭제

alter table board

drop column boardcontent;

--열삭제 확인
SELECT *
FROM  board;

 

 

4) 서비스 중 열을 삭제하는 경우

=> 데이터베이스가 서비스 되고 있는 상태에서 열을 삭제하는 것은 권장하지 않는데 데이터베이스에 데이터를 삽입하거나 삭제 및 갱신하는 동안은 Lock이 설정되서 다른 곳에서 이 데이터베이스에 삽입, 삭제, 갱신 작업을 수행할 수 없도록 하기 때문

 

=> 이런 경우에는 데이터베이스의 열을 사용하지 못하도록 해 놓고 나중에 한가해지면 그 때가서 삭제하는 방법을 권장

 

=> 사용중지

alter table 테이블이름

set unused(열이름);

 

=> 중지 중인 열을 삭제

alter table 테이블이름

drop unused columns;

 

 

4. 테이블을 삭제

drop table 테이블이름;

=> 삭제가 안되는 경우가 발생핼 수 있는데 이 경우는 이테이블의 데이터를 다른 테이블에서 참조하고 있는 경우이다.

--board 테이블 삭제
DROP TABLE BOARD;

 

 

 

5. 테이블에서 데이터만 삭제

=> 구조는 남겨두고 데이터만 삭제

truncate table 테이블이름;

--emp01 테이블의 데이터를 삭제

select *

from emp01;   

--자료가 있는지 확인

 

truncate table emp01;  

--구조는 남겨두고 자료만 삭제

 

select *

from emp01; 

 -- 자료삭제 확인

 

6. 테이블 이름 변경

rename 예전이름 to 새로운이름;

 

 

 

7 교시

 

** DML - 데이터 조작 언어

=> 데이터를 삽입하고 삭제하고 갱신하는 명령어

1. 샘플 테이블 생성

=> 운동 선수 정보를 저장하는 테이블

 

번호 : 정수3자리

이름 : 영어 문자열 20자리 - 변하지 않음

소속팀 : 영어 문자열 30자리 - 변할 수 있다.

연봉 : 정수 5자리

데뷔연도 : 정수 4자리

 

create table NBAPlayer(

                num number(3),

                name varchar2(20),

                team char (20),

                salary number(5),

                debue number(4));

 

-- 확인

select *

from NBAplayer;

 

2. 데이터 삽입

1) 컬럼이름을 기재해서 삽입

insert into 테이블이름(열이름 나열)

values(값을 나열);

=> 열 개수와 값의 개수가 반드시 일치해야 한다.

 

1번 James Harden Rockets 10000 2011 데이터 삽입

 

insert into NBAPlayer(num, name, team, salary, debue)

values(1, James Harden, Rockets, 10000, 2011);

 

-- 확인

select * 

from NBAPlayer;

 

 

2) 열이름을 생략하고 삽입

=> 테이블을 만들 때 사용한 순서대로 데이터를 대입해야 한다.

insert into 테이블이름

values(값을 나열)

 

2번 Stephan Curry Warriors 34682 2009

insert into NBAPlayer

values(2, 'Stephan Curry', 'Warriors', 34682, 2009);

 

3) null 삽입

=> 값에 직접 null 이라고 입력해도 되고 ''만 해도 된다.

=> 데이터를 삽입할 때 열 이름을 제외하고 삽입하면 기본값이 삽입된다.

 

3번 LeBron James Lakers 33285 2003

insert into NBAPlayer

values(3, 'LeBron James', null, null, 2009);

르브론이 팀이 없다니....

 

 

3. 데이터 수정 - update

update 테이블이름

set 수정할 열이름 = 계산식이나 값,....

where 조건;

=> where를 생략하면 테이블의 모든 데이터가 수정된다.

 

update NBAPlayer

set debue = 2009

where name = 'James Harden';

 

 

--NBAPlayer 테이블의 num 값을 전부 100을 추가
update NBAPlayer
set num = num + 100;

 

 

 

 

4. 데이터 삭제 - delete

delete from 테이블이름

where 조건;

=> 조건이 있으면 조건에 맞는 데이터만 삭제하고 조건이 없으면 전체 삭제를 한다.

ex1) name이 Stephan Curry인 선수를 삭제

delete from NBAPlayer

where name = 'Stephan Curry';

 

 

ex2)  테이블의 모든 데이터 삭제

delete from NBAPlayer;