본문 바로가기

기록/자바_국비

[배운내용정리] 1218 자바 국비교육 _ Oracle

728x90

2020년 12월 18일 9시 ~ 15시 30분 zoom으로 수업 진행

 


서브쿼리 (Sub Query)

단일 행 서브쿼리

 결과 값이 1개 나오는 서브쿼리

 

다중 행 서브쿼리

 결과 값이 여러 줄 나오는 서브쿼리

 

다중 열 다중 행 서브쿼리

 

서브쿼리의 사용 위치

 SELECT , FROM , WHERE , GROUP BY , HAVING , ORDER BY , JOIN

 DML : INSERT , UPDATE , DELETE
 DDL : CREATE TABLE, CREATE VIEW

 어느 위치든 사용 가능하다.

 

RANK() 
 동일한 순번이 있을 경우 , 이후의 순번은 이전과 동일한 순번의 개수만큼 건너뛰고 순번을 매기는 함수

 

DENSE_RANK()

 동일한 순번 있어도 그냥 진행

 

상호 연관 쿼리 : 상관쿼리

 메인쿼리가 사용하는 컬럼 값, 계산 식을 서브쿼리에 적용하여 서브쿼리 실행 시 메인쿼리의 값도 함께 사용한다.

 

스칼라 서브쿼리
 단일행 + 서브쿼리
 SELECT, WHERE, ORDER BY 절에 사용
 보통은 SELECT 절에 많이 사용한다. 그래서 SELECT LIST 라고도 한다. 

 

CREATE  

 데이터베이스의 객체를 생성하는 DDL

 ex) 

CREATE 객체형태 객체명 (관련내용);

 

계정 생성

ex)

CREATE USER TEST IDENTIFIED BY TEST; 
  -> 유저 명 TEST , (IDENTIFIED BY TEST)비밀번호 TEST 생성

 

데이터베이스 테이블 생성

 테이블 -> 데이터를 저장하기 위한 틀(객체) , 2차원 표 형태

ex)

CREATE TABLE 테이블명( 
  컬럼명 자료형(길이) 제약조건, 
  컬럼명 자료형(길이) 제약조건 
);

 

제약조건

 테이블을 생성할 때 각 컬럼에 기록하는 것에 대한 제약사항을 설정할때 사용하는 조건

 

종류 기능
NOT NULL NULL 값을 허용하지 않는다. 
반드시 값을 기록해야 하는 경우 설정
UNIQUE 중복 값을 허용하지 않는다. 
중복되는 값으로 추가 / 수정을 하지 못하게 막는 제약조건
CHECK 지정한 입력 값 이외에 다른 값은 허용하지 않는다. 
PRIMARY KEY  (NOT NULL + UNIQUE) 
테이블 내에서 해당 행을 인식할 수 있는 고유의 값, 유일한 값
테이블 내에서 단 1개만 존재한다.

사람의 주민등록번호 같은 존재 
FOREIGN KEY 다른 테이블에 저장된 값을 연결 지어
참조로 가져오는데이터에 지정하는 제약조건

 

제약조건 설정 방법

 컬럼 레벨 제약조건

ex)

CREATE TABLE USER_UNIQUE(
    USER_NO NUMBER,
    USER_ID VARCHAR2(20) UNIQUE, --컬럼 레벨 제약조건
    USER_PWD VARCHAR2(30)
);

 

 테이블 레벨 제약조건

 컬럼이 모두 작성된 후에 별도로 작성하는 제약조건을 

ex)

CREATE TABLE USER_UNIQUE2(
    USER_NO NUMBER,
    USER_ID VARCHAR2(20),
    UNIQUE(USER_ID)
);

 

여태까지 설정한 제약조건 살펴보기

SELECT * 
FROM USER_CONSTRAINTS C1, USER_CONS_COLUMNS C2
WHERE C1.CONSTRAINT_NAME = C2.CONSTRAINT_NAME
           AND C1.TABLE_NAME = 'USER_UNIQUE';

--P : Primary key
--C : Check (not null)
--U : Unique
--R : Foreign key (reference)

 

테이블의 각 컬럼에 주석 달기

COMMENT ON COLUMN 테이블명.컬럼명 IS '주석내용';

 

테이블에 값 추가하기

 DML : INSERT

 ex)

INSERT INTO 테이블명 VALUES(값1, 값2, ...);

 


 

--1218
--sub Query
--주가 되는 메인쿼리문 안에서
--조건이나 하나의 검색을 위한 또 하나의 쿼리를 추가하는 기법


--단일행 서브쿼리
--결과 값이 1개 나오는 서브 쿼리

--ex) 최소 급여를 받는 사원의 정보 조회
SELECT MIN(SALARY)
FROM EMPLOYEE;
--1380000
SELECT *
FROM EMPLOYEE
WHERE SALARY = 1380000;

SELECT *
FROM EMPLOYEE
WHERE SALARY = (SELECT MIN(SALARY) 
                FROM EMPLOYEE);


--다중 행 서브 쿼리
--결과 값이 여러 줄 나오는 서브 쿼리

--ex) 각 직급 별 최소 급여
SELECT JOB_CODE, MIN(SALARY)
FROM EMPLOYEE
GROUP BY JOB_CODE;

SELECT *
FROM EMPLOYEE
WHERE SALARY IN (3700000, 1380000, 3400000);

SELECT *
FROM EMPLOYEE
WHERE SALARY IN (SELECT MIN(SALARY) 
                 FROM EMPLOYEE
                 GROUP BY JOB_CODE);


--다중 열 다중 행 서브쿼리
--
SELECT *
FROM EMPLOYEE
WHERE (JOB_CODE, SALARY) IN (SELECT JOB_CODE, MIN(SALARY) 
                             FROM EMPLOYEE
                             GROUP BY JOB_CODE);
                             
                             
--퇴사한 여직원과 같은 직급, 같은 부서에 근무하는 직원들의 정보 조회                             
SELECT * 
FROM EMPLOYEE
WHERE ENT_YN = 'Y';
--이태림

SELECT * 
FROM EMPLOYEE
WHERE DEPT_CODE = (SELECT DEPT_CODE
                    FROM EMPLOYEE
                    WHERE ENT_YN='Y')
     AND JOB_CODE = (SELECT JOB_CODE
                    FROM EMPLOYEE
                    WHERE ENT_YN='Y')
     AND EMP_NAME != (SELECT EMP_NAME
                    FROM EMPLOYEE
                    WHERE ENT_YN='Y');

--방금 풀어본 문제를 다중행 다중열로 변경
SELECT * 
FROM EMPLOYEE
WHERE (DEPT_CODE,JOB_CODE) = (SELECT DEPT_CODE, JOB_CODE
                              FROM EMPLOYEE
                              WHERE ENT_YN='Y')
    AND EMP_NAME <> (SELECT EMP_NAME
                     FROM EMPLOYEE
                     WHERE ENT_YN='Y');

--서브쿼리의 사용 위치
--SELECT, FROM, WHERE, GROUP BY, HAVING, ORDER BY, JOIN
--DML : INSERT , UPDATE , DELETE
--DDL : CREATE TABLE, CREATE VIEW
--어느 위치든 사용 가능하다


--FROM  위치에 사용되는 서브쿼리
--테이블명으로 직접 조회하는 대신 서브쿼리의 ResultSet을 활용하여
--데이터를 조회할 때 사용할 수 있다.
--FROM 구문의 서브쿼리를 Inline View(인라인뷰)라고 부른다.

SELECT * 
FROM (
        SELECT EMP_ID, EMP_NAME, DEPT_TITLE, JOB_NAME
        FROM EMPLOYEE
        JOIN DEPARTMENT ON(DEPT_CODE = DEPT_ID)
        JOIN JOB USING(JOB_CODE)
     );


--TON-N 분석 조회
--가장 조건에 부합하는 내용을 순위화 하여 특정 순번까지 조회하는 방식
--ROWNUM : 데이터를 조회할 때 각 행의 번호를 매겨주는 함수
SELECT ROWNUM, EMP_NAME, SALARY
FROM EMPLOYEE;

SELECT ROWNUM, EMP_NAME, SALARY
FROM EMPLOYEE
WHERE ROWNUM < 6;


--연습
--급여 기준으로 가장 높은 급여를 받는 사원
--상위 5명 조회하여 
--사번, 사원명, 급여 조회
SELECT ROWNUM, EMP_ID, EMP_NAME, SALARY
FROM EMPLOYEE
ORDER BY SALARY DESC;
--ROWNUM은 FROM이 실행될때 번호가 정해져서
--ORDER BY SALARY ASC 해도 순서가 제각각임

--ROWNUM 을 기준으로 6~10번째 직원의 사번 , 사원명 조회하기
SELECT ROWNUM, EMP_ID, EMP_NAME
FROM EMPLOYEE
WHERE ROWNUM BETWEEN 6 AND 10;
--ROWNUM은 반드시 1부터 값을 증가시키기 떄문에
--1을 초과한 순서를 먼저 계산할 수 없다.



--실습1
--급여 평균이 3위 안에 드는 부서의 부서코드, 부서명, 급여 평균을 조회
SELECT * FROM DEPARTMENT;
--DEPT_ID, DEPT_TITLE

--1. 내부에서 정렬할 인라인 뷰 쿼리 만들기
SELECT DEPT_CODE, DEPT_TITLE, TRUNC(AVG(SALARY), -3)
FROM EMPLOYEE E, DEPARTMENT D
WHERE DEPT_CODE = DEPT_ID
GROUP BY DEPT_CODE, DEPT_TITLE
ORDER BY 3 DESC;

--2. ROWNUM과 인라인 뷰를 반영하여 상위 3개 조회
SELECT ROWNUM, DEPT_CODE, DEPT_TITLE, 평균
FROM (
        SELECT DEPT_CODE, DEPT_TITLE, TRUNC(AVG(SALARY), -3) "평균"
        FROM EMPLOYEE E, DEPARTMENT D
        WHERE DEPT_CODE = DEPT_ID
        GROUP BY DEPT_CODE, DEPT_TITLE
        ORDER BY 3 DESC
        )
WHERE ROWNUM < 4;
--ROWNUM 이 FROM으로 값을 읽어올때 넘버링이 되기때문에
--인라인뷰로 코드 작성 시 읽어오는 순서대로 ROWNUM이 넘버링된다.


--RANK() , DENSE_RANK()
--RANK() : 동일한 순번이 있을 경우 , 이후의 순번은 이전과 동일한 순번의 개수만큼 
--         건너뛰고 순번을 매기는 함수
--1
--2
--2
--4

SELECT EMP_NAME, SALARY, RANK() OVER(ORDER BY SALARY DESC) "순위"
FROM EMPLOYEE;
--급여 내림차순으로 정렬시키고 , 순위를 매겨준 것
--정렬하는 순서대로 넘버링

SELECT EMP_NAME, SALARY, RANK() OVER(ORDER BY SALARY DESC) "순위"
FROM EMPLOYEE
WHERE 순위 < 4;
--에러 : 얘도 로우넘처럼 사용해야한다

SELECT * 
FROM (
        SELECT EMP_NAME, SALARY, RANK() OVER(ORDER BY SALARY DESC) "순위"
        FROM EMPLOYEE
    )
WHERE 순위 <= 3;


--DENSE_RANK() : 는 동일한 순번 있어도 그냥 진행
--1
--2
--2
--3
SELECT EMP_NAME, SALARY, DENSE_RANK() OVER(ORDER BY SALARY DESC) "순위"
FROM EMPLOYEE;


--실습2
--EMPLOYEE테이블에서
--보너스를 포함한 연봉이 가장 높은 사원 상위 5명을
--RANK() 함수를 활용하여 조회
--사번, 사원명, 부서명, 직급명, 입사일, 연봉(보너스포함)

--직원들의 연봉계산
--ORACLE
SELECT EMP_ID, EMP_NAME, DEPT_TITLE, JOB_NAME , HIRE_DATE, 
       (SALARY * NVL(BONUS, 0)+1)*12 "연봉",
       RANK() OVER(ORDER BY (SALARY * NVL(BONUS, 0)+1)*12 DESC) "연봉순위"
FROM EMPLOYEE E, DEPARTMENT D, JOB J
WHERE DEPT_CODE = DEPT_ID AND E.JOB_CODE = J.JOB_CODE;

--ANSI
SELECT EMP_ID, EMP_NAME, DEPT_TITLE, JOB_NAME , HIRE_DATE, 
       (SALARY * NVL(BONUS, 0)+1)*12 "연봉",
       RANK() OVER(ORDER BY (SALARY * NVL(BONUS, 0)+1)*12 DESC) "연봉순위"
FROM EMPLOYEE
JOIN DEPARTMENT ON(DEPT_CODE = DEPT_ID)
JOIN JOB USING(JOB_CODE);


--상위 5명 구하는 쿼리문 작성
SELECT *
FROM (
    SELECT EMP_ID, EMP_NAME, DEPT_TITLE, JOB_NAME , HIRE_DATE, 
           (SALARY * NVL(BONUS, 0)+1)*12 "연봉",
           RANK() OVER(ORDER BY (SALARY * NVL(BONUS, 0)+1)*12 DESC) "연봉순위"
    FROM EMPLOYEE
    JOIN DEPARTMENT ON(DEPT_CODE = DEPT_ID)
    JOIN JOB USING(JOB_CODE)
)
WHERE "연봉순위" < 6;



--실습3
--부서별 급여 합계가 전체 부서 급여 총합의
--20%보다 많은 부서의 
--부서명과 부서 급여 합계를 조회

--1. 일반 단일행 서브쿼리
SELECT DEPT_CODE, SUM(SALARY)
FROM EMPLOYEE
GROUP BY DEPT_CODE
HAVING SUM(SALARY) > (SELECT SUM(SALARY)*0.2 FROM EMPLOYEE);
--HAVING 그룹별 합계 > (전체 부서 급여 총합)


--2. 인라인 뷰 활용
--서브쿼리
SELECT DEPT_CODE, SUM(SALARY) FROM EMPLOYEE GROUP BY DEPT_CODE;

SELECT DEPT_CODE, SUMSAL
FROM (
    SELECT DEPT_CODE, SUM(SALARY) "SUMSAL" FROM EMPLOYEE GROUP BY DEPT_CODE
)
WHERE SUMSAL > (SELECT SUM(SALARY)*0.2 FROM EMPLOYEE);


--상호 연관 쿼리 : 상관쿼리
--위에서 배운 내용으로는 쿼리 각각 서로 연관이 없었음
--얘는 메인쿼리가 사용하는 컬럼 값, 계산 식을 서브쿼리에 적용하여
--서브쿼리 실행 시 메인쿼리의 값도 함께 사용한다.

--사원의 직급에 따른 급여 평균보다 많이 받는 사원의 정보 조회
SELECT JOB_CODE, TRUNC(AVG(SALARY), -3)
FROM EMPLOYEE
GROUP BY JOB_CODE;

SELECT EMP_ID, EMP_NAME, JOB_CODE, SALARY
FROM EMPLOYEE E
WHERE SALARY > (
                SELECT AVG(SALARY)
                FROM EMPLOYEE E2
                WHERE E.JOB_CODE = E2.JOB_CODE
        );
--E2에서 SALARY를 평균을 내서
--WHERE로 빠져나와 E의 데이터 한 줄을 읽고 거기의 SALARY가 서브쿼리의 SALARY평균보다 크다면! 데이터를 조회하고 아닐경우 E의 다음 테이블로 넘어가서 조회하고 맞을때까지 계속 조회함, 맞을경우 출력


--스칼라 서브쿼리
--단일행 + 서브쿼리
--SELECT, WHERE, ORDER BY 절에 사용
--보통은 SELECT 절에 많이 사용한다. 그래서 SELECT LIST 라고도 한다. 

--연습(SELECT)
--모든 사원의 사번, 사원명, 관리자 사번, 관리자 명을 조회
--관리자가 없을 경우 '없음' 이라고 표시
SELECT EMP_ID, EMP_NAME, NVL(MANAGER_ID, '없음') "MANAGER_ID", 
        NVL( (SELECT EMP_NAME 
              FROM EMPLOYEE M 
              WHERE E.MANAGER_ID = M.EMP_ID), '없음') "관리자 이름"
FROM EMPLOYEE E
ORDER BY "관리자 이름";
--E 테이블을 먼저 가져오고 SELECT안에서 사용한 M 테이블의 데이터를 비교하는 쿼리문


--연습(WHERE)
--자신이 속한 직급의 평균 급여보다 많이 받는 사원의
--이름, 직급명, 급여정보 조회
SELECT EMP_NAME, JOB_NAME, SALARY
FROM EMPLOYEE E
JOIN JOB J ON(E.JOB_CODE = J.JOB_CODE)
WHERE SALARY > (
                    SELECT AVG(SALARY)
                    FROM EMPLOYEE E2
                    WHERE E.JOB_CODE = E2.JOB_CODE
                );


--연습(ORDER BY)
--모든 직원의 사번, 사원명, 부서코드를 조회
--부서명 내림차순으로 정렬하는 ORDER BY를 서브쿼리 형식으로 구현

--일반 JOIN
SELECT EMP_ID, EMP_NAME, DEPT_CODE
FROM EMPLOYEE
JOIN DEPARTMENT ON(DEPT_CODE = DEPT_ID)
ORDER BY DEPT_TITLE DESC;


--스칼라 서브쿼리
--ORDER BY에는 스칼라만 사용 가능하다~
SELECT EMP_ID, EMP_NAME, DEPT_CODE
FROM EMPLOYEE E
ORDER BY (
            SELECT DEPT_TITLE
            FROM DEPARTMENT D
            WHERE E.DEPT_CODE = D.DEPT_ID
        ) DESC NULLS LAST;
--NULLS LAST는 NULL 값이 맨 마지막에 오게 한다.


--DDL
--CREATE : 데이터베이스의 객체를 생성하는 DDL
--CREATE 객체형태 객체명 (관련내용)


-- [SYSTEM 계정에서 실행해야 한다.]
--TEST 계정 생성
CREATE USER TEST IDENTIFIED BY TEST;
--유저 명 TEST , (IDENTIFIED BY TEST)비밀번호 TEST 생성

--TEST 유저에게 권한 부여
GRANT CONNECT, RESOURCE TO TEST;

-- [TEST 계정에서 실행하기]
--테이블 생성시
--CREATE TABLE 테이블명(
--  컬럼명 자료형(길이) 제약조건,
--  컬럼명 자료형(길이) 제약조건
--);

--제약조건 : 테이블에 데이터를 저장하고자 할 때 지켜야 하는 규칙
--NOT NULL : NULL 값을 허용하지 않는다.
--UNIQUE   : 중복 값을 허용하지 않는다.
--CHECK    : 지정한 입력 값 이외에 다른 값은 허용하지 않는다.
--PRIMARY KEY : (NOT NULL + UNIQUE)
--              테이블 내에서 해당 행을 인식할 수 있는 고유의 값, 유일한 값
--              테이블 내에서 단 1개만 존재한다. 사람의 주민등록번호 같은 존재
--FOREIGN KEY : 다른 테이블에 저장된 값을 연결 지어서
--              참조로 가져오는 데이터에 지정하는 제약조건


--테이블 생성
--데이터를 저장하기 위한 틀(객체)
--2차원 표 형태
CREATE TABLE MEMBER (
    MEMBER_NO NUMBER,               --회원 번호
    MEMBER_ID VARCHAR2(20),         --회원 아이디
    MEMBER_PWD VARCHAR2(20),        --회원 비밀번호
    MEMBER_NAME VARCHAR2(15)        --회원 이름
);

SELECT * FROM MEMBER;
--넣은 데이터가 없어서 데이터를 확인 할 수는 없지만 컬럼은 확인 가능


--테이블의 각 컬럼에 주석 달기
--COMMENT ON COLUMN 테이블명.컬럼명 IS '주석내용';
COMMENT ON COLUMN MEMBER.MEMBER_NO IS '회원 번호';
COMMENT ON COLUMN MEMBER.MEMBER_ID IS '회원 아이디';
COMMENT ON COLUMN MEMBER.MEMBER_PWD IS '회원 비밀번호';
COMMENT ON COLUMN MEMBER.MEMBER_NAME IS '회원 이름';


--데이터 사전
--현재 접속한 사용자 계정이 보유한 테이블 목록
SELECT * FROM USER_TABLES;

--테이블 정보 확인하기
DESC MEMBER;

SELECT * 
FROM USER_TAB_COLUMNS
WHERE TABLE_NAME = 'MEMBER';


--주석 확인
SELECT * 
FROM ALL_COL_COMMENTS
WHERE TABLE_NAME = 'MEMBER';


---------------------------------------------------
--제약조건 (CONSTRAINTS)
--테이블을 생성할 때 각 컬럼에 기록하는 것에 대한 제약사항을 설정할때 사용하는 조건

--현재 사용자 계정에 관련된 제약 조건을 확인
SELECT * FROM USER_CONSTRAINTS;
--아직 설정한게 없어서 뜨지는 않음

--컬럼마다 설정한 제약조건 확인
SELECT * FROM USER_CONS_COLUMNS;


--제약조건
--[NOT NULL]
--'널 값을 허용하지 않는다'
--반드시 값을 기록해야 하는 경우
--데이터 삽입 / 수정 / 삭제 시에 NULL 값을 허용하지 않도록 컬럼 작성 시 함께 설정


DROP TABLE USER_NOCONS;
--이러한 이름의 테이블이 없으면 오류난다.

CREATE TABLE USER_NOCONS(
    USER_NO NUMBER,
    USER_ID VARCHAR2(20),
    USER_PWD VARCHAR2(30),
    USER_NAME VARCHAR2(15),
    GENDER VARCHAR2(3),
    PHONE VARCHAR2(14),
    EMAIL VARCHAR2(30)
);


--테이블에 값 추가하기
--DML : INSERT
INSERT INTO USER_NOCONS
VALUES(1, 'USER01', 'PASS01', '햄서터', '남', '010-1992-1204', 'HAM@EXAMPLE.COM');

INSERT INTO USER_NOCONS
VALUES(2, NULL, NULL, NULL, '남', NULL, NULL);

SELECT * FROM USER_NOCONS;
--NULL 값이 들어가있음을 확인 할 수 있다.



CREATE TABLE USER_NOT_NULL(
    USER_NO NUMBER NOT NULL,
    USER_ID VARCHAR2(20) NOT NULL,
    USER_PWD VARCHAR2(30) NOT NULL,
    USER_NAME VARCHAR2(15) NOT NULL,
    GENDER VARCHAR2(3),
    PHONE VARCHAR2(14),
    EMAIL VARCHAR2(30)
);

SELECT * FROM user_not_null;

INSERT INTO user_not_null VALUES(1, 'USER01', 'PASS01', '햄서터', '남', '010-1992-1204', 'HAM@EXAMPLE.COM');

SELECT * FROM user_not_null;

INSERT INTO user_not_null VALUES(2, NULL, NULL, NULL, '남', NULL, NULL);
--에러난다 -> cannot insert NULL 라고 명시된다.

INSERT INTO user_not_null VALUES(2, 'USER02', 'PASS02', 'RJ', NULL, NULL, NULL);
--이거는 에러 안 나고 데이터 삽입 잘 된다. 왜? -> 제약조건으로 성별, 폰, 이메일은 NOT NULL 제약조건을 걸어주지 않았기 떄문이다.

SELECT * FROM user_not_null;


--UNIQUE 제약조건
--중복을 허용하지 않는 제약조건
--만약 중복되는 값으로 추가 / 수정을 하지 못하게 막는 제약조건

INSERT INTO user_not_null VALUES(1, 'USER01', 'PASS01', '햄서터', '남', '010-1992-1204', 'HAM@EXAMPLE.COM');

SELECT * FROM user_not_null;
--중복되는 값이 들어가지는 모습을 볼 수 있다.

--테스트용 테이블 만들기
CREATE TABLE USER_UNIQUE(
    USER_NO NUMBER,
    USER_ID VARCHAR2(20) UNIQUE, --컬럼 레벨 제약조건
    USER_PWD VARCHAR2(30),
    USER_NAME VARCHAR2(15)
);

INSERT INTO USER_UNIQUE VALUES(1, 'USER01', 'PASS01', '햄서터');

INSERT INTO USER_UNIQUE VALUES(1, 'USER01', '1234', '김쿼카');
--에러난다 : unique constraint USER01이 중복되어서 UNIQUE 제약조건에 위배된다 . 테이블 생성시에 UNIQUE 선언하면 중복되는 값이 올 수 없다.

SELECT * FROM USER_UNIQUE;


SELECT * 
FROM USER_CONSTRAINTS C1, USER_CONS_COLUMNS C2
WHERE C1.CONSTRAINT_NAME = C2.CONSTRAINT_NAME
      AND C1.TABLE_NAME = 'USER_UNIQUE';

--P : Primary key
--C : Check (not null)
--U : Unique
--R : Foreign key (reference)


--제약조건 설정 방법은 2가지 있는데
--하나는 위에서 사용한 컬럼 레벨에서 제약조건 설정하기

--다른 하나는 테이블 레벨에서 제약조건 설정
CREATE TABLE USER_UNIQUE2(
    USER_NO NUMBER,
    USER_ID VARCHAR2(20), 
    --USER_ID VARCHAR2(20), UNIQUE, --컬럼 레벨 제약조건
    USER_PWD VARCHAR2(30),
    USER_NAME VARCHAR2(15),
    UNIQUE(USER_ID) --컬럼이 모두 작성된 후에 별도로 작성하는 제약조건을
                    --테이블 레벨 제약조건이라고 한다
);