개발새발 블로그

[SQL] DML(update,insert,delete)

컴퓨터/SQL
/*
    * DML(DATA MANIPULATION LANGUAGE) : 데이터 조작 언어
      : 테이블에 값으 삽입(INSERT)하거나, 수정(UPDATE), 삭제(DELETE)하는 구문
*/
--=======================================================
/*
    * INSERT
      : 테이블에 새로운 행을 추가하는 구문
      
      [표현식]
      1) INSERT INTO 테이블명 VALUES(값1, 값2, 값3 ...);
          테이블의 모든 컬럼에 대한 값을 넣고자 할때(한행 추가)
          컬럼의 순서를 지켜서 값을 넣어야 됨
          
          값의 갯수 부족하면 => NOT ENOUGH VALUE 오류
          값의 갯수 많으면 => TOO MANY VALUES 오류
*/
SELECT * FROM EMPLOYEE;

INSERT INTO EMPLOYEE_COPY
VALUES(301, '이말순', '030616-4123456','sun@naver.com',
            '01012345678','D7','J5',3500000,0.1,200,SYSDATE,NULL,'N');

-------------------------------------------------------------------------------------------------------------------------------
/*
    2) INSERT INTO 테이블명(컬럼명, 컬럼명,...) VALUES(값1,값2,...);
        : 테이블에 내가 선택한 컬럼에만 값을 삽입할 때 사용
          -> 내가 선택한 컬럼값 이외의 값들은 NULL이 들어가고 DEFAULT 값이 설정되어 있으면 DEFAULT값이 들어감
          
        ** 주의
            - 컬럼이 NOT NULL제약조건이 있으면 반드시 값을 넣어야됨
              => DEFAULT값이 설정되어 있으면 안 넣어도됨 
*/
INSERT INTO EMPLOYEE_COPY(EMP_ID, EMP_NAME, EMP_NO, JOB_CODE, HIRE_DATE, PHONE)
                                 VALUES('302', '이고잉', '120421-3456789', 'J5', SYSDATE, '01089780987');
                                 
INSERT INTO EMPLOYEE_COPY(EMP_ID, EMP_NAME, EMP_NO, HIRE_DATE, PHONE)
                                 VALUES('303', '우재남', '110711-4456780', SYSDATE, '01089780985');

INSERT INTO EMPLOYEE_COPY(EMP_ID, EMP_NAME, HIRE_DATE, PHONE)
                                 VALUES('304', '채규태', SYSDATE, '01089780983');
-- EMP_NO의 NULL값 오류

INSERT 
    INTO EMPLOYEE_COPY
            (
                  EMP_ID
                , EMP_NAME
                , EMP_NO
                , JOB_CODE
                , HIRE_DATE
                , PHONE
            )
      VALUES
            (
                  '302'
                , '이고잉'
                , '120421-3456789'
                , 'J5'
                , SYSDATE
                , '01089780987'
            );

-------------------------------------------------------------------------------------------------------------------------------
/*
    3) INSERT INTO 테이블명 (서브쿼리);
        VALUES로 값을 직접 넣는 대신 서브쿼리로 조회된 결과를 모두 INSERT가능
*/
CREATE TABLE EMP_01 (
    EMP_ID VARCHAR2(3),
    EMP_NAME VARCHAR2(20),
    DEPT_NAME VARCHAR2(35)
);

-- 전체 사원들의 사번, 사원명, 부서명 조회
SELECT EMP_ID, EMP_NAME, DEPT_TITLE
 FROM EMPLOYEE_COPY, DEPARTMENT
WHERE DEPT_CODE = DEPT_ID(+); 

INSERT INTO EMP_01 
        (SELECT EMP_ID, EMP_NAME, DEPT_TITLE
        FROM EMPLOYEE_COPY, DEPARTMENT
        WHERE DEPT_CODE = DEPT_ID(+));

-------------------------------------------------------------------------------------------------------------------------------
/*
    * INSERT ALL
      2개 이상의 테이블에 각각 INSERT할 때
      사용하는 서브쿼리가 동일한 경우
      
      [표현식]
      INSERT ALL
      INTO 테이블명1 VALUES(컬럼명, 컬럼명, ...)
      INTO 테이블명2 VALUES(컬럼명, 컬럼명, ...)
              서브쿼리;
*/
-- 테스트할 테이블 2개 생성
CREATE TABLE EMP_DEPT
AS SELECT EMP_ID, EMP_NAME, DEPT_CODE, HIRE_DATE
      FROM EMPLOYEE
      WHERE 1=0;

CREATE TABLE EMP_MANAGER
AS SELECT EMP_ID, EMP_NAME, MANAGER_ID
      FROM EMPLOYEE
      WHERE 1=0;

-- 부서코드가 D1인 사원들의 사번, 이름, 부서코드, 입사일, 사수사번 조회
SELECT EMP_ID, EMP_NAME, DEPT_CODE, HIRE_DATE, MANAGER_ID
FROM EMPLOYEE
WHERE DEPT_CODE = 'D1';

INSERT ALL
INTO EMP_DEPT VALUES(EMP_ID, EMP_NAME, DEPT_CODE, HIRE_DATE)
INTO EMP_MANAGER VALUES(EMP_ID, EMP_NAME, MANAGER_ID)
        SELECT EMP_ID, EMP_NAME, DEPT_CODE, HIRE_DATE, MANAGER_ID
        FROM EMPLOYEE
        WHERE DEPT_CODE = 'D1';

-------------------------------------------------------------------------------------------------------------------------------
/*
    * 조건을 사용하는 INSERT ALL
        
       [표현식]
       INSERT ALL
       WHEN 조건1 THEN
                 INTO 테이블명1 VALUES(컬럼명, 컬럼명, ...)
       WHEN 조건2 THEN
                 INTO 테이블명2 VALUES(컬럼명, 컬럼명, ...)
        서브쿼리;
*/
-- 2000년도 이전에 입사한 사원들을 저장할 테이블 생성
CREATE TABLE EMP_OLD
AS SELECT EMP_ID, EMP_NAME, HIRE_DATE, SALARY
      FROM EMPLOYEE
      WHERE 1=0;

-- 2000년도 이후에 입사한 사원들을 저장할 테이블 생성
CREATE TABLE EMP_NEW
AS SELECT EMP_ID, EMP_NAME, HIRE_DATE, SALARY
      FROM EMPLOYEE
      WHERE 1=0;
      
INSERT ALL
WHEN HIRE_DATE < '2000/01/01' THEN
        INTO EMP_OLD VALUES(EMP_ID, EMP_NAME, HIRE_DATE, SALARY)
WHEN HIRE_DATE >= '2000/01/01' THEN
        INTO EMP_NEW VALUES(EMP_ID, EMP_NAME, HIRE_DATE, SALARY)
SELECT EMP_ID, EMP_NAME, HIRE_DATE, SALARY
 FROM EMPLOYEE;

--=====================================================================
/*
    * UPDATE
      테이블의 데이터를 수정하는 구문
      
      [표현식]
      UPDATE 테이블명
      SET 컬럼명 = 바꿀값,
            컬럼명 = 바꿀값,
            ....
      [WHERE 조건];  --> ** 주의 : 조건을 생략하면 모든행의 데이터가 변경됨
*/
-- DEPARTMENT 복사본 테이블 생성
CREATE TABLE DEPT_COPY
AS SELECT * FROM DEPARTMENT;

-- D9 부서명을 전략기획부로 변경
UPDATE DEPT_COPY
SET DEPT_TITLE = '전략기획부';

ROLLBACK;

UPDATE DEPT_COPY
SET DEPT_TITLE = '전략기획부'
WHERE DEPT_ID = 'D9';

-- EMPLOYEE_COPY테이블에서 왕정보의 급여를 1,500,000으로 인상
UPDATE EMPLOYEE_COPY
SET SALARY = 1500000
WHERE EMP_NAME = '왕정보';  -- 보통 primary key를 이용하여 수정
                                              -- 이름으로 수정하면 동명이인일 경우 2개의 행이 변경됨

-- EMPLOYEE_COPY테이블에서 구정하의 급여를 1,800,000으로 인상하고 보너스는 10%
UPDATE EMPLOYEE_COPY
SET SALARY = 1800000,
      BONUS = 0.1
WHERE EMP_NAME = '구정하';      

-- EMPLOYEE_COPY테이블에서 전체 사원의 급여를 기존의 급여에 10% 인상한 금액으로 변경
UPDATE EMPLOYEE_COPY
SET SALARY = SALARY * 1.1;

-------------------------------------------------------------------------------------------------------------------------------
/*
    * 서브쿼리를 사용한 UPDATE
    
      [표현식]
      UPDATE 테이블명
      SET 컬럼명 = (서브쿼리)
      [WHERE 조건];
*/
-- 이고잉 사원의 급여와 보너스를 전정보사원의 급여와 보너스값으로 변경
-- 단일행 서브쿼리
UPDATE EMPLOYEE_COPY
SET SALARY = (SELECT SALARY
                        FROM EMPLOYEE_COPY
                       WHERE EMP_NAME = '전정보'),
      BONUS = (SELECT BONUS
                        FROM EMPLOYEE_COPY
                       WHERE EMP_NAME = '전정보')
WHERE EMP_NAME = '이고잉';

-- 다중열 서브쿼리 가능
UPDATE EMPLOYEE_COPY
SET (SALARY, BONUS) = (SELECT SALARY, BONUS
                                    FROM EMPLOYEE_COPY
                                    WHERE EMP_NAME = '전정보')
WHERE EMP_NAME = '이고잉';

-- EMPLOYEE_COPY테이블에서 왕정보, 구정하, 선정보, 전지연, 장정보의 급여와 보너스를
--    오정보 사원의 급여와 보너스값으로 변경
UPDATE EMPLOYEE_COPY
SET (SALARY, BONUS) = (SELECT SALARY, BONUS
                                    FROM EMPLOYEE_COPY
                                    WHERE EMP_NAME = '오정보')
WHERE EMP_NAME IN ('왕정보', '구정하', '선정보', '전지연', '장정보');

-- ASIA 지역에서 근무하는 사원들의 보너스값을 0.3으로 변경(EMPLOYEE_COPY, DEPARTMENT, LOCATION)
UPDATE EMPLOYEE_COPY
SET BONUS = 0.3
WHERE EMP_ID IN (SELECT EMP_ID
                             FROM EMPLOYEE_COPY
                             JOIN DEPARTMENT ON (DEPT_CODE = DEPT_ID)
                             JOIN LOCATION ON(LOCATION_ID = LOCAL_CODE)
                             WHERE LOCAL_NAME LIKE 'ASIA%');


-- UPDATE시에도 제약조건에 위배되면 안됨
-- 왕정보의 DEPT_CODE를 D10으로 변경
-- 부모테이블에 D0 없어서 제약조건 위배
UPDATE EMPLOYEE_COPY
SET DEPT_CODE = 'D0'
WHERE EMP_NAME = '왕정보';

-- NOT NULL 제약조건 위배
UPDATE EMPLOYEE_COPY
SET EMP_NO = NULL
WHERE EMP_NAME = '왕정보';

-------------------------------------------------------------------------------------------------------------------------------
/*
    * DELETE
        : 테이블의 데이터를 삭제하는 구문(한 행 단위로 삭제)
        
        [표현식]
        DELETE FROM 테이블명
        [WHERE 조건]               --> ** 주의 : 조건이 없으면 모든 데이터 삭제
*/
DELETE FROM EMPLOYEE_COPY; 

ROLLBACK;
    
DELETE FROM EMPLOYEE_COPY
WHERE EMP_NAME = '왕정보';

DELETE FROM EMPLOYEE_COPY
WHERE DEPT_CODE IS NULL;
    
ROLLBACK;

/*
     * TRUNCATE : 테이블의 전체 행을 삭제할 때 사용하는 구문
                          DELETE보다 수행속도가 빠름
                          
       TRUNCATE TABLE 테이블명;
*/    
TRUNCATE TABLE EMPLOYEE_COPY4;
ROLLBACK;  --> 롤백 안됨

'컴퓨터 > SQL' 카테고리의 다른 글

[SQL] DDL(CREATE)  (0) 2024.07.17
[SQL] subQuery  (2) 2024.07.05
[SQL/ORACLE] JOIN  (0) 2024.07.04
[SQL/ORACLE] group_having_집합 연산자  (0) 2024.06.27
[SQL/ORACLE] FUNCTION 종합문제  (0) 2024.06.23

[SQL] DDL(CREATE)

컴퓨터/SQL
/*
    * DDL(DATA DEFINITION LANGUAGE) : 데이터 정의 언어
      : 오라클에서 제공하는 객체(OBJECT)를 만들고(CREATE), 구조를 변경(ALTER)하고, 구조 자체를 삭제(DROP)하는 언어
        즉, 실제 데이터 값이 아닌 구조 자체를 정의하는 언어
        주로 DB관리자, 설계자가 사용함
        
        - 오라클에서 객체(구조) : 테이블(TABLE), 뷰(VIEW), 시퀀스(SEQUENCE), 인덱스(INDEX),  패키지(PACKAGE)
                                            트리거(TRIGGER), 프로시저(PROCEDURE), 함수(FUNCTION), 동의어(SYNONYM), 사용자(USER)
*/
--==================================================================================
/*
    * CREATE
      : 객체를 생성하는 구문
*/
----------------------------------------------------------------------------------------------------------------------------------------------
/*
    1. 테이블 생성
       - 테이블이란 : 행(ROW)과 열(COLUMN)으로 구성되는 가장 기본적이 데이터베이스 객체
                            모든 데이터들은 테이블을 통해 저장됨
                            (표 형태를 DB에서는 테이블)
                            
       [표현식]
       CREATE TABLE 테이블명 (
            컬럼명 자료형(크기),
            컬럼명 자료형(크기),
            컬럼명 자료형,
            ....
        );
        
        * 자료형
        - 문자 (CHAR(바이트크기) | VARCHAR2(바이트크기) ) -> 반드시 크기 지정 해야됨
           > CHAR : 최대 2000byte까지 지정 가능
                         고정길이(지정한 크기보다 더 적은값이 들어와도 공백으로라도 채워서 처음 지정한 크기만큼 고정)
                         고정된 데이터를 넣을 때 사용
          >  VARCHAR2 : 최대 4000byte까지 지정 가능
                                 가변길이(들어온 값의 크기에 따라 달라짐)
                                 몇글자 들어올지 모를 때 사용
        - 숫자(NUMBER) : 정수, 실수, 음수, 양수
        - 날짜(DATE)
*/

-- 회원에 대한 데이터를 담기위한 테이블 MEMBER생성
CREATE TABLE MEMBER(
    MEM_NO NUMBER,
    MEM_ID VARCHAR2(20),
    MEM_PWD VARCHAR2(20),
    MEM_NAME VARCHAR2(20),
    GENDER CHAR(3),
    PHONE VARCHAR(13),
    EMAIL VARCHAR(50),
    MEM_DATE DATE
);

SELECT * FROM MEMBER;

-- 사용자가 가지고 있는 테이블정보
-- 데이터 딕셔너리 : 다양한 객체들의 정보를 저장하고 있는 시스템 테이블등
-- [참고] USER_TABLES : 이 사용자가 가지고 있는 테이블의 전반적인 구조를 확인할 수 있는 시스템 테이블
SELECT * FROM USER_TABLES;

-- [참고] USER_TAB_COLUMNS : 이 사용자가 가지고 있는 테이블의 모든 컬럼의 전반적인 구조를 확인할 수 있는 시스템 테이블
SELECT * FROM USER_TAB_COLUMNS;

----------------------------------------------------------------------------------------------------------------------------------------------
/*
    2. 컬럼에 주석 달기
    
    [표현법]
    COMMENT ON COLUMN 테이블명.컬럼명 IS '주석내용';
    
    >> 잘못 작성을 했을 때 수정한 후 다시 실행하면 덮어쓰기 됨
*/
COMMENT ON COLUMN MEMBER.MEM_ID IS '회원아이디';
COMMENT ON COLUMN MEMBER.MEM_NO IS '회원번호';
COMMENT ON COLUMN MEMBER.MEM_PWD IS '회원비밀번호';
COMMENT ON COLUMN MEMBER.MEM_NAME IS '회원이름';
COMMENT ON COLUMN MEMBER.GENDER IS '회원성별(남,여)';
COMMENT ON COLUMN MEMBER.PHONE IS '회원전화번호';
COMMENT ON COLUMN MEMBER.EMAIL IS '회원이메일';
COMMENT ON COLUMN MEMBER.MEM_DATE IS '회원가입일';

COMMENT ON COLUMN MEMBER.MEM_DATE IS '가입일';

-- 테이블에 데이터를 추가시키는 구문
-- INSERT INTO 테이블명 VALUES();
INSERT INTO MEMBER VALUES(1, 'user01', 'pass01', '홍길동', '남','010-1234-5678', 'user01@naver.com', '24/06/01');
INSERT INTO MEMBER VALUES(2, 'user02', 'pass02', '남길동', '남',null, NULL, SYSDATE);

INSERT INTO MEMBER VALUES(NULL,NULL, NULL, NULL, NULL,null, NULL, NULL);

----------------------------------------------------------------------------------------------------------------------------------------------
/*
     * 제약조건(CONSTRAINTS)
        - 원하는 데이터값(유효한 형식의 값)만 유지하게 위해 특정 컬럼에 설정하는 제약
        - 데이터 무결성 보장을 목적으로 한다
          : 데이터의 결함이 없는 상태, 즉 데이터가 정확하고 유효하게 유지된 상태
          1) 개체 무결성 제약 조건 : NOT NULL, UNIQUE, PRIMARY KEY 조건 위배
          2) 참조 무결성 제약 조건 : FOREIGN KEY(외래키) 조건 위배
          
          종류 : NOT NULL, UNIQUE, PRIMARY KEY, CHECK(조건), FOREIGN KEY
          
        - 제약조건을 부여하는 방식 2가지
         1) 컬럼 레벨 방식 : 컬럼명 자료형 옆에 기술
         2) 테이블 레벨 방식 : 모든 컬럼들을 나열한 후 마지막에 기술
*/

----------------------------------------------------------------------------------------------------------------------------------------------

/*
    * NOT NULL 제약조건
      해당 컬럼에 반드시 값이 존재해야만 할 경우(즉, 컬럼값에 NULL이 들어오면 안됨)
      삽입/수정시 NULL값을 허용하지 않도록 제한
      
      -->> 컬럼 레벨 방식만 가능
*/

-- NOT NULL 제약조건 
CREATE TABLE MEM_NOTNULL (
    MEM_NO NUMBER NOT NULL,
    MEM_ID VARCHAR2(20) NOT NULL,
    MEM_PWD VARCHAR2(20) NOT NULL,
    MEM_NAME VARCHAR2(20) NOT NULL,
    GENDER CHAR(3),
    PHONE VARCHAR2(13),
    EMAIL VARCHAR2(50)
);

INSERT INTO MEM_NOTNULL VALUES(1, 'user01', 'pass01', '이고잉', '여', '010-1234-5678', 'user01@gmail.com');
INSERT INTO MEM_NOTNULL VALUES(2, 'user01', null, '김고잉', '여', null, 'user01@gmail.com');  -- 비밀번호의 null값 허용 안함 오류
-- NOT NULL 제약조건에 위배되는 오류

INSERT INTO MEM_NOTNULL VALUES(1, 'user01', 'pass03', '김앤북', '여', '010-1234-0000', 'user03@gmail.com');

----------------------------------------------------------------------------------------------------------------------------------------------
/*
    * UNIQUE 제약조건
      : 해당 컬럼에 중복된 값이 들어가서는 안되는 경우
        삽입/ 수정시 기존에 있는 데이터의 중복값이 있을 경우 오류 발생
*/
-- 컬럼 레벨 방식
CREATE TABLE MEM_UNIQUE(
    MEM_NO NUMBER NOT NULL,
    MEM_ID VARCHAR(20) NOT NULL UNIQUE,
    MEM_PWD VARCHAR(20) NOT NULL,
    MEM_NAME VARCHAR(20) NOT NULL,
    GENDER CHAR(3),
    PHONE VARCHAR(20),
    EMAIL VARCHAR(50)
);

-- 테이블 레벨 방식
CREATE TABLE MEM_UNIQUE2(
    MEM_NO NUMBER NOT NULL,
    MEM_ID VARCHAR(20) NOT NULL,
    MEM_PWD VARCHAR(20) NOT NULL,
    MEM_NAME VARCHAR(20) NOT NULL,
    GENDER CHAR(3),
    PHONE VARCHAR(20),
    EMAIL VARCHAR(50),
    UNIQUE (MEM_ID)
);

INSERT INTO MEM_UNIQUE2 VALUES (1, 'user01', 'pass01', '이고잉', '여', '010-1234-5678', 'user01@gmail.com');
INSERT INTO MEM_UNIQUE2 VALUES (2, 'user01', 'pass01', '채규태', '여', '010-1234-5678', 'user01@gmail.com');
-- 오류 UNIQUE 제약조건 위배

-- 테이블 레벨 방식
-- 각 컬럼별로 중복값 확인
CREATE TABLE MEM_UNIQUE3(
    MEM_NO NUMBER NOT NULL,
    MEM_ID VARCHAR(20) NOT NULL,
    MEM_PWD VARCHAR(20) NOT NULL,
    MEM_NAME VARCHAR(20) NOT NULL,
    GENDER CHAR(3),
    PHONE VARCHAR(20),
    EMAIL VARCHAR(50),
    UNIQUE (MEM_NO),
    UNIQUE (MEM_ID)
);

-- 테이블 레벨 방식
-- 2개의 컬럼을 묶어서 중복값 확인( ex) (1, user01) != (1, user02)  )
CREATE TABLE MEM_UNIQUE4(
    MEM_NO NUMBER NOT NULL,
    MEM_ID VARCHAR(20) NOT NULL,
    MEM_PWD VARCHAR(20) NOT NULL,
    MEM_NAME VARCHAR(20) NOT NULL,
    GENDER CHAR(3),
    PHONE VARCHAR(20),
    EMAIL VARCHAR(50),
    UNIQUE (MEM_NO, MEM_ID)
); 
INSERT INTO MEM_UNIQUE4 VALUES (1, 'user01', 'pass01', '이고잉', '여', '010-1234-5678', 'user01@gmail.com');
INSERT INTO MEM_UNIQUE4 VALUES (2, 'user01', 'pass01', '채규태', '여', '010-1234-5678', 'user01@gmail.com');

INSERT INTO MEM_UNIQUE4 VALUES (3, 'user03', 'pass03', '우재남', 'ㄴ', '010-1234-5678', 'user01@gmail.com');
-- > 성별이 유효한 값이 아니어도 들어감

----------------------------------------------------------------------------------------------------------------------------------------------
/*
    * CHECK(조건식) 제약조건
      : 사용자가 정의 제약조건을 넣고 싶을 때
*/
-- 컬럼 레벨 방식
CREATE TABLE MEM_CHECK(
    MEM_NO NUMBER NOT NULL,
    MEM_ID VARCHAR2(20) NOT NULL UNIQUE,
    MEM_PWD VARCHAR2(20) NOT NULL,
    MEM_NAME VARCHAR2(20) NOT NULL,
    GENDER CHAR(3) CHECK(GENDER IN ('남','여')),
    PHONE VARCHAR2(13),
    EMAIL VARCHAR2(50)
);

-- 테이블 레벨 방식
CREATE TABLE MEM_CHECK2(
    MEM_NO NUMBER NOT NULL,
    MEM_ID VARCHAR2(20) NOT NULL,
    MEM_PWD VARCHAR2(20) NOT NULL,
    MEM_NAME VARCHAR2(20) NOT NULL,
    GENDER CHAR(3),
    PHONE VARCHAR2(13),
    EMAIL VARCHAR2(50),
    UNIQUE (MEM_ID),
    CHECK(GENDER IN ('남','여'))
);

INSERT INTO MEM_CHECK VALUES(1, 'user01', 'pass01', '이고잉', '여', '010-1234-5678', 'user01@gmail.com');
INSERT INTO MEM_CHECK VALUES(2, 'user02', 'pass02', '우재남', 'ㄴ', '010-1234-5678', 'user02@gmail.com');
-- CHECK 제약조건에 위배

----------------------------------------------------------------------------------------------------------------------------------------------
/*
    * PRIMARY KEY(기본키) 제약조건
      : 테이블에서 각 행들을 식별하기 위해 사용될 컬럼에 부여하는 제약조건(식별자 역할)
      
      EX) 회원번호, 학번, 사원번호, 주문번호, 예약번호, 운송장 번호, .....
      
      - PRIMARY KEY(기본키) 제약조건을 부여하면 NOT NULL + UNIQUE 제약조건을 의미
        >> 대체적으로 검색, 수정, 삭제할 때 기본키의 컬럼값을 이용함
        
        ** 주의사항 : 한 테이블당 오로지 1개만 설정 가능
*/
-- 컬럼 레벨 방식
CREATE TABLE MEM_PRIMARY(
    MEM_NO NUMBER PRIMARY KEY,
    MEM_ID VARCHAR2(20) NOT NULL UNIQUE,
    MEM_PWD VARCHAR2(20) NOT NULL,
    MEM_NAME VARCHAR2(20) NOT NULL,
    GENDER CHAR(3) CHECK(GENDER IN ('남','여')),
    PHONE VARCHAR2(13),
    EMAIL VARCHAR2(50)
);

/*
    * 제약조건 부여시 제약조건명까지 지어주는 방법
    
    >> 컬럼 레벨 방식
    CREATE TABLE 테이블명(
        컬럼명 자료형 [CONSTRAINT 제약조건명] 제약조건,
        컬럼명 자료형
    );
    
    >> 테이블 레벨 방식
    CREATE TABLE 테이블명(
        컬럼명 자료형,
        컬럼명 자료형,
        [CONSTRAINT 제약조건명] 제약조건(컬럼명)
    );
*/

-- 테이블 레벨 방식
CREATE TABLE MEM_PRIMARY3(
    MEM_NO NUMBER,
    MEM_ID VARCHAR2(20) NOT NULL CONSTRAINT ID_UNIQUE UNIQUE,
    MEM_PWD VARCHAR2(20) NOT NULL,
    MEM_NAME VARCHAR2(20) NOT NULL,
    GENDER CHAR(3) CONSTRAINT MEM_GENDER CHECK(GENDER IN ('남','여')),
    PHONE VARCHAR2(13),
    EMAIL VARCHAR2(50),
    CONSTRAINT MEM_PK PRIMARY KEY(MEM_NO)
);

INSERT INTO MEM_PRIMARY3 VALUES(1, 'user01', 'pass01', '홍길동', '남', null, null);
INSERT INTO MEM_PRIMARY3 VALUES(2, 'user02', 'pass02', '우재남', '남', null, null);

-- 오류
INSERT INTO MEM_PRIMARY3 VALUES(2, 'user03', 'pass03', '이고잉', '여', null, null);   -- unique
INSERT INTO MEM_PRIMARY3 VALUES(null, 'user03', 'pass03', '이고잉', '여', null, null);  -- null

/*
    * 복합키
      : 2개이상의 컬럼을 묶어서 하나의 기본키로 설정됨
      
      >> 테이블 레벨 방식으로만 가능

      - 복합키 사용 예시(찜 기능)
        1, A    --> 2개를 묶어서 하나의 기본키 역할을 함
        1, B
        1, A    --> 오류 떠야 됨
        2, A
        2, B
        2, C
*/

CREATE TABLE TB_LIKE(
    MEM_NO NUMBER,
    PRODUCT_NAME VARCHAR2(20),
    LIKE_DATE DATE,
    PRIMARY KEY(MEM_NO, PRODUCT_NAME)
);

INSERT INTO TB_LIKE VALUES(1, 'A', SYSDATE);
INSERT INTO TB_LIKE VALUES(1, 'B', SYSDATE);

INSERT INTO TB_LIKE VALUES(2, 'A', SYSDATE);
INSERT INTO TB_LIKE VALUES(2, 'B', SYSDATE);

-- 복합키 제약조건 오류
INSERT INTO TB_LIKE VALUES(1, 'A', SYSDATE);
INSERT INTO TB_LIKE VALUES(2, 'B', SYSDATE);

-- 복합키도 기본키이기 때문에 NOT NULL + UNIQUE 다 맞아야 됨
--  각 컬럼은 NULL값 안되고, 2개의 컬럼을 합쳐서 유일해야 됨
INSERT INTO TB_LIKE VALUES(3, NULL, SYSDATE);

--===================================================================
-- 회원등급 테이블과 회원테이블 2개 생성
-- 회원 등급 테이블
CREATE TABLE MEM_GRADE(
    GRADE_CODE NUMBER PRIMARY KEY,
    GRADE_NAME VARCHAR2(30) NOT NULL
);
INSERT INTO MEM_GRADE VALUES(10, '일반회원');
INSERT INTO MEM_GRADE VALUES(20, '우수회원');
INSERT INTO MEM_GRADE VALUES(30, '특별회원');

-- 회원 테이블
CREATE TABLE MEM(
    MEM_NO NUMBER CONSTRAINT PK PRIMARY KEY,
    MEM_ID VARCHAR(20) NOT NULL UNIQUE,
    MEM_PW VARCHAR(20) NOT NULL,
    MEM_NAME VARCHAR2(20) NOT NULL,
    GENDER CHAR(3) CONSTRAINT GEN CHECK(GENDER IN ('남', '여')),
    GRADE_ID NUMBER  
);

INSERT INTO MEM VALUES(1, 'user01', 'pass01', '이고잉', '여', 10);
INSERT INTO MEM VALUES(2, 'user02', 'pass02', '우재남', '남', 30);
INSERT INTO MEM VALUES(3, 'user03', 'pass03', '채규태', '남', 100);
-- 유효한 회원등급번호가 아님에도 불구하고 입력됨

----------------------------------------------------------------------------------------------------------------------------------------------
/*
    * 외래키(FOREIGN KEY) 제약조건
      : 다른 테이블에 존재하는 값만 들어와야되는 특정컬럼에 부여하는 제약조건
       --> 다른 테이블을 참조한다고 표현
       --> 주로 외래키 제약조건에 의해 테이블 간의 관계가 형성됨
       
     >> 컬럼 레벨 방식 : 참조할컬럼명이 primary key 였을 때 생략 가능
           컬럼명 자료형 [CONSTRAINT 제약조건명] REFERENCES 참조할테이블명 [(참조할컬럼명)]
           
     >> 테이블 레벨 방식
          [CONSTRAINT 제약조건명] FOREIGN KEY(컬럼명) REFERENCES 참조할테이블명 [(참조할컬럼명)]      
*/

CREATE TABLE MEM2(
    MEM_NO NUMBER PRIMARY KEY,
    MEM_ID VARCHAR(20) NOT NULL UNIQUE,
    MEM_PW VARCHAR(20) NOT NULL,
    MEM_NAME VARCHAR2(20) NOT NULL,
    GENDER CHAR(3) CHECK(GENDER IN ('남', '여')),
    GRADE_ID NUMBER REFERENCES MEM_GRADE(GRADE_CODE)     -- 컬럼 레벨 방식
--    , FOREIGN KEY(GRADE_ID) REFERENCES MEM_GRADE(GRADE_CODE)   -- 테이블 레벨 방식
);
INSERT INTO MEM2 VALUES(1, 'user01', 'pass01', '이고잉', '여', 10);
INSERT INTO MEM2 VALUES(2, 'user02', 'pass02', '우재남', '남', 30);
INSERT INTO MEM2 VALUES(3, 'user03', 'pass03', '채규태', '남', NULL);
-- 외래키 제약조건은 기본적으로 NULL값을 허용함

INSERT INTO MEM2 VALUES(3, 'user03', 'pass03', '채규태', '남', 100);
--  외래키 제약조건 위배
--  MEM_GRADE(부모테이블) -|--------<-- MEM2 (자식테이블)

--> 이때 부모테이블에서 데이터값을 삭제할 경우 문제 발생
--  데이터삭제 : DELETE FROM 테이블명 WHERE 조건;

-- 자식테이블에서 사용하지 않는 컬럼값은 삭제 가능
DELETE FROM MEM_GRADE
WHERE GRADE_CODE = 20;

-- 자식테이블에서 사용하고 있는 컬럼값은 삭제 불가
DELETE FROM MEM_GRADE
WHERE GRADE_CODE = 10;

----------------------------------------------------------------------------------------------------------------------------------------------
/*
    * 자식테이블 생성시 외래키 제약조건 부여할 때 삭제옵션 지정 가능
      - 삭제 옵션 : 부모테이블의 데이터 삭제 시 자식테이블이 사용하고 있는 값을 어떻게 처리할지
      
      1) ON DELETE RESTRICTED(기본값) : 삭제 제한 옵션으로, 자식테이블이 쓰고 있는 값이면 부모테이블에서 삭제 안됨
      2) ON DELETE SET NULL : 부모테이블의 데이터 삭제시 자식테이블이 쓰고 있는 값들을 NULL로 변경하고 부모테이블의 행 삭제
      3) ON DELETE CASCADE : 부모테이블의 데이터 삭제시 자식테이블이 쓰고 있는 행도 삭제
*/
DROP TABLE MEM;
DROP TABLE MEM2;

-- 외래키 생성시 참조테이블명만 넣으면 참조테이블의 기본키의 컬럼이 자동으로 설정됨
CREATE TABLE MEM(
    MEM_NO NUMBER PRIMARY KEY,
    MEM_ID VARCHAR(20) NOT NULL UNIQUE,
    MEM_PW VARCHAR(20) NOT NULL,
    MEM_NAME VARCHAR2(20) NOT NULL,
    GENDER CHAR(3) CHECK(GENDER IN ('남', '여')),
    GRADE_ID NUMBER REFERENCES MEM_GRADE ON DELETE SET NULL
);

INSERT INTO MEM VALUES(1, 'user01', 'pass01', '이고잉', '여', 10);
INSERT INTO MEM VALUES(2, 'user02', 'pass02', '우재남', '남', 30);
INSERT INTO MEM VALUES(3, 'user03', 'pass03', '송미영', '여', 20);
INSERT INTO MEM VALUES(4, 'user04', 'pass04', '김앤북', '남', 30);
INSERT INTO MEM VALUES(5, 'user05', 'pass05', '채규태', '남', NULL);

DELETE FROM MEM_GRADE WHERE GRADE_CODE = 30;
-- 삭제됨 자식은 NULL값으로 바뀜

DROP TABLE MEM;

CREATE TABLE MEM(
    MEM_NO NUMBER PRIMARY KEY,
    MEM_ID VARCHAR(20) NOT NULL UNIQUE,
    MEM_PW VARCHAR(20) NOT NULL,
    MEM_NAME VARCHAR2(20) NOT NULL,
    GENDER CHAR(3) CHECK(GENDER IN ('남', '여')),
    GRADE_ID NUMBER REFERENCES MEM_GRADE ON DELETE CASCADE
);

INSERT INTO MEM_GRADE VALUES(30, '특별회원');
INSERT INTO MEM VALUES(1, 'user01', 'pass01', '이고잉', '여', 10);
INSERT INTO MEM VALUES(2, 'user02', 'pass02', '우재남', '남', 30);
INSERT INTO MEM VALUES(3, 'user03', 'pass03', '송미영', '여', 20);
INSERT INTO MEM VALUES(4, 'user04', 'pass04', '김앤북', '남', 30);
INSERT INTO MEM VALUES(5, 'user05', 'pass05', '채규태', '남', NULL);

DELETE FROM MEM_GRADE WHERE GRADE_CODE = 30;

----------------------------------------------------------------------------------------------------------------------------------------------
/*
    * DEFAULT 값 설정하기
      컬럼의 값이 들어오지 않았을 때 기본값으로 넣어줌
      
      컬럼명 자료형 DEFAULT 기본값 [제약조건]
*/
CREATE TABLE MEMBER2 (
    MEM_NO NUMBER,
    MEM_ID VARCHAR2(20) NOT NULL,
    MEM_AGE NUMBER,
    HOBBY VARCHAR2(20) DEFAULT '없음',
    MEM_DATE DATE DEFAULT SYSDATE,
    PRIMARY KEY(MEM_NO)
);
INSERT INTO MEMBER2 VALUES(1, 'user01', 25, '잠자기', '24/06/13');
INSERT INTO MEMBER2 VALUES(2, 'user02', null, null, null);
INSERT INTO MEMBER2 VALUES(3, 'user03', 27, default, default);

INSERT INTO MEMBER2 (MEM_NO, MEM_ID, MEM_AGE) VALUES(4, 'user04', 25);


--=======================================================================
------------------------------------- tjoeun 계정에서 실행 ---------------------------------------------
/*
    * SUBQUERY를 이용한 테이블 생성
       테이블 복사하는 개념
       
       [표현식]
       CREATE TABLE 테이블명 AS 서브쿼리;     
*/
-- EMPLOYEE테이블을 복제(컬럼값까지)한 새로운 테이블 생성
CREATE TABLE EMPLOYEE_COPY
AS SELECT *
      FROM EMPLOYEE;
-- 컬럼, 데이터값 복사 됨
-- 제약조건은 NOT NULL만 복사됨. 나머지는 안됨
--     FRIMARY KEY, DEFAULT, COMMENT...는 복사안됨

-- EMPLOYEE테이블을 복제(컬럼값제외 구조만)한 새로운 테이블 생성
CREATE TABLE EMPLOYEE_COPY2
AS SELECT *
      FROM EMPLOYEE
      WHERE 1=0;
      
-- EMPLOYEE테이블을 복제(컬럼값제외 구조만)한 컬럼을 내가 원하는 컬럼만으로 새로운 테이블 생성
CREATE TABLE EMPLOYEE_COPY3
AS SELECT EMP_ID, EMP_NAME, SALARY
      FROM EMPLOYEE
      WHERE 1=0;
      
-- EMPLOYEE테이블을 복제(컬럼값제외 구조만)한 컬럼을 기준테이블에 없는 컬럼도 생성
CREATE TABLE EMPLOYEE_COPY4
AS SELECT EMP_ID, EMP_NAME, SALARY, SALARY*12 연봉
      FROM EMPLOYEE;
      -- 서브쿼리 SELECT절에 산술식 또는 함수식이 기술된 경우 반드시 별칭을 지정해야 됨
      
--------------------------------------------------------------------------------------------------------------
/*
    * 테이블 생성 후 제약조건 추가
      ALTER TABLE 테이블명 변경할 내용
       - PRIMARY KEY : ALTER TABLE 테이블명 ADD PRIMARY KEY(컬럼명)
       - FOREIGN KEY : ALTER TABLE 테이블명 ADD FOREIGN KEY(컬럼명) REFERENCES 참조할테이블명 [(참조할컬럼명)]
       - UNIQUE : ALTER TABLE 테이블명 ADD UNIQUE(컬럼명)
       - CHECK : ALTER TABLE 테이블명 ADD CHECK(컬럼에 대한 조건식)
       - NOT NULL : ALTER TABLE 테이블명 MODIFY 컬럼명 NOT NULL
*/
-- EMPLOYEE_COPY 테이블에 PRIMARY KEY 제약 조건 추가
ALTER TABLE EMPLOYEE_COPY ADD PRIMARY KEY(EMP_ID);

-- EMPLOYEE_COPY 테이블에 외래키 제약 조건 추가
ALTER TABLE EMPLOYEE_COPY ADD FOREIGN KEY(DEPT_CODE) REFERENCES DEPARTMENT;

-- COMMENT
COMMENT ON COLUMN EMPLOYEE_COPY.EMP_ID IS '사원아이디';


----------------------------------------------------- 연습문제 ---------------------------------------------------
--  DDL 계정에서
/*
도서관리 프로그램을 만들기 위한 테이블들 만들기
이때, 제약조건에 이름을 부여할 것.
       각 컬럼에 주석달기

1. 출판사들에 대한 데이터를 담기위한 출판사 테이블(TB_PUBLISHER)
   컬럼  :  PUB_NO(출판사번호) NUMBER -- 기본키(PUBLISHER_PK) 
	PUB_NAME(출판사명) VARCHAR2(50) -- NOT NULL(PUBLISHER_NN)
	PHONE(출판사전화번호) VARCHAR2(13) - 제약조건 없음

   - 3개 정도의 샘플 데이터 추가하기


2. 도서들에 대한 데이터를 담기위한 도서 테이블(TB_BOOK)
   컬럼  :  BK_NO (도서번호) NUMBER -- 기본키(BOOK_PK)
	BK_TITLE (도서명) VARCHAR2(50) -- NOT NULL(BOOK_NN_TITLE)
	BK_AUTHOR(저자명) VARCHAR2(20) -- NOT NULL(BOOK_NN_AUTHOR)
	BK_PRICE(가격) NUMBER
	BK_PUB_NO(출판사번호) NUMBER -- 외래키(BOOK_FK) (TB_PUBLISHER 테이블을 참조하도록)
			         이때 참조하고 있는 부모데이터 삭제 시 자식 데이터도 삭제 되도록 옵션 지정
   - 5개 정도의 샘플 데이터 추가하기


3. 회원에 대한 데이터를 담기위한 회원 테이블 (TB_MEMBER)
   컬럼명 : MEMBER_NO(회원번호) NUMBER -- 기본키(MEMBER_PK)
   MEMBER_ID(아이디) VARCHAR2(30) -- 중복금지(MEMBER_UQ)
   MEMBER_PWD(비밀번호)  VARCHAR2(30) -- NOT NULL(MEMBER_NN_PWD)
   MEMBER_NAME(회원명) VARCHAR2(20) -- NOT NULL(MEMBER_NN_NAME)
   GENDER(성별)  CHAR(1)-- 'M' 또는 'F'로 입력되도록 제한(MEMBER_CK_GEN)
   ADDRESS(주소) VARCHAR2(70)
   PHONE(연락처) VARCHAR2(13)
   STATUS(탈퇴여부) CHAR(1) - 기본값으로 'N' 으로 지정, 그리고 'Y' 혹은 'N'으로만 입력되도록 제약조건(MEMBER_CK_STA)
   ENROLL_DATE(가입일) DATE -- 기본값으로 SYSDATE, NOT NULL 제약조건(MEMBER_NN_EN)

   - 5개 정도의 샘플 데이터 추가하기


4. 어떤 회원이 어떤 도서를 대여했는지에 대한 대여목록 테이블(TB_RENT)
   컬럼  :  RENT_NO(대여번호) NUMBER -- 기본키(RENT_PK)
	RENT_MEM_NO(대여회원번호) NUMBER -- 외래키(RENT_FK_MEM) TB_MEMBER와 참조하도록
			이때 부모 데이터 삭제시 자식 데이터 값이 NULL이 되도록 옵션 설정
	RENT_BOOK_NO(대여도서번호) NUMBER -- 외래키(RENT_FK_BOOK) TB_BOOK와 참조하도록
			이때 부모 데이터 삭제시 자식 데이터 값이 NULL값이 되도록 옵션 설정
	RENT_DATE(대여일) DATE -- 기본값 SYSDATE

   - 3개 정도 샘플데이터 추가하기
*/
-- 1.
CREATE TABLE TB_PUBLISHER(
    PUB_NO NUMBER CONSTRAINT PUBLISHER_PK PRIMARY KEY,
    PUB_NAME  VARCHAR2(50) CONSTRAINT PUBLISHER_NN NOT NULL,
    PHONE VARCHAR2(13)
);
COMMENT ON COLUMN TB_PUBLISHER.PUB_NO IS '출판사번호';
COMMENT ON COLUMN TB_PUBLISHER.PUB_NAME IS '출판사명';
COMMENT ON COLUMN TB_PUBLISHER.PHONE IS '출판사전화번호';

-- 2. 
CREATE TABLE TB_BOOK(
    BK_NO NUMBER CONSTRAINT BOOK_PK PRIMARY KEY,
    BK_TITLE  VARCHAR2(50) CONSTRAINT BOOK_NN_TITLE NOT NULL,
    BK_AUTHOR  VARCHAR2(20) CONSTRAINT BOOK_NN_AUTHOR NOT NULL,
    BK_PRICE NUMBER,
    BK_PUB_NO NUMBER CONSTRAINT BOOK_FK REFERENCES TB_PUBLISHER ON DELETE CASCADE
);
COMMENT ON COLUMN TB_BOOK.BK_NO IS '도서번호';
COMMENT ON COLUMN TB_BOOK.BK_TITLE IS '도서명';
COMMENT ON COLUMN TB_BOOK.BK_AUTHOR IS '저자명';
COMMENT ON COLUMN TB_BOOK.BK_PRICE IS '가격';
COMMENT ON COLUMN TB_BOOK.BK_PUB_NO IS '출판사번호';

--3.
CREATE TABLE TB_MEMBER (
    MEMBER_NO NUMBER CONSTRAINT MEMBER_PK PRIMARY KEY,
    MEMBER_ID  VARCHAR2(30) CONSTRAINT MEMBER_UQ UNIQUE,
    MEMBER_PW  VARCHAR2(30) CONSTRAINT MEMBER_NN_PWD NOT NULL,
    MEMBER_NAME  VARCHAR2(20) CONSTRAINT MEMBER_NN_NAME NOT NULL,
    GENDER CHAR(1) CONSTRAINT MEMBER_CK_GEN CHECK(GENDER IN ('M', 'F')),
    ADDRESS VARCHAR2(70),
    PHONE VARCHAR2(13),
    STATUS CHAR(1) DEFAULT 'N' CONSTRAINT MEMBER_CK_STA CHECK(STATUS IN ('Y', 'N')),
    ENROLL_DATE DATE DEFAULT SYSDATE CONSTRAINT MEMBER_NN_EN NOT NULL
);

-- 4.
CREATE TABLE TB_RENT (
    RENT_NO NUMBER CONSTRAINT RENT_PK PRIMARY KEY,
    RENT_MEM_NO NUMBER CONSTRAINT RENT_FK_MEM REFERENCES TB_MEMBER ON DELETE SET NULL,
    RENT_BOOK_NO NUMBER CONSTRAINT RENT_FK_BOOK REFERENCES TB_BOOK ON DELETE SET NULL,
    RENT_DATE DATE DEFAULT SYSDATE
);

'컴퓨터 > SQL' 카테고리의 다른 글

[SQL] DML(update,insert,delete)  (0) 2024.07.24
[SQL] subQuery  (2) 2024.07.05
[SQL/ORACLE] JOIN  (0) 2024.07.04
[SQL/ORACLE] group_having_집합 연산자  (0) 2024.06.27
[SQL/ORACLE] FUNCTION 종합문제  (0) 2024.06.23

[SQL] subQuery

컴퓨터/SQL
/*
    * 서브쿼리
       : 하나의 sql문안에 포함된 또다른 select문
       - 메인 sql문을 위해 보조 역할을 하는 쿼리문
*/

-- 김정보와 같은 부서의 사원 조회
-- 1. 김정보 부서 먼저 조회
SELECT DEPT_CODE
 FROM EMPLOYEE
WHERE EMP_NAME = '김정보';

-- 2. 부서가 D9인 사원 조회
SELECT EMP_NAME
 FROM EMPLOYEE
WHERE DEPT_CODE = 'D9'; 

-- 위의 단계를 하나의 쿼리문으로 합침
SELECT EMP_NAME
 FROM EMPLOYEE
WHERE DEPT_CODE = (SELECT DEPT_CODE
                                    FROM EMPLOYEE
                                  WHERE EMP_NAME = '김정보'); 

-- 전 직원의 평균급여보다 더 많은 급여를 받는 사원의 사번, 사원명, 직급코드 ,급여 조회
-- 1. 전 직원의 평균급여
SELECT CEIL(AVG(SALARY))
 FROM EMPLOYEE;
 
-- 2. 평균급여보다 많이 받는 사원들 조회
SELECT EMP_ID, EMP_NAME, JOB_CODE, SALARY
 FROM EMPLOYEE
WHERE SALARY > (SELECT CEIL(AVG(SALARY))
                             FROM EMPLOYEE); 

 -------------------------------------------------------------------------------------------------------------------
 /*
    * 서브쿼리의 구분
       서브쿼리를 수행한 결과값이 몇 행 몇 열이냐 따라 구분
       
       - 단일행 서브쿼리 : 서브쿼리의 조회 결과값이 오로지 1개 일때(1행1열)
       - 다중행 서브쿼리 : 서브쿼리의 조회 결과값이 여러행 일 때(여러행 1열)
       - 다중열 서브쿼리 : 서브쿼리의 조회 결과값이 여러열 일 때(1행 여러열)
       - 다중행 다중열 서브쿼리 : 서브쿼리의 조회 결과값이 여러행 여러열 일 때(여러행 여러열)
       
       >> 서브쿼리의 종류가 뭐냐에 따라 서브쿼리 앞에 붙는 연산자가 달라짐
 */

 -------------------------------------------------------------------------------------------------------------------
/*
    1. 단일행 서브쿼리 : 서브쿼리의 조회 결과값이 오로지 1개 일때(1행1열)
        일반 비교연산자 사용 가능
        =, !=, >, < .....
*/
--  1) 전 직원의 평균급여보다 더 적게 받는 직원의 사원명, 직급코드, 급여 조회
SELECT EMP_NAME, JOB_CODE, SALARY
 FROM EMPLOYEE
WHERE SALARY < (SELECT AVG(SALARY)
                             FROM EMPLOYEE);

-- 2) 최저급여를 받는 사원의 사번, 사원명, 급여, 입사일 조회
SELECT EMP_ID, EMP_NAME, SALARY, HIRE_DATE
 FROM EMPLOYEE
WHERE SALARY = (SELECT MIN(SALARY)
                             FROM EMPLOYEE);

-- 3) 박정보 사원의 급여보다 더 많이받는 사원들의 사번, 사원명, 부서코드, 급여 조회
SELECT EMP_ID, EMP_NAME, DEPT_CODE, SALARY
 FROM EMPLOYEE
WHERE SALARY > (SELECT SALARY
                             FROM EMPLOYEE
                             WHERE EMP_NAME = '박정보');

-- JOIN
-- 4) 박정보 사원의 급여보다 더 많이받는 사원들의 사번, 사원명, 부서명, 급여 조회
--    >> 오라클전용 구문
SELECT EMP_ID, EMP_NAME, DEPT_TITLE, SALARY
 FROM EMPLOYEE, DEPARTMENT
WHERE DEPT_CODE = DEPT_ID    
    AND SALARY > (SELECT SALARY
                             FROM EMPLOYEE
                             WHERE EMP_NAME = '박정보');  -- 순서 상관 없음

--    >> ANSI 구문
SELECT EMP_ID, EMP_NAME, DEPT_TITLE, SALARY
 FROM EMPLOYEE
   JOIN DEPARTMENT ON (DEPT_CODE = DEPT_ID)
WHERE SALARY > (SELECT SALARY
                             FROM EMPLOYEE
                             WHERE EMP_NAME = '박정보');
                             
-- 5) 왕정보 사원과 부서코드가 같은 사원들의 사번, 사원명, 전화번호, 입사일, 부서명 조회(단, 왕정보는 제외)
--    >> 오라클전용 구문
SELECT EMP_ID, EMP_NAME, PHONE, HIRE_DATE, DEPT_TITLE
 FROM EMPLOYEE, DEPARTMENT
WHERE DEPT_CODE = DEPT_ID
   AND DEPT_CODE = (SELECT DEPT_CODE
                                  FROM EMPLOYEE
                                 WHERE EMP_NAME = '왕정보')
   AND EMP_NAME != '왕정보';

--    >> ANSI 구문
SELECT EMP_ID, EMP_NAME, PHONE, HIRE_DATE, DEPT_TITLE
 FROM EMPLOYEE
  JOIN DEPARTMENT ON (DEPT_CODE = DEPT_ID)
WHERE DEPT_CODE = (SELECT DEPT_CODE
                                  FROM EMPLOYEE
                                 WHERE EMP_NAME = '왕정보')
   AND EMP_NAME != '왕정보';
   
   
-- GROUP BY
-- 6) 부서별 급여합이 가장 큰 부서의 부서코드, 급여합 조회
--      6.1 부서별 급여합 중 가장 큰 값 하나만 조회
SELECT MAX(SUM(SALARY))
 FROM EMPLOYEE
GROUP BY DEPT_CODE;

--     6.2 부서별 급여합이 17700000인 부서를 조회
SELECT DEPT_CODE, SUM(SALARY)
 FROM EMPLOYEE
GROUP BY DEPT_CODE
HAVING SUM(SALARY) = 17700000;

-- 위의 쿼리문을 하나로
SELECT DEPT_CODE, SUM(SALARY)
 FROM EMPLOYEE
GROUP BY DEPT_CODE
HAVING SUM(SALARY) = (SELECT MAX(SUM(SALARY))
                                       FROM EMPLOYEE
                                      GROUP BY DEPT_CODE);

 -------------------------------------------------------------------------------------------------------------------
/*
    2. 다중행 서브쿼리
       : 서브쿼리의 조회 결과값이 여러행 일 때(여러행 1열)
       - IN 서브쿼리 : 여러개의 결과값 중에서 한개라도 일치하는 값이 있다면
          > ANY 서브쿼리 : 여러개의 결과값 중에서 "한개라도" 클 경우
                                    (여러개의 결과값 중에서 가장 작은값 보다 클 경우)
          < ANY 서브쿼리 : 여러개의 결과값 중에서 "한개라도" 작은 경우
                                     (여러개의 결과값 중에서 가장 큰값 보다 작을 경우)  
                                     
        비교대상 > ANY (값1, 값2, 값3)
        비교대상 > 값1 OR 비교대상 > 값2 OR 비교대상 > 값3
*/
-- 1) 조정연 또는 전지연 사원과 같은 직급인 사원들의 사번, 사원명, 직급코드, 급여 조회
--    1.1 조정연 또는 전지연의 직급
SELECT JOB_CODE
 FROM EMPLOYEE
WHERE EMP_NAME IN ('조정연', '전지연');

--    1.2 J3, J7 직급인 사원들 조회
SELECT EMP_ID, EMP_NAME, JOB_CODE, SALARY
 FROM EMPLOYEE
WHERE JOB_CODE IN ('J3', 'J7'); 

-- 위의 쿼리문을 한줄로
SELECT EMP_ID, EMP_NAME, JOB_CODE, SALARY
 FROM EMPLOYEE
WHERE JOB_CODE IN  (SELECT JOB_CODE
                                   FROM EMPLOYEE
                                 WHERE EMP_NAME IN ('조정연', '전지연'));

--  사원 -> 대리 -> 과장
-- 2) 대리 직급임에도 불구하고 과장직급 급여들 중 최소 급여보다 많이 받는 직원의 사번, 사원명, 직급, 급여 조회
--     2.1. 과장들의 급여
SELECT SALARY
 FROM EMPLOYEE
  JOIN JOB USING (JOB_CODE)
WHERE JOB_NAME = '과장';     -- 2200, 2500, 3760

--     2.2. 직급이 대리이면서 급여가 위의 목록의 값 보다 하나라도 큰 사원
SELECT EMP_ID, EMP_NAME, JOB_NAME, SALARY
 FROM EMPLOYEE
 JOIN JOB USING (JOB_CODE)
WHERE JOB_NAME = '대리' 
    AND SALARY > ANY(2200000, 2500000, 3760000);

-- 위의 쿼리문을 하나로
SELECT EMP_ID, EMP_NAME, JOB_NAME, SALARY
 FROM EMPLOYEE
 JOIN JOB USING (JOB_CODE)
WHERE JOB_NAME = '대리' 
    AND SALARY > ANY (SELECT SALARY
                                    FROM EMPLOYEE
                                      JOIN JOB USING (JOB_CODE)
                                    WHERE JOB_NAME = '과장');

-- 단일행 쿼리로도 가능
SELECT EMP_ID, EMP_NAME, JOB_NAME, SALARY
 FROM EMPLOYEE
 JOIN JOB USING (JOB_CODE)
WHERE JOB_NAME = '대리' 
    AND SALARY > (SELECT MIN(SALARY)
                              FROM EMPLOYEE
                                JOIN JOB USING (JOB_CODE)
                            WHERE JOB_NAME = '과장');

 -------------------------------------------------------------------------------------------------------------------
/*
    3. 다중열 서브쿼리
       : 결과값이 한행이고 컬럼수가 여러개 일 때
*/
-- 1) 장정보 사원과 같은 부서코드, 같은 직급코드에 해당하는 사원들의 사원명, 부서코드, 직급코드, 입사일 조회
--     1.1  장정보 사원의 부서코드, 직급코드
SELECT DEPT_CODE, JOB_CODE
FROM EMPLOYEE
WHERE EMP_NAME = '장정보';

SELECT EMP_NAME, DEPT_CODE, JOB_CODE, HIRE_DATE
 FROM EMPLOYEE
WHERE DEPT_CODE = (SELECT DEPT_CODE
                                  FROM EMPLOYEE
                                 WHERE EMP_NAME = '장정보')
   AND JOB_CODE = (SELECT JOB_CODE
                                  FROM EMPLOYEE
                                 WHERE EMP_NAME = '장정보');

-- 다중열 서브쿼리
SELECT EMP_NAME, DEPT_CODE, JOB_CODE, HIRE_DATE
 FROM EMPLOYEE
WHERE (DEPT_CODE, JOB_CODE) = (SELECT DEPT_CODE, JOB_CODE
                                                     FROM EMPLOYEE
                                                    WHERE EMP_NAME = '장정보');
                                                    
-- 지정보 사원과 같은 직급코드, 같은 사수를 가지고 있는 사원들의 사번, 사원명, 직급코드, 사수번호 조회                                                   
SELECT EMP_ID, EMP_NAME, DEPT_CODE, MANAGER_ID
 FROM EMPLOYEE
WHERE(DEPT_CODE, MANAGER_ID) = (SELECT DEPT_CODE, MANAGER_ID
                                                         FROM EMPLOYEE
                                                        WHERE EMP_NAME='지정보');

 -------------------------------------------------------------------------------------------------------------------
/*
    4. 다중행 다중열 서브쿼리
       : 결과값이 여러행이고 컬럼수가 여러개 일 때
*/

-- 1) 각 직급별 최소급여 금액을 받는 사원의 사번, 사원명, 직급코드, 급여 조회
--    1.1 각 직급별 최소금여
SELECT JOB_CODE, MIN(SALARY)
 FROM EMPLOYEE
GROUP BY JOB_CODE; 

SELECT EMP_ID, EMP_NAME, JOB_CODE, SALARY
 FROM EMPLOYEE
GROUP BY JOB_CODE = 'J1' AND SALARY = 8000000 
          OR JOB_CODE = 'J2' AND SALARY = 3700000
          ....;

SELECT EMP_ID, EMP_NAME, JOB_CODE, SALARY
 FROM EMPLOYEE
WHERE (JOB_CODE, SALARY) = ('J1', 8000000)
      OR (JOB_CODE, SALARY) = ('J2', 3700000)
       ....;

-- 서브쿼리
SELECT EMP_ID, EMP_NAME, JOB_CODE, SALARY
 FROM EMPLOYEE
WHERE (JOB_CODE, SALARY) IN (SELECT JOB_CODE, MIN(SALARY)
                                                FROM EMPLOYEE
                                                GROUP BY JOB_CODE);
                                                
-- 2) 각 부서별 최고급여를 받는 사원들의 사번, 사원명, 부서코드, 급여조회
SELECT EMP_ID, EMP_NAME, DEPT_CODE, SALARY
 FROM EMPLOYEE
WHERE(DEPT_CODE, SALARY) IN (SELECT DEPT_CODE, MAX(SALARY) 
                                                FROM EMPLOYEE
                                                GROUP BY DEPT_CODE);
                                                
--=================================================================
-- 인라인 뷰

/*
    인라인 뷰(INLINE VIEW)
    : 서브쿼리를 수행한 결과를 마치 테이블처럼 사용
      FROM절에 서브쿼리  작성
      
    - 주로 사용하는 예 : TOP-N분석(상위 몇위만 가져오기)
*/
-- 1) 사원들의 사번, 이름, 보너스를포함한연봉, 부서코드 조회(연봉에 NULL이 안나오게)
--      단, 보너스포함 연봉이 3000만원이상인 사원들만 조회
                                                
SELECT EMP_ID, EMP_NAME, (SALARY * NVL(1+BONUS,1))*12 연봉, DEPT_CODE
FROM EMPLOYEE
-- WHERE 연봉 >= 30000000    오류 순서 FROM -> WHERE
WHERE (SALARY * NVL(1+BONUS,1))*12 >= 30000000;
                                                
-- 별칭을 사용하려면 INLINE VIEW 사용
SELECT *
FROM (SELECT EMP_ID, EMP_NAME, (SALARY * NVL(1+BONUS,1))*12 연봉, DEPT_CODE
            FROM EMPLOYEE)
WHERE 연봉 >= 30000000;

SELECT EMP_ID, EMP_NAME, 연봉
FROM (SELECT EMP_ID, EMP_NAME, (SALARY * NVL(1+BONUS,1))*12 연봉, DEPT_CODE
            FROM EMPLOYEE)
WHERE 연봉 >= 30000000;

/*
SELECT EMP_ID, EMP_NAME, 연봉, HIRE_DATE
FROM (SELECT EMP_ID, EMP_NAME, (SALARY * NVL(1+BONUS,1))*12 연봉, DEPT_CODE
            FROM EMPLOYEE)
WHERE 연봉 >= 30000000;  --  FROM절 뒤의 테이블에 HIRE_DATE라는 컬럼은 없다
*/

-- TOP-N분석
-- 전 직원중 급여가 가장 높은 상위 5명만 조회
-- * ROWNUM : 오라클에서 제공해주는 컬럼, 조회된 순서대로 1부터 순번을 부여

SELECT ROWNUM, EMP_ID, EMP_NAME, SALARY
FROM EMPLOYEE;

SELECT ROWNUM, EMP_ID, EMP_NAME, SALARY
FROM EMPLOYEE
ORDER BY SALARY DESC;  -- FROM -> SELECT -> ORDER

SELECT ROWNUM, EMP_ID, EMP_NAME, SALARY
FROM EMPLOYEE
WHERE ROWNUM <= 10
ORDER BY SALARY DESC;


-- ORDER BY를 한후, ROWNUM을 붙여줘야 한다
SELECT *
 FROM (SELECT EMP_ID, EMP_NAME, SALARY
            FROM EMPLOYEE
            ORDER BY SALARY DESC);

SELECT ROWNUM, EMP_ID, EMP_NAME, SALARY
 FROM (SELECT EMP_ID, EMP_NAME, SALARY
            FROM EMPLOYEE
            ORDER BY SALARY DESC)
WHERE ROWNUM <= 5;

SELECT ROWNUM, *
 FROM (SELECT EMP_ID, EMP_NAME, SALARY
            FROM EMPLOYEE
            ORDER BY SALARY DESC)
WHERE ROWNUM <= 5;  -- 오류

-- 테이블에 별칭을 부여하면 가능
SELECT ROWNUM, E.*
 FROM (SELECT EMP_ID, EMP_NAME, SALARY
            FROM EMPLOYEE
            ORDER BY SALARY DESC) E
WHERE ROWNUM <= 5;

-- 2) 가장 최근에 입사한 사원 5명의 사원명, 급여, 입사일 조회
--     2.1 입사일 기준 내림차순 정렬한 INLINE VIEW
SELECT EMP_NAME, SALARY, HIRE_DATE
 FROM (SELECT *
            FROM EMPLOYEE
            ORDER BY HIRE_DATE DESC);

--    2.2          
SELECT ROWNUM, EMP_NAME, SALARY, HIRE_DATE
 FROM (SELECT *
            FROM EMPLOYEE
            ORDER BY HIRE_DATE DESC)
WHERE ROWNUM <= 5;            
            
            
-- 3) 각 부서별 평균급여가 높은 3개 부서의 부서코드, 평균급여 조회
SELECT *
 FROM (SELECT DEPT_CODE, CEIL(AVG(SALARY)) 평균급여
            FROM EMPLOYEE
            GROUP BY DEPT_CODE
            ORDER BY 평균급여 DESC)
WHERE ROWNUM <= 3;

 -------------------------------------------------------------------------------------------------------------------
/*
    * WITH
      : 서브쿼리에 이름을 붙여주고 인라인 뷰로 사용시 서브쿼리의 이름으로 FROM절에 기술
      
      - 장점
        같은 서브쿼리가 여러번 사용될 경우 중복 작성을 피할 수 있고, 실행속도가 빠르다
*/
WITH TOPN_SAL AS (SELECT DEPT_CODE, CEIL(AVG(SALARY)) 평균급여
                                FROM EMPLOYEE
                                GROUP BY DEPT_CODE
                                ORDER BY 평균급여 DESC)
                            
SELECT *
 FROM TOPN_SAL
WHERE ROWNUM <= 3; 

-- 실행시 세미콜론이 붙으면(끝) 그 테이블 사용할 수 없음
-- UNION, MINUS 나 이런식으로 FROM절에 2번 쓸 때 가능
SELECT *
 FROM TOPN_SAL
WHERE ROWNUM <= 5; 

--=================================================================
/*
    * 순위 매기는 함수(WINDOW FUNCTION)
      RANK() OVER(정렬기준) | DENSE_RANK() OVER(정렬기준)
      - RANK() OVER(정렬기준) : 동일한 순위 이후의 등수를 동일한 인원 수 만큼 건너뛰고 순위 계산
                                            ex) 공동1위가 2명이면 그 다음 순위는 3위
      - DENSE_RANK() OVER(정렬기준)  :  동일한 순위 이후 그 다음 등수를 무조건 1씩 증가시킴
                                            ex) 공동1위가 2명이면 그 다음 순위는 2위      
      
*/
-- 급여가 높은 순서대로 순위 매겨서 사원명, 급여, 순위 조회
SELECT EMP_NAME, SALARY, RANK() OVER(ORDER BY SALARY DESC) 순위
 FROM EMPLOYEE;

SELECT EMP_NAME, SALARY, DENSE_RANK() OVER(ORDER BY SALARY DESC) 순위
 FROM EMPLOYEE;
 
-- 급여가 상위 5위인 사람의 사원명, 급여, 순위 조회
SELECT EMP_NAME, SALARY, RANK() OVER(ORDER BY SALARY DESC) 순위
 FROM EMPLOYEE
WHERE RANK() OVER(ORDER BY SALARY DESC) <= 5;  -- 오류
-- 윈도우 함수는 WHERE절에서는 사용 못함. SELECT절에서만 사용가능

-->> 인라인 뷰를 사용하면 됨
SELECT *
 FROM (SELECT EMP_NAME, SALARY, RANK() OVER(ORDER BY SALARY DESC) 순위
            FROM EMPLOYEE)
WHERE 순위 <= 5;

-->> WITH와 같이 사용
WITH TOPN_SALARY AS(SELECT EMP_NAME, SALARY, RANK() OVER(ORDER BY SALARY DESC) 순위
                                    FROM EMPLOYEE)
                                    
SELECT *
 FROM TOPN_SALARY
WHERE 순위 <= 5;

--------------------------------------------------------- 연습문제 --------------------------------------------------------
-- 1. 2020년 12월 25일의 요일 조회
SELECT TO_CHAR(TO_DATE('20201225','YYYYMMDD'), 'DAY')
 FROM DUAL;
 
-- 2. 70년대 생(1970~1979) 중 여자이면서 전씨인 사원의 사원명, 주민번호, 부서명, 직급명 조회
-- ANSI 구문
SELECT EMP_NAME, EMP_NO, DEPT_ID, JOB_NAME
 FROM EMPLOYEE
   JOIN DEPARTMENT ON (DEPT_CODE = DEPT_ID)
   JOIN JOB USING (JOB_CODE)
WHERE SUBSTR(EMP_NO, 1, 2) >= 70 AND SUBSTR(EMP_NO, 1, 2) <= 79
    AND SUBSTR(EMP_NO, 8, 1) = 2
    AND EMP_NAME LIKE '전%';

-- 오라클전용 구문
SELECT EMP_NAME, EMP_NO, DEPT_ID, JOB_NAME
 FROM EMPLOYEE E,  DEPARTMENT D,  JOB J
WHERE DEPT_CODE = DEPT_ID
   AND E.JOB_CODE = J.JOB_CODE
   AND SUBSTR(EMP_NO, 1, 2) >= 70 AND SUBSTR(EMP_NO, 1, 2) <= 79
   AND SUBSTR(EMP_NO, 8, 1) = 2
   AND EMP_NAME LIKE '전%';
   
-- 3. 나이가 가장 막내의 사번, 사원명, 나이, 부서명, 직급명 조회
SELECT EMP_ID, EMP_NAME, 
     EXTRACT(YEAR FROM SYSDATE) - EXTRACT(YEAR FROM (TO_DATE(SUBSTR(EMP_NO,1,2), 'RR'))) AS 나이,
           DEPT_TITLE, 
           JOB_NAME
 FROM EMPLOYEE
   JOIN DEPARTMENT ON (DEPT_CODE = DEPT_ID)
   JOIN JOB USING (JOB_CODE)
WHERE EXTRACT(YEAR FROM SYSDATE) - EXTRACT(YEAR FROM (TO_DATE(SUBSTR(EMP_NO,1,2), 'RR'))) =
           (SELECT MIN(EXTRACT(YEAR FROM SYSDATE) - EXTRACT(YEAR FROM (TO_DATE(SUBSTR(EMP_NO,1,2), 'RR')))) 
             FROM EMPLOYEE);

-- INLINE VIEW
SELECT *
 FROM (SELECT EMP_ID, EMP_NAME, 
            EXTRACT(YEAR FROM SYSDATE) - EXTRACT(YEAR FROM (TO_DATE(SUBSTR(EMP_NO,1,2), 'RR'))) AS 나이,
            DEPT_TITLE, 
            JOB_NAME
 FROM EMPLOYEE
   JOIN DEPARTMENT ON (DEPT_CODE = DEPT_ID)
   JOIN JOB USING (JOB_CODE)) E
WHERE E.나이 =  (SELECT MIN(EXTRACT(YEAR FROM SYSDATE) - EXTRACT(YEAR FROM (TO_DATE(SUBSTR(EMP_NO,1,2), 'RR')))) 
                           FROM EMPLOYEE);
                           
-- WITH 사용
WITH E AS (SELECT EMP_ID, EMP_NAME, 
                 EXTRACT(YEAR FROM SYSDATE) - EXTRACT(YEAR FROM (TO_DATE(SUBSTR(EMP_NO,1,2), 'RR'))) AS 나이,
                DEPT_TITLE, 
                JOB_NAME
        FROM EMPLOYEE
          JOIN DEPARTMENT ON (DEPT_CODE = DEPT_ID)
          JOIN JOB USING (JOB_CODE))
          
SELECT *
 FROM E
WHERE 나이 = (SELECT MIN(나이) FROM E);
                         
-- 4. 이름에 ‘하’가 들어가는 사원의 사번, 사원명, 직급명 조회
SELECT EMP_ID, EMP_NAME, JOB_NAME
 FROM EMPLOYEE
   JOIN JOB USING (JOB_CODE)
WHERE EMP_NAME LIKE '%하%';   

-- 5. 부서 코드가 D5이거나 D6인 사원의 사원명, 직급명, 부서코드, 부서명 조회
SELECT EMP_NAME, JOB_NAME, DEPT_CODE, DEPT_TITLE
 FROM EMPLOYEE
   JOIN DEPARTMENT ON (DEPT_CODE=DEPT_ID)
   JOIN JOB USING(JOB_CODE)
WHERE DEPT_CODE IN ('D5','D6');   

-- 6. 보너스를 받는 사원의 사원명, 보너스, 부서명, 지역명 조회
SELECT EMP_NAME, BONUS, DEPT_TITLE, LOCAL_NAME
 FROM EMPLOYEE
   JOIN DEPARTMENT ON (DEPT_CODE = DEPT_ID)
   JOIN LOCATION ON (LOCATION_ID = LOCAL_CODE)
WHERE BONUS IS NOT NULL;
   
-- 7. 모든 사원의 사원명, 직급명, 부서명, 지역명 조회
SELECT EMP_NAME, JOB_NAME, DEPT_TITLE, LOCAL_NAME
 FROM EMPLOYEE
   JOIN DEPARTMENT ON (DEPT_CODE = DEPT_ID)
   JOIN JOB USING(JOB_CODE)
   JOIN LOCATION ON (LOCATION_ID = LOCAL_CODE);
   
-- 8. 한국이나 일본에서 근무 중인 사원의 사원명, 부서명, 지역명, 국가명 조회 
SELECT EMP_NAME, DEPT_TITLE, LOCAL_NAME, NATIONAL_NAME
 FROM EMPLOYEE
   JOIN DEPARTMENT ON (DEPT_CODE = DEPT_ID)
   JOIN LOCATION ON (LOCATION_ID = LOCAL_CODE)
   JOIN NATIONAL USING(NATIONAL_CODE)
WHERE NATIONAL_NAME IN ('한국','일본');   

-- 9. 하정연 사원과 같은 부서에서 일하는 사원의 사원명, 부서코드 조회
SELECT EMP_NAME, DEPT_CODE
 FROM EMPLOYEE
WHERE DEPT_CODE = (SELECT DEPT_CODE
                                    FROM EMPLOYEE
                                  WHERE EMP_NAME = '하정연');

-- 10. 보너스가 없고 직급 코드가 J4이거나 J7인 사원의 사원명, 직급명, 급여 조회
SELECT EMP_NAME, JOB_NAME, SALARY
 FROM EMPLOYEE
  JOIN JOB USING(JOB_CODE)
WHERE BONUS IS NULL
    AND JOB_CODE IN ('J4', 'J7');
  
-- 11. 퇴사 하지 않은 사람과 퇴사한 사람의 수 조회
SELECT COUNT(*)
 FROM EMPLOYEE
GROUP BY ENT_YN;

-- 12. 보너스 포함한 연봉이 높은 5명의 사번, 사원명, 부서명, 직급명, 입사일, 순위 조회
SELECT *
 FROM (SELECT EMP_ID, EMP_NAME, DEPT_TITLE, JOB_NAME, HIRE_DATE, 
                       RANK() OVER(ORDER BY(SALARY*NVL(1+BONUS,1)*12) DESC) 순위
            FROM EMPLOYEE
              JOIN DEPARTMENT ON (DEPT_CODE = DEPT_ID)
              JOIN JOB USING(JOB_CODE))
WHERE 순위 <= 5;

-- 13. 부서 별 급여 합계가 전체 급여 총 합의 20%보다 많은 부서의 부서명, 부서별 급여 합계 조회
--	13-1. JOIN과 HAVING 사용 
SELECT DEPT_TITLE, SUM(SALARY)
 FROM EMPLOYEE
   JOIN DEPARTMENT ON (DEPT_CODE = DEPT_ID)
GROUP BY DEPT_TITLE
HAVING SUM(SALARY) > (SELECT SUM(SALARY)*0.2
                                       FROM EMPLOYEE);
   
--	13-2. 인라인 뷰 사용  
SELECT *
 FROM (SELECT DEPT_TITLE, SUM(SALARY) "부서별 합"
            FROM EMPLOYEE
             JOIN DEPARTMENT ON (DEPT_CODE = DEPT_ID)
            GROUP BY DEPT_TITLE)
WHERE "부서별 합" > (SELECT SUM(SALARY)*0.2
                                       FROM EMPLOYEE);   
                                       
--	13-3. WITH 사용
WITH TOTAL_SAL AS (SELECT DEPT_TITLE, SUM(SALARY) "부서별 합"
            FROM EMPLOYEE
             JOIN DEPARTMENT ON (DEPT_CODE = DEPT_ID)
            GROUP BY DEPT_TITLE)
            
SELECT *
 FROM TOTAL_SAL
WHERE "부서별 합" > (SELECT SUM(SALARY)*0.2
                                       FROM EMPLOYEE);
                                       
-- 14. 부서명별 급여 합계 조회(NULL도 조회되도록)
-- ANSI 구문
SELECT DEPT_TITLE, SUM(SALARY)
FROM EMPLOYEE
LEFT JOIN DEPARTMENT ON (DEPT_CODE = DEPT_ID)
GROUP BY DEPT_TITLE;

-- 오라클전용 구문
SELECT DEPT_TITLE, SUM(SALARY)
FROM EMPLOYEE, DEPARTMENT
WHERE DEPT_CODE = DEPT_ID(+)
GROUP BY DEPT_TITLE;

-- 15. WITH를 이용하여 급여합과 급여평균 조회
WITH SUM_SAL AS (SELECT SUM(SALARY) FROM EMPLOYEE),
         AVG_SAL AS (SELECT CEIL(AVG(SALARY)) FROM EMPLOYEE)

/*
SELECT * FROM SUM_SAL
UNION
SELECT * FROM AVG_SAL;
*/
SELECT *
 FROM SUM_SAL, AVG_SAL;

'컴퓨터 > SQL' 카테고리의 다른 글

[SQL] DML(update,insert,delete)  (0) 2024.07.24
[SQL] DDL(CREATE)  (0) 2024.07.17
[SQL/ORACLE] JOIN  (0) 2024.07.04
[SQL/ORACLE] group_having_집합 연산자  (0) 2024.06.27
[SQL/ORACLE] FUNCTION 종합문제  (0) 2024.06.23

[SQL/ORACLE] group_having_집합 연산자

컴퓨터/SQL

* GROUP BY절
      그룹기준을 제시할 수 있는 구문(해당 그룹별로 여러 그룹으로 묶을 수 있음)
      여러개의 값들을 하나의 그룹으로 묶어서 처리할 목적으로 사용

SELECT SUM(SALARY)
 FROM EMPLOYEE;  -- 전체 사원을 하나의 그룹으로 묶어서 총합을 구한 결과
 
 -- 각 부서별 급여의 합계
 SELECT DEPT_CODE, SUM(SALARY)
  FROM EMPLOYEE
GROUP BY DEPT_CODE;

-- 각 부서별 사원의 수
SELECT DEPT_CODE, COUNT(*)
  FROM EMPLOYEE
GROUP BY DEPT_CODE; 

SELECT DEPT_CODE, COUNT(*), SUM(SALARY)
  FROM EMPLOYEE
GROUP BY DEPT_CODE
ORDER BY 1;

-- 각 직급별 직원의 수와 급여의 합계 조회
SELECT JOB_CODE, COUNT(*), SUM(SALARY)
 FROM EMPLOYEE
GROUP BY JOB_CODE
ORDER BY JOB_CODE;

-- 각 직급별 직원의 수, 보너스를 받는 사원수, 급여 합, 평균급여, 최저급여, 최고급여
SELECT JOB_CODE, COUNT(BONUS), SUM(SALARY), CEIL(AVG(SALARY)), MIN(SALARY), MAX(SALARY)
  FROM EMPLOYEE
GROUP BY JOB_CODE
ORDER BY 1;

-- GROUP BY 함수식 사용가능
SELECT DECODE(SUBSTR(EMP_NO, 8, 1),'1','남','2','여') 성별, COUNT(*) 인원수
 FROM EMPLOYEE
GROUP BY SUBSTR(EMP_NO, 8, 1);

-- GROUP BY절에 여러 컬럼 기술
SELECT DEPT_CODE, JOB_CODE, COUNT(*), SUM(SALARY)
 FROM EMPLOYEE
GROUP BY DEPT_CODE, JOB_CODE
ORDER BY DEPT_CODE;

 

* HAVING 절 : 그룹에 대한 조건을 제시할 때 사용(주로 그룹함수식을 가지고 조건을 제시할 때 사용)

-- 각 부서별 평균 급여 조회
SELECT DEPT_CODE, AVG(SALARY)
  FROM EMPLOYEE
GROUP BY DEPT_CODE;

-- 각 부서별 평균 급여가 3백만원 이상인 부서들만 조회
/*
SELECT DEPT_CODE, AVG(SALARY)
  FROM EMPLOYEE
 WHERE AVG(SALARY) >= 3000000
 GROUP BY DEPT_CODE;  -- 오류 : 그룹함수에서 조건은 WHERE절에서 하면 안됨
*/
SELECT DEPT_CODE, CEIL(AVG(SALARY))
  FROM EMPLOYEE
GROUP BY DEPT_CODE
HAVING AVG(SALARY) >= 3000000;

--- 문제
-- 1. 직급별 총 급여합(단, 직급별 급여합이 1000만원 이상인 직급만 조회). 직급코드, 급여합
SELECT JOB_CODE, SUM(SALARY)
 FROM EMPLOYEE
GROUP BY JOB_CODE
HAVING SUM(SALARY) >= 10000000;

-- 2. 부서별 보너스를 받는 사원이 없는 부서만 부서코드를 조회
SELECT DEPT_CODE, COUNT(*)
FROM EMPLOYEE
GROUP BY DEPT_CODE
HAVING COUNT(BONUS) = 0;

 

/*
    SELECT문 실행 순서
    1. FROM
    2. ON
    3. JOIN
    4. WHERE
    5. GROUP BY
    6. HAVING
    7. SELECT
    8. DISTINCT
    9. ORDER BY
*/

 

* 집계 함수 : 그룹별 산출된 결과 값에 중간집계를 계산해주는 함수
    
    ROLLUP / CUBE => GROUP BY절에 기술
     - ROLLUP(컬럼1, 컬럼2) : 컬럼1을 가지고 다시 중계집계를 내는 함수
     - CUBE(컬럼1, 컬럼2) : 컬럼1을 가지고 중계집계를 내고, 컬럼2를 가지고도 중간집계를 냄

-- 각 직급별 급여합
SELECT JOB_CODE, SUM(SALARY)
 FROM EMPLOYEE
GROUP BY JOB_CODE
ORDER BY JOB_CODE;

SELECT JOB_CODE, SUM(SALARY)
 FROM EMPLOYEE
GROUP BY CUBE(JOB_CODE)
ORDER BY JOB_CODE;  -- 컬럼이 1개일 때 써도 되지만 의미가 없다

-- DEPT_CODE, JOB_CODE 쌍으로 집계를 낼때
SELECT DEPT_CODE, JOB_CODE, SUM(SALARY)
 FROM EMPLOYEE
GROUP BY DEPT_CODE, JOB_CODE
ORDER BY DEPT_CODE;

-- ROLLUP
SELECT DEPT_CODE, JOB_CODE, SUM(SALARY)
 FROM EMPLOYEE
GROUP BY ROLLUP(DEPT_CODE, JOB_CODE)
ORDER BY DEPT_CODE;

-- CUBE
SELECT DEPT_CODE, JOB_CODE, SUM(SALARY)
 FROM EMPLOYEE
GROUP BY CUBE(DEPT_CODE, JOB_CODE)
ORDER BY DEPT_CODE;

 

--=========================================================================
--                                                          집합 함수
--========================================================================= 


    * 집합 연산자 = SET OPERATION
      여러개의 쿼리문을 가지고 하나의 쿼리문으로 만드는 연산자
      
      - UNION : OR | 합집합(두 쿼리문을 수행한 결과값을 더한 후 중복되는 값은 한번만 조회)
      - INTERSECT : AND | 교집합(두 쿼리문을 수행한 결과값중 중복된 결과값 조회)
      - UNION ALL : 합집합 + 교집합(중복된 값은 2번 표현될 수 있다)
      - MINUS : 차집합(선행결과값에서 후행결과값을 뺀 나머지)

 

---------------------------------------------------- 1. UNION -----------------------------------------------------
-- 부서코드가 D5인사원 또는 급여가 300만원 초과인 사원들 조회
SELECT EMP_NAME, DEPT_CODE, SALARY
 FROM EMPLOYEE
WHERE DEPT_CODE = 'D5'
UNION
SELECT EMP_NAME, DEPT_CODE, SALARY
 FROM EMPLOYEE
WHERE SALARY > 3000000;

-- OR
SELECT EMP_NAME, DEPT_CODE, SALARY
 FROM EMPLOYEE
WHERE DEPT_CODE = 'D5' OR SALARY > 3000000;

---------------------------------------------------- 2. INTERSECT -----------------------------------------------------
-- 부서코드가 D5이면서 급여가 300만원 초과인 사원들 조회
SELECT EMP_NAME, DEPT_CODE, SALARY
 FROM EMPLOYEE
WHERE DEPT_CODE = 'D5'
INTERSECT
SELECT EMP_NAME, DEPT_CODE, SALARY
 FROM EMPLOYEE
WHERE SALARY > 3000000;
-- 각 쿼리문의 SELECT절에 작성되는 컬럼은 동일해야 됨

-- AND
SELECT EMP_NAME, DEPT_CODE, SALARY
 FROM EMPLOYEE
WHERE DEPT_CODE = 'D5' AND SALARY > 3000000;

---------------------------------------------------- 3. UNION ALL -----------------------------------------------------
-- 부서코드가 D5인사원 또는 급여가 300만원 초과인 사원들 조회(중복은 중복된 갯수만큼 조회)
SELECT EMP_NAME, DEPT_CODE, SALARY
 FROM EMPLOYEE
WHERE DEPT_CODE = 'D5'
UNION ALL
SELECT EMP_NAME, DEPT_CODE, SALARY
 FROM EMPLOYEE
WHERE SALARY > 3000000;

---------------------------------------------------- 4. MINUS -----------------------------------------------------
-- 부서코드가 D5인사원들 중 급여가 300만원 초과인 사원을 제외하고 조회
SELECT EMP_NAME, DEPT_CODE, SALARY
 FROM EMPLOYEE
WHERE DEPT_CODE = 'D5'
MINUS
SELECT EMP_NAME, DEPT_CODE, SALARY
 FROM EMPLOYEE
WHERE SALARY > 3000000;

-- AND
SELECT EMP_NAME, DEPT_CODE, SALARY
 FROM EMPLOYEE
WHERE DEPT_CODE = 'D5' AND SALARY <= 3000000;

[SQL/ORACLE] FUNCTION(단일행 함수 - 문자 처리 함수, 숫자 처리 함수, 날짜 처리 함수)

컴퓨터/SQL

 <함수 function>
    전달된 컬럼값을 읽어들여 함수를 실행한 결과 반환
    
    - 단일행 함수 : N개의 값을 읽어들여 N개의 결과값 반환(매 행마다 실행)
    - 그룹 함수 : N개의 값을 읽어들여 1개의 결과값 반환(그룹별로 함수 실행)
    
    >> SELECT절에 단일행 함수와 그룹함수를 함께 사용할 수 없음
    >> 함수식을 기술할 수 있는 위치 : SELECT절, WHERE절, ORDER BY절, HAVING절

 

* 단일행 함수

1. 문자처리 함수

1.1 LENGTH / LENGTHB => NUMBER로 반환

    LENGTH(컬럼|'문자열') : 해당 문자열의 글자수 반환
    LENGTHB(컬럼|'문자열') : 해당 문자열의 byte수 반환
      - 한글 : XE버전일 때 => 1글자당 3BYTE(ㄱ, ㅏ 등도 3BYTE)
                EE버전일 때 => 1글자당 2BYTE
      - 그외 : 1글자당 1BYTE

SELECT LENGTH('오라클'), LENGTHB('오라클')
  FROM DUAL;   -- 오라클에서 제공하는 가상테이블

SELECT LENGTH('ORACLE'), LENGTHB('ORACLE')
  FROM DUAL;

SELECT EMP_NAME, LENGTH(EMP_NAME), LENGTHB(EMP_NAME),
       EMAIL, LENGTH(EMAIL), LENGTHB(EMAIL)
  FROM EMPLOYEE;

 

1.2 INSTR

      문자열로부터 특정문자의 시작위치(INDEX)를 찾아서 반환(반환형:NUMBER)
      - ORACLE에서 INDEX번호는 1부터 시작.  찾을 문자가 없으면 0반환
      
     [표현법]
     INSTR(컬럼|'문자열', '찾고자하는 문자', [찾을위치의 시작값, [순번]])
       - 찾을위치 시작값
          1 : 앞에서부터 찾기(기본값)
          -1 : 뒤에서부터 찾기

SELECT INSTR('JAVASCRIPTJAVAORACLE', 'A') FROM DUAL;
SELECT INSTR('JAVASCRIPTJAVAORACLE', 'A', 1) FROM DUAL;
SELECT INSTR('JAVASCRIPTJAVAORACLE', 'A', -1) FROM DUAL;
SELECT INSTR('JAVASCRIPTJAVAORACLE', 'A', 1, 3) FROM DUAL;
SELECT INSTR('JAVASCRIPTJAVAORACLE', 'A', -1, 2) FROM DUAL;

SELECT INSTR('JAVASCRIPTJAVAORACLE', 'A', 3) FROM DUAL;

SELECT EMAIL, INSTR(EMAIL, '_') "_의 위치", INSTR(EMAIL, '@') "@의 위치"
  FROM EMPLOYEE;

 

1.3 SUBSTR

   문자열에서 특정 문자열을 추출하여 반환(반환형 : NUMBER)
    
    [표현법]
    SUBSTR(컬럼|'문자열', POSITION, [LENGTH])
      - POSITION : 문자열을 추추할 시작위치 INDEX
      - LENGTH : 추출할 문자의 갯수(생략시 마지막까지 추출)

SELECT SUBSTR('ORACLEHTMLCSS', 7) FROM DUAL;
SELECT SUBSTR('ORACLEHTMLCSS', 7, 4) FROM DUAL;
SELECT SUBSTR('ORACLEHTMLCSS', 1, 6) FROM DUAL;
SELECT SUBSTR('ORACLEHTMLCSS', -7, 4) FROM DUAL;

-- EMPLOYEE에서 사원명, 주민번호, 성별(주민번호에서 성별만 추출하기)
SELECT EMP_NAME, EMP_NO, SUBSTR(EMP_NO,8,1) 성별
  FROM EMPLOYEE;
  
-- EMPLOYEE에서 여자사원들의 사원번호, 사원명, 성별 조회
SELECT EMP_ID, EMP_NAME, SUBSTR(EMP_NO,8,1) 성별
  FROM EMPLOYEE
 WHERE SUBSTR(EMP_NO,8,1) = '2' OR SUBSTR(EMP_NO,8,1) = '4';

-- EMPLOYEE에서 남자사원들의 사원번호, 사원명, 성별 조회
SELECT EMP_ID, EMP_NAME, SUBSTR(EMP_NO,8,1) 성별
  FROM EMPLOYEE
 WHERE SUBSTR(EMP_NO,8,1) IN (1,3)
 ORDER BY 2;

-- EMPLOYEE에서 사원명, 이메일, 아이디 조회
SELECT EMP_NAME, EMAIL, SUBSTR(EMAIL, 1, INSTR(EMAIL,'@')-1) "아이디"
  FROM EMPLOYEE;

SELECT EMAIL, INSTR(EMAIL,'@')
  FROM EMPLOYEE;

 

1.4 LPAD / RPAD

     문자열을 조회할 때 통일감있게 조회하고자 할 때(반환형 : CHARACTER)
      
      [표현법]
      LPAD/RPAD('문자열', 최종적으로 반환할 문자의 길이, [덧붙이고자하는 문자])
       - 문자열에 덧붙이고자하는 문자를 왼쪽 또는 오른쪽에 덧붙여서 최종 N길이만큼의 문자열 반환

-- EMPLOYEE에서 사원명, 이메일(길이 20, 오른쪽 정렬)
SELECT EMP_NAME, EMAIL, LPAD(EMAIL, 25)     -- 덧붙이고자하는 문자 생략시 공백으로 채워짐
  FROM EMPLOYEE;

SELECT EMP_NAME, EMAIL, LPAD(EMAIL, 25, '#') 
  FROM EMPLOYEE;

SELECT EMP_NAME, EMAIL, RPAD(EMAIL, 25, '#') 
  FROM EMPLOYEE;
  
-- EMPLOYEE에서 사번, 사원명, 주민번호(단, 123456-1******형식으로 출력) 조회
SELECT EMP_ID, EMP_NAME, RPAD(SUBSTR(EMP_NO, 1, 8), 14, '*') 주민번호
  FROM EMPLOYEE;
  
SELECT EMP_ID, EMP_NAME, SUBSTR(EMP_NO, 1, 8) || '******'
  FROM EMPLOYEE;

 

1.5 LTRIM/RTRIM

 문자열에서 특정문자를 제거한 나머지를 반환(반환형: CHARACTER )

 TRIM : 문자열에서 앞/뒤 양쪽에 있는 특정문자를 제거한 나머지를 반환
        - 주의사항 : 제거할 문자 1글자만 가능

      
    [표현법]
    LTRIM / RTRIM('문자열',[제거하고자하는 문자들])
    TRIM([LEADING|TRAILING|BOTH]제거하고자하는 문자들 FROM  '문자열')
       
    문자열의 왼쪽 또는 오른쪽으로 제거하고자하는 문자들을 찾아서 제거한 나머지 문자열 반환

-- 제거하고자하는 문자를 넣지않으면 공백 제거
SELECT LTRIM('     tjoeun     ')||'학원' FROM DUAL;
SELECT RTRIM('     tjoeun     ')||'학원' FROM DUAL;

SELECT LTRIM('JAVAJAVASCRIPT','JAVA') FROM DUAL;
SELECT LTRIM('BACACABCFIACB','ABC') FROM DUAL;
SELECT LTRIM('37284BAC38290','0123456789') FROM DUAL;

SELECT RTRIM('BACACABCFIACB','ABC') FROM DUAL;
SELECT RTRIM('37284BAC38290','0123456789') FROM DUAL;

-- BOTH가 기본값 : 양쪽제거
SELECT TRIM('     tjoeun     ')||'학원' FROM DUAL;
SELECT TRIM('A' FROM 'AAABKSLEIDKAAA') FROM DUAL;   -- 1글자만 가능
SELECT TRIM(BOTH 'A' FROM 'AAABKSLEIDKAAA') FROM DUAL;

SELECT TRIM(LEADING 'A' FROM 'AAABKSLEIDKAAA') FROM DUAL;
SELECT TRIM(TRAILING 'A' FROM 'AAABKSLEIDKAAA') FROM DUAL;

 

1.6 LOWER / UPPER / INITCAP

   문자열을 대소문자로 변환 및 단어의 앞글자만 대문자로 변환
    
    [표현법]
    LOWER('문자열')

SELECT LOWER('Java JavaScript Oracle') from dual;
SELECT UPPER('Java JavaScript Oracle') from dual; 
SELECT INITCAP('java javaScript oracle') from dual;

-- EMPLOYEE에서 EMAIL 대문자로 출력
SELECT EMAIL, UPPER(EMAIL)
  FROM EMPLOYEE

 

1.7 CONCAT

    문자열 두개를 하나로 합친 후 반환
    
    [표현법]
    CONCAT('문자열','문자열')

SELECT CONCAT('Oracle','오라클') FROM DUAL;
SELECT 'Oracle'||'오라클' FROM DUAL;

-- SELECT CONCAT('Oracle','오라클','02-1234-5678') FROM DUAL;  -- 문자열 2개만 넣을 수 있음
SELECT 'Oracle'||'오라클'||'02-1234-5678' FROM DUAL;

 

1.8 REPLACE

   기존문자열을 새로운 문자열로 바꿈
    
    [표현법]
    REPLACE('문자열','기존문자열','바꿀문자열')

-- EMPLOYEE에서 EMAIL의 문자를 tjoeun.or.kr -> naver.com으로 바꾸어 출력
SELECT REPLACE(EMAIL, 'tjoeun.or.kr', 'naver.com')
  FROM EMPLOYEE;

 

2. 숫자처리 함수

2.1 ABS

   숫자의 절대값을 구하는 함수
    
    [표현법]
    ABS(NUMBER)

SELECT ABS(-5) FROM DUAL;
SELECT ABS(-3.14) FROM DUAL;

 

2.2 MOD

     두 수를 나눈 나머지값 반환하는 함수
    
    [표현법]
    MOD(NUMBER, NUMBER)

SELECT MOD(10,3) FROM DUAL;

 

2.3 ROUND

    반올림한 결과 반환
    
    [표현법]
    ROUND(NUMBER, [위치])

SELECT ROUND(1234.567) FROM DUAL;
SELECT ROUND(1234.123) FROM DUAL;
SELECT ROUND(1234.123, 2) FROM DUAL;
SELECT ROUND(1234.127, 2) FROM DUAL;
SELECT ROUND(1234567, -2) FROM DUAL;

 

2.4 CEIL

   올림한 결과 반환

   
    [표현법]
    ROUND(NUMBER)

SELECT CEIL(123.4566) FROM DUAL;
SELECT CEIL(-123.4566) FROM DUAL;

 

2.5 FLOOR

   내림한 결과 반환
    
    [표현법]
    FLOOR(NUMBER)

SELECT FLOOR(123.987) FROM DUAL;
SELECT FLOOR(-123.987) FROM DUAL;

 

2.6 TRUNC 

   위치 지정 가능한 버리처리 함수
    
    [표현법]
    TRUNC(NUMBER, [위치지정])

SELECT TRUNC(123.789) FROM DUAL;
SELECT TRUNC(123.789, 1) FROM DUAL;
SELECT TRUNC(123.789, -1) FROM DUAL;

SELECT TRUNC(-123.789) FROM DUAL;
SELECT TRUNC(-123.789, -2) FROM DUAL;

 

3. 날짜처리 함수

3.1 MONTHS_BETWEEN(DATE1, DATE2)

   두 날짜 사이의 개월수
    
    [표현법]
    MONTHS_BETWEEN(날짜, 날짜)

SELECT EMP_NAME, HIRE_DATE, SYSDATE-HIRE_DATE "근무일수"
  FROM EMPLOYEE;

SELECT EMP_NAME, HIRE_DATE, CEIL(SYSDATE-HIRE_DATE) "근무일수"
  FROM EMPLOYEE;

SELECT EMP_NAME, HIRE_DATE, MONTHS_BETWEEN(SYSDATE, HIRE_DATE) "근무개월수"
  FROM EMPLOYEE;
  
SELECT EMP_NAME, HIRE_DATE, CEIL(MONTHS_BETWEEN(SYSDATE, HIRE_DATE)) "근무개월수"
  FROM EMPLOYEE;
  
SELECT EMP_NAME, HIRE_DATE, CEIL(MONTHS_BETWEEN(SYSDATE, HIRE_DATE)) || '개월차' "근무개월수"
  FROM EMPLOYEE;  
  
SELECT EMP_NAME, HIRE_DATE, CONCAT(CEIL(MONTHS_BETWEEN(SYSDATE, HIRE_DATE)), '개월차') "근무개월수"
  FROM EMPLOYEE;

 

3.2 DD_MONTHS(DATE, NUMBER)

     특정날짜에 해당 숫자만큼 개월수를 더해  반환

SELECT SYSDATE, NEXT_DAY(SYSDATE, '금요일') FROM DUAL;
SELECT SYSDATE, NEXT_DAY(SYSDATE, '금') FROM DUAL;

SELECT SYSDATE, NEXT_DAY(SYSDATE, 6) FROM DUAL;
-- SELECT SYSDATE, NEXT_DAY(SYSDATE, 'FRIDAY') FROM DUAL;   -- 오류 : 현재언어가 KOREA아기 때문

-- 언어변경
ALTER SESSION SET NLS_LANGUAGE = AMERICAN;
SELECT SYSDATE, NEXT_DAY(SYSDATE, 'FRIDAY') FROM DUAL;
-- SELECT SYSDATE, NEXT_DAY(SYSDATE, '금요일') FROM DUAL;   -- 오류 

ALTER SESSION SET NLS_LANGUAGE = KOREAN;

 

3.3 LAST_DAY(DATE)

      해당월의 마지막 날짜를 반환해주는 함수

SELECT LAST_DAY(SYSDATE) FROM DUAL;

-- EMPLOYEE에서 사원명, 입사일, 입사한 날의 마지막 날짜 조회
SELECT EMP_NAME, HIRE_DATE, LAST_DAY(HIRE_DATE)
  FROM EMPLOYEE;

 

3.4 EXTRACT

      특정 날짜로 부터 년도|월|일 값을 추출하여 반환해주는 함수(반환형:NUMBER)
       
       EXTRACT(YEAR FROM DATE) : 년도 추출
       EXTRACT(MONTH FROM DATE) : 월만 추출
       EXTRACT(DAY FROM DATE) : 일만 추출

-- EMLPYEE에서 사원명, 입사년도, 입사월, 입사일 조회
SELECT EMP_NAME, 
        EXTRACT(YEAR FROM HIRE_DATE) "입사년도",
        EXTRACT(MONTH FROM HIRE_DATE) "입사월",
        EXTRACT(DAY FROM HIRE_DATE) "입사일"
 FROM EMPLOYEE
ORDER BY 입사년도, 입사월, 입사일;

 

[SQL/ORACLE] SELECT

컴퓨터/SQL

1. SELECT 구문

<SELECT>
 데이터 조회할 때 사용하는 구문
    >> RESULT SET : SELECT구문을 통해서 조회된 결과물( 즉, 조회된 행들의 집합)
    
    [표현법]
    SELECT 조회하고자하는 컬럼명, 컬럼명, ...
    FROM 테이블명;

-- EMPLOYEE테이블의 모든 컬럼(*) 조회
SELECT *
  FROM employee;

SELECT *
FROM DEPARTMENT;

SELECT * FROM JOB;

-- EMPLOYEE테이블에서 사번, 이름, 급여만 조회
SELECT EMP_ID, EMP_NAME, SALARY 
  FROM employee;

 

------------------- 실습문제----------------------
--1. JOB테이블에 직급명만 조회

SELECT JOB_NAME
	FROM JOB;


--2. DEPARTMENT 테이블의 모든 컬럼 조회

SELECT * FROM DEPARTMENT;


--3. DEPARTMENT 테이블의 부서코드, 부서명만 조회

SELECT DEPT_CODE, DEPT_TITLE
	FROM DEPARTMENT;


--4. EMPLOYEE 테이블에 사원명, 이메일, 전화번호, 입사일, 급여 조회

SELECT EMP_NAME, EMAIL, PHONE, HIRE_DATE, SALARY
	FROM EMPLOYEE;

 

2. 컬럼값을 통한 산술연산

 SELECT절의 컬럼명 작성부분에 산술연산 기술 가능(이때 산술연산된 결과 조회)

-- EMPLOYEE테이블 사원명, 사원의 연봉(급여*12) 조회
SELECT EMP_NAME, SALARY*12
  FROM EMPLOYEE;
  
-- EMPLOYEE테이블 사원명, 급여, 보너스
SELECT EMP_NAME, SALARY, BONUS
  FROM EMPLOYEE;

-- EMPLOYEE테이블 사원명, 급여, 보너스, 연봉, 보너스를 포함한 연봉( (급여+보너스*급여)*12) )
SELECT EMP_NAME, SALARY, BONUS, SALARY*12, (SALARY+BONUS*SALARY)*12
  FROM EMPLOYEE;
  --> 산술연산 중 NULL값이 존재할 경우 산술연산한 결과값도 무조건 NULL이 됨
  
-- EMPLOYEE테이블 사원명, 입사일, 근무일수(오늘날짜-입사일)
-- DATE형태끼리도 연산 가능 : 결과값은 일 단위
-- * 오늘날짜 : SYSDATE
SELECT EMP_NAME, HIRE_DATE, SYSDATE - HIRE_DATE
  FROM EMPLOYEE;
-- 소수점까지 나오는 이유는 초단위까지 관리하기 때문

 

3. 컬럼명에 별칭 지정하기

산술연산시 컬럼명이 산술에 들어간 수식 그대로 됨. 이때 별칭을 부여하면 별칭이 컬럼명이 됨
    
    [표현법]
    컬럼명 별칭 
    컬럼명 AS 별칭
    컬럼명 "별칭"
    컬럼명 AS "별칭"

SELECT EMP_NAME, SALARY, BONUS, SALARY*12 연봉, (SALARY+BONUS*SALARY)*12 AS 총연봉
  FROM EMPLOYEE;

-- 별칭에 특수기호나 띄어쓰기가 들어가면 반드시 쌍따옴표(")로 묶어줘야함
SELECT EMP_NAME, SALARY, BONUS, SALARY*12 "연봉(원)", (SALARY+BONUS*SALARY)*12 AS "총 연봉"
  FROM EMPLOYEE;
  
-- 위의 예제에서 사원명, 급여, 보너스, 연봉(원), 총 연봉 별칭부여하기
SELECT EMP_NAME 사원명, SALARY AS 급여, BONUS "보너스", SALARY*12 "연봉(원)", (SALARY+BONUS*SALARY)*12 AS "총 연봉"
  FROM EMPLOYEE;

 

4. 리터럴

임의로 지정한 문자열(')
    
    SELECT절에 리터럴을 넣으면 마치 테이블상에 존재하는 데이터 처럼 조회 가능
    조회된 RESULT SET의 모든 행에 반복적으로 같이 출력

-- EMPLOYEE 사번, 사원명, 급여 조회 - 컬럼을 하나 만들어서 원을 넣어주도록함
SELECT EMP_ID, EMP_NAME, SALARY, '원' AS 단위
  FROM EMPLOYEE;

 

5. 연결 연산자 : ||

여러 컬럼값을 마치 하나의 컬럼값인것처럼 연결하거나, 컬럼값과 리터럴을 연결할 수 있음

-- EMPLOYEE 사번, 사원명, 급여를 하나의 컬럼으로 조회
SELECT EMP_ID || EMP_NAME || SALARY AS "사원의 급여"
  FROM EMPLOYEE;

-- 컬럼값과 리터럴과 연결
SELECT EMP_NAME || '의 월급은 ' || SALARY || '원 입니다'
FROM EMPLOYEE;

SELECT EMP_ID, EMP_NAME, SALARY || '원' 급여
  FROM EMPLOYEE;

 

6. DISTINCT

    컬럼에 중복된 값들은 한번씩만 표시하고자 할 때

-- EMPLOYEE에 부서코드 중복제거 조회
SELECT DISTINCT DEPT_CODE
FROM EMPLOYEE;

-- EMPLOYEE에 직급코드 중복제거 조회
SELECT DISTINCT JOB_CODE
  FROM EMPLOYEE;
  
-- 주의사항 : DISTINCT는 SELECT절에 딱 한번만 기술 가능
-- SELECT DISTINCT DEPT_CODE DISTINCT JOB_CODE
SELECT DISTINCT DEPT_CODE, JOB_CODE  -- 2개의 조합으로 1번
  FROM EMPLOYEE;

 

7. WHERE 절

    조회하고자 하는 테이블로부터 특정조건에 만족하는 데이터만 조회할 때
    이때 WHERE절에 조건식을 쓰면 됨
    조건식에서는 다양한 연산자 사용가능
    
    [표현법]
    SELECT 컬럼명,... 
    FROM 테이블명
    WHERE 조건식;
    
    - 비교연산자
    >, <, >=, <=   --> 대소비교
    =              --> 같은지 비교
    !=, ^=, <>     --> 같지않은지 비교

-- EMPLOYEE에서 부서코드가 'D9'인 사원들의 모든 컬럼 조회
SELECT *
  FROM EMPLOYEE
 WHERE DEPT_CODE = 'D9';
 
-- EMPLOYEE에서 부서코드가 'D1'인 사원들의 사원명, 급여, 부서코드 조회
SELECT EMP_NAME, SALARY, DEPT_CODE
FROM EMPLOYEE
WHERE DEPT_CODE = 'D1';

-- EMPLOYEE에서 부서코드가 'D1'이 아닌 사원들의 사원명, 이메일, 부서코드 조회
SELECT EMP_NAME, EMAIL, DEPT_CODE
  FROM EMPLOYEE
-- WHERE DEPT_CODE != 'D1';
-- WHERE DEPT_CODE ^= 'D1';
WHERE DEPT_CODE <> 'D1';

-- EMPLOYEE에서 급여가 4백만원 이상인 사원들의 사원명, 부서코드, 급여 조회
SELECT EMP_NAME, DEPT_CODE, SALARY
  FROM EMPLOYEE
 WHERE SALARY >= 4000000; 

-- EMPLOYEE에서 재직중(ENT_YN)인 사원의 사원명, 전화번호 조회
SELECT EMP_NAME, PHONE
  FROM EMPLOYEE
 WHERE ENT_YN = 'N';

 

8. 논리 연산자

    AND (그리고, ~이면서)
    OR (또는, ~이거나)

-- EMPLOYEE에서 부서코드가 'D9'이면서 급여가 500만원 이상인 사원들의 사원명, 부서코드, 급여 조회
SELECT EMP_NAME, DEPT_CODE, SALARY
  FROM EMPLOYEE
 WHERE DEPT_CODE = 'D9' AND SALARY >= 5000000;
 
-- EMPLOYEE에서 부서코드가 'D5'이거나 급여가 300만원 이상인 사원들의 사원명, 부서코드, 급여 조회
SELECT EMP_NAME, DEPT_CODE, SALARY
  FROM EMPLOYEE
 WHERE DEPT_CODE = 'D5' OR SALARY >= 3000000; 

-- EMPLOYEE에서 급여가 350만원 이상 600만원 이하인 사원들의 사원명, 사번, 급여 조회
SELECT EMP_NAME, EMP_ID, SALARY
  FROM EMPLOYEE
-- WHERE 3500000 <= SALARY <= 6000000  -- 오류
WHERE 3500000 <= SALARY AND SALARY <= 6000000;

 

9. BETWEEN AND

    조건식에서 사용되는 구문
    ~이상 ~이하인 범위에 대한 조건을 제시할 사용하는 연산자
    
    [표현법]
    비교대상컬럼 BETWEEN 하한값 AND 상한값
    -> 해당 컬럼값이 하한값 이상이고 상한값 이하인 경우

SELECT EMP_NAME, EMP_ID, SALARY
  FROM EMPLOYEE
 WHERE SALARY BETWEEN 3500000 AND 6000000;  

-- 입사일이 1990년대 들어온 사원의 사원명, 입사일 조회
SELECT EMP_NAME, HIRE_DATE
  FROM EMPLOYEE
  
-- WHERE HIRE_DATE >= '90/01/01' AND HIRE_DATE <= '99/12/31';
 WHERE HIRE_DATE BETWEEN '90/01/01' AND '99/12/31';

 

10. LIKE

   비교하고자하는 컬럼값이 내가 제시한 특정 패턴에 만족하는 경우 조회
    
    [표현법]
    비교대상컬럼 LIKE '특정패턴'
    : 특정패턴 제시시 '%','_'를 와일드카드로 쓸 수 있음
    
    >> '%' : 0글자 이상
    EX) 비교대상컬럼 LIKE '문자%' => 비교대상의 컬럼값이 '문자'로 시작되는 데이터 조회
        비교대상컬럼 LIKE '%문자' => 비교대상의 컬럼값이 '문자'로 끝나는 데이터 조회
        비교대상컬럼 LIKE '%문자%' => 비교대상의 컬럼값이 '문자'가 포함되어 있는 데이터 조회
        
    >> '_' : 1글자
    EX) 비교대상컬럼 LIKE '_문자' => 비교대상의 컬럼값이 '문자'앞에 무조건 한글자가 들어있는 데이터 조회
        비교대상컬럼 LIKE '_ _문자' => 비교대상의 컬럼값이 '문자'앞에 무조건 두글자가 들어있는 데이터 조회
        비교대상컬럼 LIKE '_문자_' => 비교대상의 컬럼값이 '문자'앞에 무조건 한글자, 뒤에도 무조건 한글자가 들어있는 데이터 조회

-- EMPLOYEE에서 사원 성이 전씨인 사원들의 사원명, 급여, 입사일 조회
SELECT EMP_NAME, SALARY, HIRE_DATE
  FROM EMPLOYEE
 WHERE EMP_NAME LIKE '전%'; 

-- EMPLOYEE에서 사원의 이름에 '하'자가 들어있는 사원의 사원명, 이메일, 전화번호 조회
SELECT EMP_NAME, EMAIL, PHONE
  FROM EMPLOYEE
 WHERE EMP_NAME LIKE '%하%'; 

-- EMPLOYEE에서 사원의 이름에 '하'자 중간에 들어있는 사원의 사원명, 이메일, 전화번호 조회
SELECT EMP_NAME, EMAIL, PHONE
  FROM EMPLOYEE
 WHERE EMP_NAME LIKE '_하_'; 

-- EMPLOYEE에서 전화번호의 3번째 자리가 '1'인 사원의 사원명, 전화번호 조회
SELECT EMP_NAME, PHONE
  FROM EMPLOYEE
 WHERE PHONE LIKE '__1%'; 
 
-- 이메일중 _(언더바) 앞에 글자가 3글자인 사원들의 사원명, 이메일 조회
SELECT EMP_NAME, EMAIL
  FROM EMPLOYEE
 WHERE EMAIL LIKE '____%';
 -- 와일드카드로 사용하는 문자와 컬럼값에 들어있는 문자가 동일하기 때문에 조회 안됨
 -- 모두다 와일드카드로 인식
 /*
    > 와일드카드와 문자를 구분해줘야 함
    > 나만의 와일드카드를 ESCAPE로 등록
      - 데이터값으로 취급하고자하는 값 앞에 나만의 와일드카드(문자,숫자,특수문자)를 넣어줌
      - 특수기호 '&'는 안쓰는것이 좋다. 사용자로부터 입력받을 때 &를 사용함
 */
 
 SELECT EMP_NAME, EMAIL
  FROM EMPLOYEE
 WHERE EMAIL LIKE '___$_%' ESCAPE '$';
 
 SELECT EMP_NAME, EMAIL
  FROM EMPLOYEE
 WHERE EMAIL LIKE '___d_%' ESCAPE 'd';

   

11. IS NULL / IS NOT NULL

     컬럼값에 NULL이 있을 경우 NULL값 비교에 사용하는 연산자

-- EMPLOYEE에서 보너스를 받지 않는 사원의 사원명, 급여, 보너스 조회
SELECT EMP_NAME, SALARY, BONUS
  FROM EMPLOYEE
 -- WHERE BONUS = NULL; -- 조회안됨
WHERE BONUS IS NULL;

-- EMPLOYEE에서 보너스를 받는 사원의 사원명, 급여, 보너스 조회
SELECT EMP_NAME, SALARY, BONUS
  FROM EMPLOYEE
WHERE BONUS IS NOT NULL;

-- EMPLOYEE에서 사수가 없는(MANAGER_ID값이 NULL인)사원의 사원명, 부서코드 조회
SELECT EMP_NAME, DEPT_CODE, MANAGER_ID
  FROM EMPLOYEE
 WHERE MANAGER_ID IS NULL;

-- EMPLOYEE에서 부서배치를 받지 않았지만 보너스는 받는 사원의 사원명, 보너스, 부서코드 조회
SELECT EMP_NAME, BONUS, DEPT_CODE
  FROM EMPLOYEE
 WHERE DEPT_CODE IS NULL AND BONUS IS NOT NULL;

 

12. IN / NOT IN

    IN : 컬럼값이 내가 제시한 목록중에 일치하는 것만 조회
    NOT IN : 컬럼값이 내가 제시한 목록중에 일치하는 값을 제외하고 조회
    
    [표현법]
    비교대상컬럼 IN ('값1','값2','값3',...))

-- EMPLOYEE에서 부서코드가 D6이거나 D8이거나 D5인 사원들의 사원명, 부서코드, 급여조회
SELECT EMP_NAME, DEPT_CODE, SALARY
  FROM EMPLOYEE
-- WHERE DEPT_CODE = 'D6' OR DEPT_CODE = 'D8' OR DEPT_CODE = 'D5'; 
 WHERE DEPT_CODE IN ('D6', 'D8', 'D5');
 
-- 그외 사원들
SELECT EMP_NAME, DEPT_CODE, SALARY
  FROM EMPLOYEE
 WHERE DEPT_CODE NOT IN ('D6', 'D8', 'D5');

 

13. 연산자 우선순위

    1. ()
    2. 산술연산자
    3. 연결연산자
    4. 비교연산자
    5. IS NULL / LIKE '특정패턴' /IN
    6. BETWEEN AND
    7. NOT(논리연산자)
    8. AND(논리연산자)
    9. OR(논리연산자)

-- ** AND가 OR보다 순위 더 높다
-- 직급코드가 J7이거나 J2인 사원들 중 급여가 200만원 이상인 사원들의 사원명, 급여, 직급코드 조회
SELECT EMP_NAME, SALARY, JOB_CODE
  FROM EMPLOYEE
 WHERE (JOB_CODE = 'J7' OR JOB_CODE = 'J2') AND SALARY >= 2000000;

 

14. ORDER BY절

   데이터를 정렬하여 보여줌
    SELECT문의 가장 마지막 줄에 작성 뿐만 아니라 실행 순서도 마지막에 실행
    
    [표현법]
    SELECT 조회할 컬럼1, 컬럼2,...
    FORM 테이블명
    WHERE 조건식
    ORDER BY 정렬기준의 컬럼명 | 별칭 | 컬럼순번[ASC|DESC] [NULLS FIRST | NULLS LAST];
    
    - ASC : 오름차순 정렬(생략시 기본값)
    - DESC : 내림차순 정렬
    
    - NULLS FIRST : 컬럼값에 NULL이 있을 경우 맨앞에 배치(생략시 DESC 일때의 기본값)
    - NULLS LAST : 컬럼값에 NULL이 있을 경우 맨뒤에 배치(생략시 ASC 일때의 기본값)

 

SELECT *
  FROM EMPLOYEE
-- ORDER BY BONUS;          -- 오름차순 정렬 NULL이 LAST
-- ORDER BY BONUS ASC;
-- ORDER BY BONUS NULLS FIRST;

--ORDER BY BONUS DESC;        -- 내림차순 정렬
ORDER BY BONUS DESC, SALARY;    -- 정렬기준 여러개일 때, 앞을 기준으로 정렬하고 값이 같으면 뒤에 기준으로 정렬

-- 모든 사원의 사원명, 연봉 조회 이때, 연봉의 내림차순 정렬 조회
SELECT EMP_NAME, SALARY*12 "연봉"
  FROM EMPLOYEE
-- ORDER BY SALARY*12 DESC; 
-- ORDER BY 연봉 DESC;      -- 별칭 사용 가능
ORDER BY 2 DESC;            -- 2번째 컬럼

 

 

[SQL/ORACLE] 기초 및 일반 계정 생성

컴퓨터/SQL

1. 주석 처리

-- 한줄 주석 (단축키 : ctrl+/ )
/*
    여러줄 주석
    alt + shift + c
*/

 

2. 실행 단축키 : crtl + enter

 

3. 나의 계정 보기

show user;

 

4. 사용자 계정 조회

/*
    - 조회시
      select 속성명 from 테이블명
*/
select * from dba_users;
select username, user_id from dba_users;

 

5. USER 계정 생성

/*
    오라클 12버전부터 일반사용자는 c##로 시작하는 이름을 가져야 함
    비밀번호는 문자로만 가능
*/
-- CREATE USER user1 IDENTIFIED BY 1234;
create user c##user2 IDENTIFIED by 1234;

 

6. c##키워드를 회피하는 설정

alter session set "_oracle_script" = true;

 

7. 수업시간에 사용할 user생성

/*
    계정명은 대소문자 안가림
    create user 계정명 identified by 비밀번호
*/
create user tjoeun identified by 1234;

 

8. 권한생성

/*
    [표현법] GRANT 권한1, 권한2, .... TO 계정명;
*/
GRANT RESOURCE, CONNECT TO TJOEUN;

 

9. USER 삭제

DROP USER USER명 CASCAED;

 

10. insert시 생성된 유저에게 테이블스페이스에 얼마만큼의 영역을 할당할 것인지 정해줘야함

alter user tjoeun default tablespace users quota unlimited on users;

 

10-1. 특정 용량만큼 정해서 할당

alter user tjoeun quota 30M on users;

 

11. 

/*
 (')홀따옴표 : 문자열
 (")쌍따옴표 : 컬럼명
*/

 

* 계정 생성 정리 *

alter session set "_oracle_script" = true;		
create user 계정명 identified by 1234;			
GRANT RESOURCE, CONNECT TO 계정명;
alter user 계정명 default tablespace users quota unlimited on users;