1)뷰의 개념

  • 물리적인 테이블을 근거한 논리적인 가상 테이블(실제로 데이터를 가지고 있는 테이블X)                                               =>데이터 저장 공간이 없다. 단지 쿼리문을 저장하고 있는 객체
  • 기본 테이블에서 파생된 객체로 기본 테이블에 대한 하나의 쿼리문
  • 실제 테이블에 저장된 데이터를 뷰를 통해서 볼 수 있도록 한다.

 

--ORA-01031: 권한이 불충분합니다
GRANT CREATE VIEW TO hr; --PDB_SYS로 접속해서 권한 부여, GRANT=DCL

--Grant을(를) 성공했습니다.
--CREATE를 주면 생성뿐만 아니라 조회하고, 삭제할 수 있는 권한도 부여
--각 각 주고 싶으면 SELECT 이용
--작업 끝나면 PDB_SYS 접속 해제(최고 권한자 접속 해제)
--PDB(데이터베이스)_SYS(사용자이름)

 

 

뷰의 사용 목적: 직접적인 테이블 접근을 제한(보안성), 복잡한 질의를 쉽게 만듦(편의성)

뷰의 특징: 테이블에 대한 제한을 가지고 테이블의 일정한 부분만 보일 수 있는 가상의 테이블, 실제 자료를 갖지는 않지만 테이블을 관리 가능, 하나의 테이블에 뷰의 개수는 제한이 없다.

 

2) 뷰 생성과 조회

테이블 생성과 같이 CREATE문 사용

기본 테이블: 뷰에 의해 제한적으로 겁근해서 사용하는 실질적으로 데이터를 저장하고 있는 물리적인 테이블.

 

3) 뷰 생성

테이블을 생성할 때와 유사하게 CREATE VIEW 명령어로 생성

CREATE [OR REPLACE] [FORCE|NOFORCE] VIEW VIEW_NAME
[(ALIAS,ALIAS,ALIAS,...)]
AS SUBQUERY
[WITH CHECK OPTION]
[WITH READ ONLY];		--해당 뷰를 통해서는 SELECT만 가능하며
				--INSERT/UPDATE/DELETE 불가능
                
 CREATE VIEW 뷰이름
 AS
 SELECT ~
 
 --뷰 확인
 SELECT 컬럼 FROM 뷰이름;

 

OR REPLACE VIEW: 새로운 뷰를 만들 수 있을 뿐만 아니라 기존에 뷰가 존재하더라도 삭제하지 않고 새로운 구조의 뷰로.

 

WITH CHECK OPTION: 해당 뷰를 통해서 볼 수 있는 범위 내에서만 UPDATE OR INSERT가 가능

 

1] 뷰에 관련된 데이터 딕셔너리

데이터 딕셔너리 USER_VIEWS에 사용자가 생성한 모든 뷰에 대한 정의가 저장

VIEW_NAME: 뷰의 이름, TEXT:칼럼과 뷰를 작성할 때 기술한 서브 쿼리문

SELECT VIEW_NAME, TEXT
FROM USER_VIEWS;

 

2] 뷰의 동작 원리

  1. 사용자가 뷰에 대해서 질의를 하면 USER_VIEWS에서 뷰에 대한 정의를 조회
  2. 기본 테이블에 대한 뷰의 접근 권한을 살핌.
  3. 뷰에 대한 질의를 기본 테이블에 대한 질의로 변환
  4. 기본 테이블에 대한 질의를 통해 데이터를 검색
  5. 검색된 결과 출력

4) 뷰의 종류(뷰를 정의하기 위해서 사용되는 기본 테이블의 수에 따라 단순 뷰와 복합 뷰로 나뉜다)

단순 뷰 복합 뷰
하나의 테이블로 생성 여러 개의 테이블로 생성
그룹 함수의 사용이 불가능 그룹 함수의 사용이 가능
DISTINCT 사용이 불가능 DISTINCT 사용이 가능
DML(INSERT/UPDATE/DELETE) 사용 가능 DML(INSERT/UPDATE/DELETE) 사용 불가능

 

1]단순 뷰에 대한 데이터 조작 :  INSERT, UPDATE, DELETE 사용 가능

--단순뷰를 기준으로 INSERT/UPDATE/DELETE 수행이 가능
--이때 뷰를 기준으로 입력하면 실제 테이블(기본 테이블)에 데이터가 저장
--뷰에 INSERT하면 기본 테이블에도 삽입이 된다.
--기본테이블에 INSERT가 가능한 조건이여야 뷰에 INSERT가능
INSERT INTO VIEW_EMP01
VALUES(250, 'ANGEL', 7000, 30);

 

2] 단순 뷰의 칼럼에 별칭 부여

--사원번호, 사원명, 급여, 부서번호로 구성된 뷰를 작성하되 기본 테이블은 EMP01로 하고 칼럼명은 한글화
CREATE OR REPLACE VIEW VIEW_EMP02 --기존에 뷰가 존재하면 구조 변경
AS
SELECT EMPLOYEE_ID 사원번호, FIRST_NAME 사원명, SALARY 급여, DEPARTMENT_ID 부서번호 --별칭이 컬럼명이됨
FROM EMP01;

-- 별칭을 부여한 뷰는 실제 컬럼명으로 조회할 수 없다.
--ORA-00904: "DEPARTMENT_ID": 부적합한 식별자
--부서번호로 조회해야 한다.
SELECT * 
FROM VIEW_EMP02
WHERE DEPARTMENT_ID =10;

SELECT * 
FROM VIEW_EMP02
WHERE 부서번호 =10;

 

--부서별 급여의 합, 급여의 평균을 조회할 수 있는 VIEW_SALARY 뷰 생성
CREATE OR REPLACE VIEW VIEW_SALARY
AS
SELECT DEPARTMENT_ID, SUM(SALARY) AS "SALARYSUM", TRUNC(AVG(SALARY)) AS "SALARYAVG" --함수가 선언되어 있는 컬럼에 대해서는 별칭이 무조건 필요!
FROM EMP01
GROUP BY DEPARTMENT_ID
ORDER BY DEPARTMENT_ID;

3] 단 순 뷰에 DML 명령어로 조작 불가능한 경우

  • 뷰 정의에 포함되지 않은 칼럼 중에 기본 테이블의 칼럼이 NOT NULL 제약 조건이 지정되어 있는 경우 INSERT 사용 불가능
  • SALARY*12와 같이 산술 표현식으로 정의된 가상 칼럼이 뷰에 정의되면 INSERT나 UPDATE 불가능
  • DISTINCT을 포함한 경우에도 DML명령 사용 불가능
  • 그룹함수나 GROUP BY 절을 포함한 경우에도 DML(INSERT, UPDATE, DELETE,SELECT) 명령 사용X

4] 복합 뷰: 2개 이상의 기본 테이블에 의해 정의된 뷰

--사원번호, 사원이름,급여, 부서번호, 부서명 조회할 수 있는 VIEW_EMP_DEPT 생성
CREATE VIEW VIEW_EMP_DEPT
AS
SELECT EMPLOYEE_ID, FIRST_NAME, SALARY, E.DEPARTMENT_ID, DEPARTMENT_NAME
FROM EMPLOYEES E INNER JOIN DEPARTMENTS D ON E.DEPARTMENT_ID = D.DEPARTMENT_ID
ORDER BY DEPARTMENT_ID DESC;

SELECT * FROM VIEW_EMP_DEPT;

--ON대신 USING으로 변경
--ORA-25154: USING 절의 열 부분은 식별자를 가질 수 없음
CREATE OR REPLACE VIEW VIEW_EMP_DEPT
AS
SELECT E.EMPLOYEE_ID, E.FIRST_NAME, E.SALARY, DEPARTMENT_ID, D.DEPARTMENT_NAME
FROM EMPLOYEES E INNER JOIN DEPARTMENTS D
USING (DEPARTMENT_ID)
ORDER BY DEPARTMENT_ID DESC;

 

6) 뷰 수정을 위한 OR REPLACE 옵션

CREATE VIEW 대신 CREATE OR REPLACE VIEW를 사용하면, 존재하지 않은 뷰이면 새로운 뷰를 생성하고 기존에 존재하는 뷰이면 내용을 변경

 

7) 기본 테이블 없이 뷰를 생성하기 위한 FORCE 옵션: 기본 테이블이 존재하지 않더라도 뷰를 생성하려면 FORCE옵션 추가

--경고: 컴파일 오류와 함께 뷰가 생성되었습니다.
CREATE OR REPLACE FORCE VIEW VIEW_NOTABLE
AS
SELECT EMPLOYEE_ID, FIRST_NAME, DEPARTMENT_ID
FROM EMP15		--존재하지 않는 테이블
WHERE EMPLOYEE_ID = 10;

 

8)WITH CHECK OPTION: 뷰 생성시 조건으로 지정한 칼럼값을 변경하지 못하도록 하는 것

CREATE OR REPLACE VIEW VIEW_CHK
AS
SELECT EMPLOYEE_ID, FIRST_NAME, SALARY,DEPARTMENT_ID
FROM EMP01
WHERE DEPARTMENT_ID =20 WITH CHECK OPTION;
-------------------------------------------------
--SQL 오류: ORA-01402: 뷰의 WITH CHECK OPTION의 조건에 위배 됩니다
UPDATE VIEW_CHK
SET DEPARTMENT_ID =10   --급여가 5000니상인 사원을 10번 부서로 이동하는 쿼리문
WHERE SALARY>=5000;     --부서 번호에 옵션을 지정하였으므로 이 뷰를 통해서는 부서번호 변경 불가능

 

9)WITH READ ONLY: 데이터 자체를 읽기 전용으로 VIEW를 통해 변경 불가능

CREATE OR REPLACE VIEW VIEW_READ
AS
SELECT EMPLOYEE_ID, FIRST_NAME, SALARY,DEPARTMENT_ID
FROM EMP01
WHERE DEPARTMENT_ID = 30 WITH READ ONLY;

--SQL 오류: ORA-42399: 읽기 전용 뷰에서는 DML 작업을 수행할 수 없습니다.
UPDATE VIEW_READ
SET SALARY =1000;

-WITH CHECK OPTION:조건에 사용한 컬럼의 값을 수정하지 못하게 함

-WITH READ ONLY: 기본 테이블의 모두를 수정 못하게 함.

 

10)뷰 활용하기

ROWNUM칼럼 : 오라클에서 내부적으로 부여, INSERT문에 의해 입력한 순서에 따라 1 씩 증가되며 값이 지정,레코드가 들어올때 카운팅(내부 컬럼: 행의 번호)

SELECT ROWNUM, EMPLOYEE_ID,FIRST_NAME, HIRE_DATE
FROM EMPLOYEES
ORDER BY HIRE_DATE DESC;
--입사일을 기준으로 내림차순으로 정렬을 하였는데도 해당 행의 ROWNUM은 바뀌지 않는다.
--데이터가 입력된 시점에서 결정되면 다시는 값이 바뀌지 않기 때문이다
--새로운 테이블에 입사일을 기준으로 내림차순으로 정렬한 쿼리문의 결과를 저장하면 
--최근에 입사한 순으로 ROWNUM 칼럼 값이 1부터 부여
CREATE OR REPLACE VIEW VIEW_HIRE
AS
SELECT EMPLOYEE_ID,FIRST_NAME, HIRE_DATE
FROM EMPLOYEES
ORDER BY HIRE_DATE DESC;

SELECT ROWNUM, EMPLOYEE_ID, FIRST_NAME, HIRE_DATE
FROM VIEW_HIRE;

--기본 ROWNUM을 첫번째부터 구할 때 사용 , N번째 부터 N번째까지 구하라는 것일 때 사용X
SELECT ROWNUM, EMPLOYEE_ID, FIRST_NAME, HIRE_DATE
FROM VIEW_HIRE
WHERE ROWNUM<=5;

 


 

SELECT 컬럼
FROM (SELECT~):인라인 뷰 SELECT의 SELECT

SELECT 컬럼, (SELECT 컬럼 FROM 테이블
			 WHERE 외부테이블.컬럼 = 내부테이블.컬럼)
FROM 테이블: 스칼라

 

1]인라인 뷰로 TOP-N 구하기

--인라인 뷰로 TOP-N
SELECT ROWNUM, EMPLOYEE_ID, FIRST_NAME, HIRE_DATE
FROM (SELECT EMPLOYEE_ID, FIRST_NAME, HIRE_DATE --함수X
        FROM EMPLOYEES
        ORDER BY HIRE_DATE DESC)
WHERE ROWNUM <=5;

--서브 쿼리에 이름을 붙여주고 인라인 뷰로 사용 시 서브쿼리의 이름으로 FROM 절에 기술 가능
--같은 서브쿼리가 여러번 사용될 경우 중복 작성을 피할 수 있고 실행속도도 빨라진다는 장점이 있음
WITH TOPN_HIRE
AS
(SELECT EMPLOYEE_ID, FIRST_NAME, HIRE_DATE
FROM EMPLOYEES
ORDER BY HIRE_DATE DESC)

SELECT ROWNUM, EMPLOYEE_ID,FIRST_NAME, HIRE_DATE
FROM TOPN_HIRE;

--EMPLOYEES 테이블과 DEPARTMENTS 테이블을 조회하여 부서 번호와 부서별 최대 급여 및 부서명 출력
SELECT E.DEPARTMENT_ID, D.DEPARTMENT_NAME, E.SALARY
FROM ( SELECT DEPARTMENT_ID, MAX(SALARY) SALARY
        FROM EMPLOYEES 
        GROUP BY DEPARTMENT_ID
        ) E INNER JOIN DEPARTMENTS D
ON E.DEPARTMENT_ID = D.DEPARTMENT_ID;
--EMPLOYEES 테이블에서 급여가 자신이 속한 부서의 평균 급여보다 많이 받는
--사원의 부서번호, 이름, 급여를 출력하는 SELECT
SELECT E.DEPARTMENT_ID, E.FIRST_NAME, E.SALARY
FROM EMPLOYEES E INNER JOIN (SELECT DEPARTMENT_ID, AVG(SALARY) D_SALARY
                            FROM EMPLOYEES
                            GROUP BY DEPARTMENT_ID) D
ON E.DEPARTMENT_ID = D.DEPARTMENT_ID
WHERE E.SALARY > D.D_SALARY
ORDER BY E.DEPARTMENT_ID;

2] 순위 관련 함수들

  • RANK()  : 중복 순위 개수만큼 다음 순위 값을 증가 시킴
  • 형식 : RANK() OVER(ORDER BY 컬럼명 (ASC|DESC)) (AS 별칭)
  • DENSE_RANK() : 중복 순위가 존재해도 순차적으로 다음 순위 값을 표시함 
  • ROW_NUMBER() : 중복값에 관계없이 SEQUENCE(순차적인 순위 값) 값을 반환
--RANK() OVER
SELECT SALARY_RANK, FIRST_NAME, SALARY
FROM (SELECT FIRST_NAME, SALARY,
            RANK() OVER(ORDER BY SALARY DESC) AS SALARY_RANK
        FROM EMPLOYEES
        ORDER BY SALARY DESC)
WHERE ROWNUM <= 5;

--사원테이블에서 80번 부서에 소속된 사원 중에서 입사년도가 가장 최근에 입사한 순부터 
--사원번호, 사원명, 입사일자, 순위를 부여하여 출력
SELECT EMPLOYEE_ID AS 사원번호, FIRST_NAME AS 사원명, HIRE_DATE AS 입사일자,
        RANK() OVER(ORDER BY HIRE_DATE DESC) AS 순위
FROM EMPLOYEES
WHERE DEPARTMENT_ID =80;

--DENSE_RANK() OVER
SELECT 순위, FIRST_NAME, SALARY
FROM (SELECT FIRST_NAME, SALARY, DENSE_RANK() OVER(ORDER BY SALARY DESC) AS 순위
FROM EMPLOYEES
ORDER BY SALARY DESC)
WHERE ROWNUM <=5;

--사원 테이블에서 80번 부서에 소속된 사원 중에서 급여를 가장 많이 받는 순으로
--사원번호, 사원명,급여, 순위 출력
--급여가 가장 높은 순
SELECT EMPLOYEE_ID AS 사원번호, FIRST_NAME AS 사원명, SALARY AS 급여,
        DENSE_RANK() OVER(ORDER BY SALARY DESC) AS 순위 --DENSE_는 중복되었을 때 다음 순서를 그대로 준다.
FROM EMPLOYEES
WHERE DEPARTMENT_ID =80;

'DataBase+SQL' 카테고리의 다른 글

PL/SQL  (0) 2023.06.26
MVIEW, 시퀀스  (0) 2023.06.23
서브 쿼리  (0) 2023.06.21
그룹 함수  (0) 2023.06.20
JOIN(조인)  (0) 2023.06.19

SQL 가로 목록으로 표현

SELECT 
    COUNT(DECODE(DEPARTMENT_ID, 10, 1)) "10번 부서 인원 수", --부서번호 기준 DECODE는 범위값X, 일치하는 값 대상, 값이 같으면 1을 준다고 표현했음
    COUNT(DECODE(DEPARTMENT_ID, 20, 1)) "20번 부서 인원 수", --1명씩 COUNT, 2줄 써도 같은 의미,1의 개수 COUNTING
    COUNT(DECODE(DEPARTMENT_ID, 30, 1)) "30번 부서 인원 수",
    COUNT(DECODE(DEPARTMENT_ID, 40, 1)) "40번 부서 인원 수",
    COUNT(DECODE(DEPARTMENT_ID, 50, 1)) "50번 부서 인원 수",
    COUNT(DECODE(DEPARTMENT_ID, 50, 1)) "50번 부서 인원 수"
FROM EMPLOYEES;


1) 서브 쿼리

하나의 SELECT 문장의 절 안에 포함된 또 하나의 SELECT 문 => 메인쿼리: 서브 쿼리를 포함하고 있는 쿼리문, 서브쿼리: 포함된 또 하나의 쿼리

SELECT 컬럼
FROM 테이블
WHERE 칼럼 연산자 (SELECT~--서브쿼리): 괄호 묶기

실행 순서(우선 순위)는 서브쿼리가 먼저 한 번만 실행.

  • SUB QUERY 부분은 WHERE 절에 연산자 오른쪽에 위치해야 하며 반드시 괄호로 묶어야 한다.
  • 서브 커리에서 SELECT 하지 않은 컬럼은 주 쿼리에서 사용할 수 없다.
  • 특별한 경우(인라인 뷰 등)를 제외하고는 SUB QUERY 절에 ORDER BY 절이 올 수 없다.
  • 서브 쿼리 안에 서브 쿼리가 들어갈 수 있다. 메모리가 허용하는 한 무제한으로 중첩할 수 있다.
  • 단일 행 SUB QUERY와 다중 행 SUB QUERY에 따라 연산자를 잘 선택해야 한다.
--ORA-00907: 누락된 우괄호
--괄호 안에 ORDER BY
SELECT DEPARTMENT_NAME
FROM DEPARTMENTS
WHERE DEPARTMENT_ID=(SELECT DEPARTMENT_ID
FROM EMPLOYEES
WHERE FIRST_NAME='Susan'
ORDER BY DEPARTMENT_ID);
SELECT EMPLOYEE_ID, FIRST_NAME, SALARY, NVL(COMMISSION_PCT,0), TO_CHAR(HIRE_DATE,'YYYY.MM.DD') HIRE_DATE
FROM EMPLOYEES
WHERE DEPARTMENT_ID=(
    SELECT DEPARTMENT_ID
    FROM EMPLOYEES
    WHERE FIRST_NAME='Guy'
)
AND FIRST_NAME <> 'Guy'; --WHERE절 부정 연산자 <>:같지않음

2)단일 행 서브 쿼리

내부 SELECT문장으로부터 오직 하나의 로우(행, ROW)만을 반환 받으며, 단일 행 비교 연산자를 사용

 

1] 서브 쿼리에서 그룹 함수의 사용: 서브 쿼리를 사용하여 평균 급여보다 더 많은 급여를 받는 사원을 검색

SELECT 그룹함수(컬럼) FROM 테이블; --일반 컬럼과 같이 쓰여지지 않았을 경우

SELECT 컬럼 그룹함수(컬럼)
FROM 테이블
GROUP BY 컬럼

--서브 쿼리를 사용하여 평균 급여보다 더 많은 급여를 받는 사원을 검색
SELECT FIRST_NAME, SALARY
FROM EMPLOYEES
WHERE SALARY>(SELECT AVG(SALARY)
              FROM EMPLOYEES);
              
--문제1)EMPLOYEES 테이블에서 LAST_NAME 컬럼에서 Kochhar의 급여보다 많은 사원의 정보를
--사원번호, 이름, 담당업무, 급여를 출력해 주세요
SELECT EMPLOYEE_ID, FIRST_NAME, JOB_ID, SALARY
FROM EMPLOYEES
WHERE SALARY >(SELECT SALARY
               FROM EMPLOYEES
               WHERE LAST_NAME='Kochhar');

--문제2) 가장 적은 급여를 받는 사원의 사번, 이름, 급여를 출력
SELECT EMPLOYEE_ID,FIRST_NAME, SALARY
FROM EMPLOYEES
WHERE SALARY = (SELECT MIN(SALARY) FROM EMPLOYEES);

--추가문제 가장 많은 급여를 받는 사원 이름과 사원의 핸드폰 번호를 출력
SELECT FIRST_NAME, PHONE_NUMBER
FROM EMPLOYEES
WHERE SALARY = (SELECT MAX(SALARY) FROM EMPLOYEES);

--문제3)가장 오랜 기간 근무한 사원의 이름과 이메일, 담당업무, 입사일을 출력
SELECT FIRST_NAME, EMAIL, JOB_ID, HIRE_DATE
FROM EMPLOYEES
WHERE HIRE_DATE = (SELECT MIN(HIRE_DATE) FROM EMPLOYEES);

 

3)다중 행 서브 쿼리 => 비교 연산자 사용 불가능

서브 쿼리에서 반환되는 결과가 하나 이상의 행일 때 사용하는 서브 쿼리

반드시아래와 같은 다중 행 연산자와 함께 사용

종류 의미
IN 메인 쿼리의 비교 조건('=' 연산자로 비교할 경우)이 서브 쿼리의 결과 중에서 하나라도 일치하면 참
ANY 메인 쿼리의 비교 조건이 서브
ALL 메인 쿼리의 비교 조건이 서브 쿼리의 검색 결과와 모든 값이 일치하면 참
EXIST 메인 쿼리의 비교 조건이 서브 쿼리의 결과 중에서 만족하는 값이 하나라도 존재하면 참

 

1] IN 연산자

결과가 2개 이상 구해지는 쿼리문을 서브 쿼리로 기술할 경우에 다중 행 연산자와 함께 사용해갸 한다.

IN 연산자는 메인 쿼리의 비교 조건에서 서브 쿼리의 출력 결과와 하나라도 일치하면 메인 쿼리의 WHERE 절의 참이 되도록하는 연산자

WHERE 컬럼 IN(값,값,값,..)
WHERE 컬럼 = 값 OR 컬럼 = 값 OR...

 

2] ALL연산

ALL 조건은 메인 쿼리의 비교 조건이 서브 쿼리의 검색 결과와 모든 값이 일치하면 참. 찾아진 값에 대해서 AND 연산을 해서 모두 참이면 참, > ALL은 모든 비교값보다 크냐고 묻는 것이 되므로 최대값보다 더 크면 참이 된다. 

따라서 < 로 비교하면 서브쿼리에 MIN 함수를 적용한 효과, > 로 비교하면 서브쿼리에 MAX 함수를 적용한 효과

--30번 소속 직원들 중에서 급여를 가장 많이 받은 사원보다 더 많은 급여를
--받는 사람의 이름, 급여를 출력 (30번 부서 직원 급여들 모두에 대해서 커야 하므로 최대값보다 큰 급여만)
SELECT FIRST_NAME, SALARY
FROM EMPLOYEES
WHERE SALARY > ALL (SELECT SALARY
                    FROM EMPLOYEES
                    WHERE DEPARTMENT_ID =30); --전부 만족

 

3] ANY 연산자(연산자 > ANY 는 찾아진 값에 대해서 하나라도 크면 참)

ANY조건은 메인 쿼리의 비교 조건이 서브쿼리의 검색 결과와 하나 이상만 일치하면 참

> ANY는 찾아진 값에 대해서 하나라도 크면 참이 되므로 찾아진 값 중에서 가장 작은 값 즉, 최소값 보다 크면 참이 된다.

< 연산자로 비교하면 서브쿼리에 MAX 함수를 적용한 효과가 나고, > 연산자로 비교하면 서브 쿼리에 MIN 함수를 적용한 효과가 난다.

 

SELECT FIRST_NAME, SALARY
FROM EMPLOYEES
WHERE SALARY > ANY (SELECT SALARY
                   FROM EMPLOYEES
                   WHERE DEPARTMENT_ID =110);

 

 상호 연관 서브 쿼리: 상호 연관 Sub Query란 Main Query값을 Sub Query에 주고 Sub Query를 수행한 후
그 결과를 다시 Main Query로 반환해서 수행하는 Sub Query를 말한다. 
즉, Main Query와 Sub Query가 서로 데이터를 주고 받는 형태이다.

 

EXISTS 연산자
EXISTS 연산자는 서브 쿼리문에서 주로 사용하며 서브 쿼리의 결과 값이 참이 나오기만 하면
바로 메인 쿼리의 결과 값을 리턴한다.

 

SELECT EMPLOYEE_ID, FIRST_NAME, E.JOB_ID, JOB_TITLE
FROM EMPLOYEES E INNER JOIN JOBS J ON E.JOB_ID = J.JOB_ID
WHERE EXISTS (SELECT *
              FROM JOB_HISTORY --사원에 잇는 정보를 참조해서 가져온다: 상호 연관 서브 쿼리-> 괄호 안에 것만 실행 불가능
              WHERE E.EMPLOYEE_ID = EMPLOYEE_ID)
ORDER BY E.EMPLOYEE_ID;

 

4) 서브 쿼리로 테이블 작성하기

SUB QUERY는 오는 위치에 따라서 그 이름이 다름

--서브쿼리
SELECT 컬럼 -- 메인쿼리
FROM 테이블
WHERE 컬럼 연산자()(SUBQUERY);

--인라인 뷰
SELECT 컬럼
FROM (SELECT~(SUBQUERY));

--스칼라 서브쿼리
SELECT 컬럼명, (SELECT 컬럼(SUBQUERY) FROM 테이블
				WHERE 내부 테이블. 컬럼 = 외부 테이블.컬럼)
FROM 테이블;

--모든 사원의 사원번호, 이름, 관리자 번호, 관리자명을 조회
SELECT EMPLOYEE_ID, FIRST_NAME, MANAGER_ID, NVL((SELECT M.FIRST_NAME FROM EMPLOYEES M
                                             WHERE M.EMPLOYEE_ID = E.MANAGER_ID), '없음') 관리자명
FROM EMPLOYEES E
ORDER BY 1;

 

[문제] EMPLOYEES 테이블에서/ 급여가/ 자신이 속한 부서/의 평균 급여/보다 /많이 받는/ 사원의 부서번호, 이름, 급여를 출력하는 SELECT문을 작성하시오.

--[문제] EMPLOYEES 테이블에서/ 급여가/ 자신이 속한 부서/의 평균 급여/보다 /많이 받는/ 사원의 부서번호, 이름, 급여를 출력하는 SELECT문을 작성하시오.
SELECT E.DEPARTMENT_ID, FIRST_NAME, E.SALARY
FROM EMPLOYEES E
WHERE E.SALARY > (SELECT AVG(SALARY)
                    FROM EMPLOYEES D
                    WHERE D.DEPARTMENT_ID = E.DEPARTMENT_ID)
ORDER BY E.DEPARTMNET_ID, E.SALARY DESC;

 

테이블의 구조만 복사

CREATE TABLE EMP03
AS
SELECT *
FROM EMPLOYEES
WHERE 1=0;

 

5)서브 쿼리를 이용한 데이터 추가

서브 쿼리를 사용하여 INSERT 문장을 작성하며 VALUES 절은 사용하지 않는다.

VALUES 절에 기술하는 자료를 서브 쿼리에서 얻어온다. 단, 서브 쿼리의 값 개수와 INSERT할 테이블의 열 수가 일치해야한다.

 

6)서브 쿼리를 이용한 데이터 수정

 

7)서브 쿼리를 이용한 두 개 이상의 칼럼에 대한 값 변경

UPDATE TABLE_NAME
SET COLUMN_NAME1 = (SUB_QUERY1), COLUMN_NAME2 = (SUB_QUERY2),...
WHERE 조건;

UPDATE TABLE_NAME
SET(COLUMN_NAME1, COLUMN_NAME2) = (SUB_QUERY)
WHERE 조건;

--20번 부서의 부서명과 지역명을 30번 부서의 부서명과 지역명으로 수정.
UPDATE DEPT01
SET DEPARTMENT_NAME = (SELECT DEPARTMENT_NAME
                       FROM DEPT01
                       WHERE DEPARTMENT_ID =30),
    LOCATION_ID = ( SELECT LOCATION_ID
                    FROM DEPT01
                    WHERE DEPARTMENT_ID =30)
WHERE DEPARTMENT_ID =20;

UPDATE DEPT01
SET(DEPARTMENT_NAME, LOCATION_ID) = (SELECT DEPARTMENT_NAME, LOCATION_ID
                                    FROM DEPT01
                                    WHERE DEPARTMENT_ID = 30)
WHERE DEPARTMENT_ID =20;

 

8) 서브 쿼리를 이용한 데이터 삭제

 


외래키, 참조키를 찾아 삭제, 수정하기 위한 쿼리문

--HR 사용자로 생성한 DEPT01 테이블과 참조키(외래키) 설정 테이블 확인
SELECT FK.OWNER, FK.CONSTRAINT_NAME, FK.TABLE_NAME
FROM ALL_CONSTRAINTS FK, ALL_CONSTRAINTS PK
WHERE FK.R_CONSTRAINT_NAME = PK.CONSTRAINT_NAME
AND PK.OWNER = 'HR'
AND FK.CONSTRAINT_TYPE='R'
AND PK.TABLE_NAME = 'DEPT01'
ORDER BY FK.TABLE_NAME;

'DataBase+SQL' 카테고리의 다른 글

MVIEW, 시퀀스  (0) 2023.06.23
뷰(VIEW)  (0) 2023.06.22
그룹 함수  (0) 2023.06.20
JOIN(조인)  (0) 2023.06.19
SELECT문 함수  (2) 2023.06.16


 

NoSuchElement

java.util.NoSuchElementException: 존재하지 않는 것을 가져오려고 할 때 발생

 

Scanner

Scanner에서 이 에러가  발생하는 경우: 더 이상 입력받을 수 있는 값이 없을 때

EX1) 두 수를 입력받고, 합을 출력할 때 수가 하나만 입력되는 경우 // 수가 하나 입력되지 않았을 때

java.util.NoSuchElementException오류

 

더 이상 입력이 없다는 것을 판단할 부분 필요

Scanner에서는 hasNext, hasNextInt, hasNextLong,..

import java.util.*;
class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        while (sc.hasNextInt()) {
            int a = sc.nextInt();
            int b = sc.nextInt();
            System.out.println(a+b);
        }
    }
}

 

StringTokenizer

StringTokenizer에서 에러가 발생하는 경우는 Scanner와 비슷

import java.util.*;
class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String line = sc.nextLine();
        StringTokenizer st = new StringTokenizer(line);
        int sum = 0;
        for (int i=0; i<6; i++) {
            if (st.hasMoreTokens()) {
                sum += Integer.parseInt(st.nextToken());
            }
        }
        System.out.println(sum);
    }
}
import java.util.*;
class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String line = sc.nextLine();
        StringTokenizer st = new StringTokenizer(line);
        int sum = 0;
        while (st.hasMoreTokens()) {
            sum += Integer.parseInt(st.nextToken());
        }
        System.out.println(sum);
    }
}

 

날짜 형태 (YYYY.MM.DD)로 구하기

TO_CHAR(T_DATE, 'YYYY.MM.DD')
 
SELECT TO_CHAR(SUM(SALARY), '$999,999') AS TOTAL --999999는 자릿수
FROM EMPLOYEES;
 
 TO_CHAR: 변환함수, 날짜, 숫자-> 문자변화
 문자->문자 X

SUM 그룹의 누적 합계를 반환
AVG 그룹의 평균을 반환
MAX 그룹의 최댓값을 반환
MIN 그룹의 최솟값을 반환
COUNT 그룹의 총 갯수를 반환

 

SELECT TO_CHAR(SUM(SALARY), '$999,999') AS TOTAL --999999는 자릿수
FROM EMPLOYEES;

SELECT AVG(SALARY) FROM EMPLOYEES;
SELECT ROUND(AVG(SALARY),1) FROM EMPLOYEES; --소수점 한자리 표현
SELECT FLOOR(AVG(SALARY)) FROM EMPLOYEES;   --무조건 소수점자리 잘라낸다 FLOOR

SELECT TO_CHAR(MAX(HIRE_DATE), 'YYYY-MM-DD'), TO_CHAR(MIN(HIRE_DATE),'YYYY-MM-DD') FROM EMPLOYEES;

NULL을 저장한 칼럼과 연산=>NULL

BUT, 그룹함수는 다른 연산자와 달리, 해당 칼럼값이 NULL인 것을 제외하고 계산

그러므로, 결과:NULL (X)

그래서 로우(레코드) 개수 구하는 COUNT 함수는 NULL 값에 대해서 세지 X

 

SELECT COUNT(*), COUNT(EMPLOYEE_ID), COUNT(COMMISSION_PCT) 
--EMPLOYEE_ID: 기본키(NULL 허용X), COMMISSION_PCT(NULL 제외)
FROM EMPLOYEES;

SELECT COUNT(DISTINCT JOB_ID) FROM EMPLOYEES;
--DISTINCT: '중복을 제외한'

칼럼과 그룹 함수를 같이 사용할 때 유의 점

--칼럼과 그룹 함수를 같이 사용할 때 유의할 점
--결과:ORA-00937: 단일 그룹의 그룹 함수가 아닙니다
--00937. 00000 -  "not a single-group group function"
--일반 칼럼과 그룹함수를 같이 쓸 때 오류
--레코드 107개, 작은것 1개 매칭 불가능
SELECT FIRST_NAME, MIN(SALARY) FROM EMPLOYEES;

GROUP BY 절을 사용해 특정 조건으로 세부적인 그룹화

--일반 칼럼명 기준
SELECT 칼럼명 그룹함수(칼럼명)
FROM 테이블명
WHERE 조건문
GROUP BY 칼럼명
------------------------------
SELECT 그룹함수(칼럼)
FROM 테이블;

SELECT DEPARTMENT_ID
FROM EMPLOYEES
GROUP BY DEPARTMENT_ID
ORDER BY DEPARTMENT_ID;
  • 특정 그룹으로 묶어 데이터 집계 시 사용
  • WHERE와 ORDER BY절 사이에 위치
  • 집계(그룹)함수와 함께 사용
  • SELECT 리스트에서 집계(그룹)함수를 제외한 모든 칼럼과 표현식은 GROUP BY 절에 명시
--부서별 최대 급여와 최소 급여 구하기
SELECT DEPARTMENT_ID, MAX(SALARY) "최대 급여", MIN(SALARY) "최소 급여"
FROM EMPLOYEES
GROUP BY DEPARTMENT_ID;

정렬 문장(ORDER BY)에 레코드가 2개 오면 첫번째 레코드를 기준으로 하고 동일 값이 있을 경우 다음 레코드를 기준으로 정렬

--부서별 DEPARTMENT_ID/ 같은 업무JOB_ID/를 담당하는 사원 수를 구하라
SELECT DEPARTMENT_ID, JOB_ID, COUNT(EMPLOYEE_ID)
FROM EMPLOYEES
GROUP BY DEPARTMENT_ID, JOB_ID
ORDER BY DEPARTMENT_ID, JOB_ID;

SELECT DEPARTMENT_ID, TO_CHAR(MIN(HIRE_DATE), 'YYYY.MM.DD') "오래 근무한 사원의 입사일"
FROM EMPLOYEES
WHERE DEPARTMENT_ID='30'
GROUP BY DEPARTMENT_ID;
--WHERE 다음에 GROUP BY

HAVING 조건

SELECT 절에 조건을 사용하여 결과를 제한할 때는 WHERE 절을 사용하지만, 그룹의 결과를 제한할 때는 HAVING절 사용

  • GOUP BY절 다음에 위치해 GROUP BY한 결과를 대상으로 다시 필터를 거는 역할
  • HAVING 다음에는 SELECT 리스트에 사용 했던 집계함수를 이용한 조건을 명시

EX) 평균을 구한 후 결과에 조건 부여

HAVING AVG(SALARY)>=5000

집합 연산자: 여러 개의 SELECT 문을 연결해 또 다른 하나의 쿼리를 만드는 역할을 하는 것

UNION ALL 각 쿼리의 결과 집합의 합집합.
UNION 각 쿼리의 결과 집합의 합집합이다. 중복된 행은 한 줄로 출력된다.
INTERSECT 각 쿼리의 결과 집합의 교집합이다. 중복된 행은 한 줄로 출력된다.
MINUS 앞에 있는 쿼리의 결과 집합에서 뒤에 있는 쿼리의 결과 집합을 뺀 차집합니다. 중복된 행은 한줄로 출력

1)UNION:합집합, 두 개의 데이터 집합이 있으면 각 집합 원소를 모두 포함한 결과가 반환, 중복된 것들은 한번만

SELECT GOODS
FROM EXP_GOODS_ASIA
WHERE COUNTRY = '한국'
UNION
SELECT GOODS
FROM EXP_GOODS_ASIA
WHERE COUNTRY = '일본';

2) UNION ALL: 중복된 항목도 모두 조회

SELECT GOODS
FROM EXP_GOODS_ASIA
WHERE COUNTRY = '한국'
UNION ALL
SELECT GOODS
FROM EXP_GOODS_ASIA
WHERE COUNTRY = '일본';

3)INTERSECT: 교집합. 데이터 집합에서 공통된 항목만 추출

SELECT GOODS
FROM EXP_GOODS_ASIA
WHERE COUNTRY = '한국'
INTERSECT 
SELECT GOODS
FROM EXP_GOODS_ASIA
WHERE COUNTRY = '일본';

4)MINUS: 차집합, 한 데이터 집합을 기준으로 다른 데이터 집합과 공통된 항목을 제외한 결과 추출

SELECT GOODS 
FROM EXP_GOODS_ASIA
WHERE COUNTRY = '한국'
MINUS
SELECT GOODS
FROM EXP_GOODS_ASIA
WHERE COUNTRY ='일본';

5) 집합 연산자의 제한 사항: 집합 연산자로 연결되는 각 SELECT문의 SELECT 리스트의 개수데이터 타입은 일치, 집합 연산자로 SELECT 문을 연결할 때 ORDER BY 절은 맨 마지막 문장에서만 사용

--ORA-01789: 질의 블록은 부정확한 수의 결과 열을 가지고 있습니다.
SELECT GOODS --1개
FROM EXP_GOODS_ASIA
WHERE COUNTRY='한국'
UNION
SELECT SEQ, GOODS --2개
FROM EXP_GOODS_ASIA
WHERE COUNTRY = '일본';

--ORA-01790: 대응하는 식과 같은 데이터 유형이어야 합니다
SELECT SEQ --숫자
FROM EXP_GOODS_ASIA
WHERE COUNTRY ='한국'
UNION
SELECT GOODS --문자
FROM EXP_GOODS_ASIA
WHERE COUNTRY = '일본';

--ORA-00933: SQL 명령어가 올바르게 종료되지 않았습니다
SELECT GOODS
FROM EXP_GOODS_ASIA
WHERE COUNTRY = '한국'
ORDER BY GOODS
UNION 
SELECT GOODS
FROM EXP_GOODS_ASIA
WHERE COUNTRY='일본';

UNION 응용

-- 부서별 같은 업무(직무)를 담당하는 사원의 급여의 합과 사원수
SELECT DEPARTMENT_ID, JOB_ID, COUNT(*), SUM(SALARY)
FROM EMPLOYEES
GROUP BY DEPARTMENT_ID, JOB_ID
ORDER BY DEPARTMENT_ID;

-- 부서별 급여의 합과 사원수
SELECT DEPARTMENT_ID, NULL JOB_ID, COUNT(*), SUM(SALARY)
FROM EMPLOYEES
GROUP BY DEPARTMENT_ID
ORDER BY DEPARTMENT_ID;

-- 전체 사원의 급여의 합과 사원수
SELECT NULL DEPARTMENT_ID, NULL JOB_ID, COUNT(*), SUM(SALARY)
FROM EMPLOYEES
ORDER BY DEPARTMENT_ID, JOB_ID;

--집합 연산자로 표현
SELECT DEPARTMENT_ID, JOB_ID, COUNT(*), SUM(SALARY)
FROM EMPLOYEES
GROUP BY DEPARTMENT_ID, JOB_ID
UNION ALL
SELECT DEPARTMENT_ID, NULL JOB_ID, COUNT(*), SUM(SALARY)
FROM EMPLOYEES
GROUP BY DEPARTMENT_ID
UNION ALL
SELECT NULL DEPARTMENT_ID, NULL JOB_ID, COUNT(*), SUM(SALARY)
FROM EMPLOYEES
ORDER BY DEPARTMENT_ID, JOB_ID;;
ROLLUP
SELECT[조회하고자 하는 부분]
FROM[테이블]
WHERE[테이블에서 GROUP BY 진행전 필터링 조건]
GROUP BY ROLLUP(묶는 기준)->그룹별 결과 뿐만 아니라 총 집계도 해줌
HAVING[GROUP BY 결과 필터링할 조건]

CUBE(exp1, exp2,...): 소계와 전체 합계까지 출력하는 함수, 명시한 표현식 개수에 따라 가능한 모든 조합별로 집계한 결과 반환

SELECT DEPARTMENT_ID, JOB_ID, COUNT(*), SUM(SALARY)
FROM EMPLOYEES
GROUP BY CUBE(DEPARTMENT_ID, JOB_ID)
ORDER BY DEPARTMENT_ID;
SELECT DEPARTMENT_NAME, COUNT(EMPLOYEE_ID)--DEPARTMENT_NAME이 1번, COUNT(EMPLOYEE_ID) 2번
FROM EMPLOYEES E INNER JOIN DEPARTMENTS D
USING(DEPARTMENT_ID)
GROUP BY D.DEPARTMENT_NAME
HAVING COUNT(EMPLOYEE_ID)>=5
ORDER BY 2 DESC;--SELECT 순서

 

'DataBase+SQL' 카테고리의 다른 글

뷰(VIEW)  (0) 2023.06.22
서브 쿼리  (0) 2023.06.21
JOIN(조인)  (0) 2023.06.19
SELECT문 함수  (2) 2023.06.16
무결성 제약(CONSTRAINT) 조건  (0) 2023.06.15

StringTokenizer: 컴마로 구분되는 문자열들을 분리 or 특정 문자에 따라 문자열을 나누고 싶을 때

String :문자열을 Tokenizer: 토큰화하다 => 하나의 문자열을 여러 개의 토큰으로 분리

 

IMPORT

import java.util.StringTokenizer;
import java.util.*;

StringTokenizer 생성자

  1. StringTokenizer st = new StringTokenizer(문자열);   //띄어쓰기 기준으로 문자열 분리
  2. StringTokenizer st = new StringTokenizer(문자열, 구분자);   //구분자를 기준으로 문자열 분리
  3. StringTokenizer st = new StringTokenizer(문자열, 구분자, true/false);   //구분자를 기준으로 분리된 문자열 토큰에 포함 안시킬지(default: false), 시킬지(true)

StringTokenizer 메서드

return 값 method명 역할
boolean hasMoreTokens() 남아있는 토큰이 있으면 true를 리턴, 더 이상 토큰이 없으면 false 리턴
boolean hasMoreElements() hasMoreTokens와 동일한데 엘레먼트보다 토큰으로 된 메서드를 주로 씀
String nextToken() 객체에서 다음 토큰을 반환
String nextToken(String delim) delim 기준으로 다음 토큰을 반환
Object nextElement() nextTokens 메서드와 동일하지만 문자열이 아닌 객체를 리턴
int countTokens() 총 토큰의 개수를 리턴

 

구분자가 여러개

StringTokenizer st = new StringTokenizer(str, ".,");

//.와 , 두 개의 문자를 구분자로 사용해서 분리

=> 하나 문자가 아닌, 여러개의 문자를 구분자로 토큰을 만들 수 있다.

 

Split 는 빈 문자열을 토큰으로 인식하지만, StringTokenizer는 인식하지 않는다.

StringTokenizer는 결과값이 문자열, Split 는 결과값이 문자열 배열 

=> StringTokenizer를 이용하면, 전체 토큰을 반복문을 이용해서 출력해야 한다.

StringTokenizer st = new StringTokenizer(str, ".", true);
int i=1;
while(st.hasMoreTokens()){
System.out.println(i++)+st.nextToken());
}

 

readLine(): BufferedReader의  mothod, BufferedReader에서 읽어들인 값을 String으로 저장

만약, 숫자형으로 다시 변환하고 싶다면 Integer.parseInt() 사용

 

integer 나 char를 string으로 변환

String str = String.valueOf(num);

String 을 Char로 변환

Char ch1 = str.CharAt(0); //str의 길이가 1인 경우
Char[] ch2 = str.toCharArray(); //str의 길이가 2 이상

2번 줄의 char array를 출력하여 확인하고자 하면 다음과 같이 변환하여 출력
Arrays.toString(a1.toCharArray())

백준 반복문, 빠른 A+B

import java.util.*;
import java.io.*;
public class Main {
	public static void main(String args[]) throws IOException {
	BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
	BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
	
	int T = Integer.parseInt(br.readLine());
	
	for(int i=0;i<T;i++) {
		StringTokenizer st = new StringTokenizer(br.readLine());
		int a = Integer.parseInt(st.nextToken());
		int b = Integer.parseInt(st.nextToken());
		bw.write((a+b)+"\n");
	}
	bw.flush();
	}
}

JOIN: 한 개 이상의 테이블에서 데이터를 조회하기 위해서 사용되는 것.

두 개 이상의 테이블을 결합해야만 원하는 결과를 얻을 수 있을 때

 

Equi join 동일 칼럼을 기준으로 조인(inner join simple join)
NinEqui Join 동일 칼럼이 없이 다른 조건을 사용하여 조인
Outer Join 조인 조건에 만족하지 않는 행도 나타낸다.
Self Join 한 테이블 내에서 조인// 마치 서로 다른 테이블인 것 처럼

WHERE절에 명시하는 조건이 FROM절에 명시한 여러 TABLE을 묶는 JOIN조건이 된다. 이러한 JOIN조건은 반도시 묶어야 할 TABLE수보다 하나가 적다. 즉, TABLE 수가 N 개라면 JOIN조건은 N-1이 된다.


1.CARTESIAN PRODUCT OR CROSS JOIN --INNER JOIN

2개 이상의 테이블이 조인될 때 WHERE절에 의해 공통되는 칼럼에 의한 결합이 발생되지 않는 경우 (대부분 사용X, WHERE을 가지지 않아야 해서 경우의 수가 다 나옴)

 

기본적으로 조인은 다음과 같은 규칙을 준수

  1. PRIMARY KEY와 FOREIGN KEY 컬럼을 통한 다른 테이블의 행과 연결
  2. 연결 KEY 사용으로 테이블과 테이블이 결합
  3.  WHERE 절에서 조인 조건을 사용. (조인 조건 개수 = 연결 테이블의 수-1)
  4. 명확성을 위해 칼럼 이름 앞에 테이블명 OR 테이블 별칭을 붙인다. (테이블 별칭.컬럼)

자식 테이블에 외래키(부모키가 중복을 허용하면 안됨.// 기본키 OR 유일키)에 있는 값만이 존재한다고 관계 설정

 

먼저 읽는 테이블을 선행 테이블,뒤에 읽는 테이블을 후행 테이블=> 선행 테이블은 조회할 데이터가 적은 테이블로 선택해야 속도에 유리

 

2.Equi Join(inner join)=> 가장 많이 사용

두 테이블에서 공통적으로 존재하는 칼럼의 값이 일치되는 행을 연결하여 결과를 생성

칼럼명이 같게 되면 혼동이 오기 때문에 칼럼명 앞에  테이블명을 점(.)과 함께 기술

WHERE EMPLOYEES.DEPARTMENT_ID = DEPARTMENTS.DEPARTMENT_ID;
--DEPARTMENT_ID라고만 하면 ORA-00918: 열의 정의가 애매합니다 오류 //테이블이 2개 있는데 어디에 있는 값을 가져올지...
--비교 연산자로 "=" EQUI

 

테이블명이 너무 긴 경우에는 테미블 명에 간단하게 별칭을 부여해서 문장을 간단하게 기술

FROM EMPLOYEES E, DEPARTMENTS D
--이제부터는 테이블명 대신 별칭, 혼합사용X
SELECT E.FIRST_NAME, D.DEPARTMENT_NAME, E.DEPARTMENT_ID
FROM EMPLOYEES E, DEPARTMENTS D
WHERE E.DEPARTMENT_ID = D.DEPARTMENT_ID;

3.Non-Equi Join

조인할 테이블 사이에 칼럼의 값이 직접적으로 일치X

--사원명, 급여, 등급
SELECT E.FIRST_NAME, S.GRADE, E.SALARY
FROM EMPLOYEES E, SALARYGRADE S
WHERE E.SALARY BETWEEN S.MINSALARY AND S.MAXSALARY;

4.Outer Join

조인 조건에 만족하지 않는 행들도 나타내기 위해

사용하는 기호는 (+), 조인 조건에서 정보가 부족한 칼럼명 뒤에 위치하게 하면 된다.

--Inner Join// 일치하는 값만
SELECT E.FIRST_NAME, D.DEPARTMENT_ID, D.DEPARTMENT_NAME
FROM EMPLOYEES E, DEPARTMENTS D
WHERE E.DEPARTMENT_ID = D.DEPARTMENT_ID
ORDER BY D.DEPARTMENT_ID;
--Outer Join //한쪽의 테이블은 다
--부서 테이블의 데이터는 다 가져옴. 사원은 없으니 NULL
SELECT E.FIRST_NAME, D.DEPARTMENT_ID, D.DEPARTMENT_NAME
FROM EMPLOYEES E, DEPARTMENTS D
WHERE E.DEPARTMENT_ID(+) = D.DEPARTMENT_ID;

 

--2007년도 상반기에 입사한 사원
SELECT EMPLOYEE_ID, FIRST_NAME, HIRE_DATE, DEPARTMENT_ID
FROM EMPLOYEES
WHERE HIRE_DATE BETWEEN '2007.01.01' AND '2007.06.30';

--DEPARTMENT_ID가 NULL값인 것이 사라짐. 부서 배정을 안받음
SELECT E.EMPLOYEE_ID, E.FIRST_NAME, E.HIRE_DATE, D.DEPARTMENT_NAME
FROM EMPLOYEES E, DEPARTMENTS D
WHERE E.DEPARTMENT_ID = D.DEPARTMENT_ID AND HIRE_DATE BETWEEN '2007.01.01' AND '2007.06.30';

--부서가 적으므로 부서 쪽에 (+)
SELECT EMPLOYEE_ID, FIRST_NAME, HIRE_DATE, D.DEPARTMENT_NAME
FROM EMPLOYEES E, DEPARTMENTS D
WHERE E.DEPARTMENT_ID = D.DEPARTMENT_ID(+) AND HIRE_DATE BETWEEN '2007.01.01' AND '2007.06.30';

 

5.Self Join

자기 자신과 조인을 맺는 것. 서로 다른 테이블인 것처럼 인식할 수 있도록 하기 위해서 별칭을 사용

SELECT WORK.FIRST_NAME 사원명, MANAGER.FIRST_NAME 매니저명
FROM EMPLOYEES WORK, EMPLOYEES MANAGER
WHERE WORK.MANAGER_ID = MANAGER.EMPLOYEE_ID;
SELECT CONCAT(CONCAT(CONCAT(RPAD(WORK.FIRST_NAME, 11, ' '), '의 매니저는 '), MANAGER.FIRST_NAME), '이다.') AS " 그 사원의 매니저"
FROM EMPLOYEES WORK, EMPLOYEES MANAGER
WHERE WORK.MANAGER_ID = MANAGER.EMPLOYEE_ID;

6.ANSI Join

대부분의 상용 데이터 베이스  시스템에서 표준 언어

다른 DBMS와의 호환성을 위해 ANSI 조인을 사용하는 것이 좋다.

 

1)ANSI Cross Join

SELECT * FROM EMPLOYEES CROSS JOIN DEPARTMENTS;

2)ANSI Inner Join

, 대신 INNER JOIN

JOIN만 쓰면 기본값 INNER JOIN

INNER JOIN - ON-

SELECT * FROM TABLE1 INNER JOIN TABLE2
ON TABEL1.COLUMN1 = TABLE2.COLUMN2

SELECT 컬럼
FROM 테이블 INNER JOIN 테이블
ON 조인 조건식

--2개 연결하고 나머지 하나 연결, 3번째 테이블과 앞에 있는 테이블
SELECT 컬럼
FROM 테이블1 INNER JOIN 테이블2
ON 테이블1.컬럼 = 테이블2.컬럼
INNER JOIN 테이블3
ON 테이블1.컬럼=테이블3.컬럼
--사원 테이블(EMPLOYEES)과 직무 테이블(JOBS)=> 공통 컬럼: JOB_ID
--사원 테이블(EMPLOYEES)과 부서 테이블(DEPARTMENTS)=>공통 컬럼: DEPARMENT_ID
--사원명, 직무ID, 직무명(JOB_TITLE), 부서번호, 부서명
SELECT E.FIRST_NAME, E.JOB_ID, J.JOB_TITLE, E.DEPARTMENT_ID, D.DEPARTMENT_NAME
FROM EMPLOYEES E INNER JOIN JOBS J ON E.JOB_ID = J.JOB_ID
                 INNER JOIN DEPARTMENTS D ON E.DEPARTMENT_ID = D.DEPARTMENT_ID;

-USING: 두 테이블 간의 조인 조건에 사용되는 칼럼이 같다면 ON 대신 USING을 사용 (ON을 써서 명확히 명시)

SELECT * FROM TABLE1 INNER JOIN TABLE2
USING(공통칼럼)

 

4)ANSI Outer Join

부족한 쪽이 아니라 전체를 가져올 TABLE쪽을 기준으로 LEFT, RIGHT를 설정

SELECT * FROM TABLE1 [LEFT|RIGHT|FULL] OUTER JOIN TABLE2 ON 조건문

 

'DataBase+SQL' 카테고리의 다른 글

서브 쿼리  (0) 2023.06.21
그룹 함수  (0) 2023.06.20
SELECT문 함수  (2) 2023.06.16
무결성 제약(CONSTRAINT) 조건  (0) 2023.06.15
테이블에 내용을 추가INSERT, 수정UPDATE, 삭제DELETE하기 위한 DML  (0) 2023.06.14

1.DUAL 테이블과 SQL 함수 분류

함수를 배우기 전에 한 행으로 결과를 출력하기 위한 테이블인 DUAL 테이블에 대해 살펴본다.

 

1)DAUL 테이블

SELECT 컬럼명, 컬럼명

FROM 테이블명

WHERE 조건식

ORDER BY 컬럼(별칭 OR 순번 작성 가능) ASC/DESC

 

SELECT 연산식 FROM 테이블명; (SELECT를 쓸려면 FROM 필요. 테이블이 가지고 있는 레코드 수 만큼 출력)

SELECT 연산식 FROM DUAL; 연산식 함수 확인하기 위한 테이블(한번만 나옴. DUAL: 임시 테이블, 산술 연산이나 가상  칼럼 등의 값을 하나의 레코드로 출력, DUMMY라는 한 개의 칼럼으로 구성)

 

테이블을 만들면 테이블을 만들때 소유주가 있어서 테이블 공통이 아님.(계정 체크 중요 HR(샘플용으로 제공)이면 HR에서만 사용 가능, 다른 사용자가 접근 못함. 권한을 주면 말이 다름. 테이블을 만들 때 사용자 체크)

 

2) 단일 행 함수와 그룹 함수로 SQL 함수 분류

함수를 이용하여 복잡한 질의를 간결하게 표현(함수를 주면 함수를 포함한게 컬럼이름=> 별칭을 줌)

SELECT DEPARTMENT_ID, LOWER(FIRST_NAME), SALARY --LOWER(FIRST_NAME)이 칼럼 이름이 된다.
FROM EMPLOYEES
WHERE DEPARTMENT_ID=30;

 

단일행 함수(행 마다 함수가 적용되어 결과를 반환)

그룹함수(하나 이상의 행을 그룹으로 묶어 연산)

 

2.문자함수

LOWER 소문자로 변환
UPPER 대문자로 변환
INITCAP 첫 글자만 대문자로 나며지 글자는 소문자로 변환
CONCAT 문자의 값을 연결
SUBSTR 문자를 잘라 추출(한글 1BYTE)
SUBSTRB 문자를 잘라 추출(한글 2BYTE)
LENGTH 문자의 길이를 반환 (한글 1BYTE)
LENTRHB 문자의 길이를 반환(한글 1BYTE)
INSTR 특정 문자의 위치값을 반환(한글 1BYTE)
INSTRB 특정 문자의 위치 값을 반환(한글 2BYTE)
LPAD, RPAD 입력 받은 문자열과 기호를 정렬하여 특정 길이의 문자열로 반환
TRIM 잘라내고 남은 문자를 표시
CONVERT CHAR SET을 변환
CHR ASCII코드 값으로 변화
ASCII ASCII코드 값을 문자로 변화
REPLACE 문자열에서 특정 문자를 변경

 

1) 소문자로 변환: LOWER

WHERE LOWER(JOB_ID)='it_prog';

 

2) 대문자로 변환: UPPER

WHERE EMAIL=UPPER('jking');

 

3) 첫 글자만 대문자 나머지 소문자: INICAP

SELECT EMPLOYEE_ID, FIRST_NAME, INITCAP(EMAIL)

 

4)두 문자를 연결: CONCAT

SELECT CONCAT(CONCAT(FIRST_NAME, ' '),LAST_NAME) "사원 이름"
--중복 사용 가능

 

5)문자 길이를 구함: LENGTH/LENGTHB

WHERE LENGTH(FIRST_NAME)=4;

 

6)문자열 일부만 추출: SUBSTR/SUBSTRB //DB시작은 1부터

- 시작위치 인자 값: 음수

뒤 쪽에서부터 세어서 시작위치를 잡음

WHERE SUBSTR(HIRE_DATE,1,2)=03;
WHERE SUBSTR(FIRST_NAME, -1,1)='k';

 

7)특정 문자의 위치를 구하는 INSTR/INSTRB

INSTR(FIRST_NAME, 'e')

 

8)특정 기호로 채우는 LPAD/RPAD =>자릿수를 맞출때 사용

SELECT LPAD('DATABASE',10, ' ') FROM DUAL;

SELECT CONCAT(RPAD(FIRST_NAME, 10,' '), CONCAT('($',CONCAT(SALARY,')'))) AS "사원 정보"

 

9)특정 문자를 잘라내는 TRIM 함수=>공백제거, 방향설정 가능, 앞뒤 공백 제거하고 데이터만 검색

 

SELECT TRIM(LEADING FROM '  ABCD  '), LENGTH(TRIM(LEADING FROM '  ABCD  ')) LT_LEN,
TRIM(TRAILING FROM '  ABCD  ') RT, LENGTH(TRIM(TRAILING FROM '  ABCD  ')) RT_LEN,
TRIM(BOTH FROM '  ABCD  ') BOTH1, LENGTH(TRIM(BOTH FROM '  ABCD  ')) BOTH1,
TRIM('  ABCD  ') BOTH2, LENGTH(TRIM('  ABCD  ')) BOTHLEN2
FROM DUAL;

 

3. 숫자함수

숫자형 데이터를 조작하여 변환된 숫자 값을 반환

 

ABS 절대값을 반환
COS COSINE값을 반환
CEIL 무조건 소수점 아래를 올린다.(절상)
FLOOR 무조건 소수점 아래를 잘라낸다.(절사)
LOG LOG값을 반환
POWER POWER(m,n) m의 n승을 반환
SIGN SIGN(n) n<0이면 -1, n=0이면 0, n>0이면 1을 반환
SIN SINE값을 반환
TAN TANGENT값을 반환
ROUND 특정 자릿수에서 반올림
TRUNC 특정 자릿수에서 잘라낸다.(버림)
MOD 입력 받은 수를 나눈 나머지 값을 반환

 

1)ABS 함수/CEIL 함수/FLOOR 함수

ABS 함수는 절대값을 반환

 

CEIL(n) 무조건 소수점 아래를 올림, FLOOR(n) 함수 무조건 소수점 아래를 잘라냄

 

2) 특정 자릿수에서 반올림: ROUND 함수

ROUND(대상, 표시할 자릿수) //소수점 첫째자리가 1 기준

 

3)특정 자릿수에서 잘라내는 TRUNC 함수

지정한 자릿수 이하를 버린 것

 

4)나머지 값을 반환: MOD

 

4.날짜 함수

DATE형에 사용하는 함수 결과 값으로 날짜 또는 기간

날짜+숫자=>date

날짜-숫자=>date

날짜-날짜=>number(일수)

SYSDATE 시스템 저장된 현재 날짜를 반환
MONTHS_BETWEEN 두 날짜 사이가 몇 개월인지 반환
ADD_MONTHS 특정 날짜에 개원 수를 더한다.
NEXT_DAY 특정 날짜에서 최초로 도래하는 인자로 받은 요일의 날짜를 반환
LAST_DAY 해당 달의 마지막 날짜를 반환
ROUND 인자로 받은 날짜를 특정 기준으로 반올림
TRUNC 인자로 받은 날짜를 특정 기준으로 버림

 

1)현재 날짜를 반환:SYSDATE

-날짜형 데이터는 더하기나 빼기 연산 가능(일자)

2)두 날짜 사이 간격을 계산: MONTHS_BETWEEN

-날짜와 날짜 사이의 개월 수를 구함.

TO_DATE: 문자를 날짜 데이터 타입으로 변화해 주는 함수

3)특정 날짜에 개월수를 더함: ADD_MONTHS

4) 해당 요일의 가까운 날짜: NEXT_DAY

5) 해당 달의 마지막 날짜를 반환:LAST_DAY

6)ROUND 함수의 다양한 적용

7)TRUNC 함수의 다양한 적용

 

5. 변환 함수

자료형을 변환시키고자 할때 사용

TO_CHAR: 날짜형 혹은 숫자형을 문자형으로 변환

TO_DATE: 문자형을 날짜형으로 변환

TO_NUMBER: 문자형을 숫자형으로 변환

 

1)TO_CHAR

DDD: 01~365일

DY: 요일의 약어(월, 화,..)

DL: 현재 일을 요일까지 표시

 

AM,PM / A.M, P.M / HH, HH12 (시간1~12)/ HH24 (24시간)

 

숫자형을 문자형으로 변환

TO_CHAR(숫자, '출력형식')

0 자릿수를 나타내며 자릿수가 맞지 않을 경우 0으로 채움
9 자릿수를 나타내며 자릿수가 맞지 않아도 채우지 않는다.
L 각 지역별 통화 기호를 앞에 표시
--숫자형을 문자형으로 변환하기
SELECT TO_CHAR(123467, 'FM999,999') --123,467
     , TO_CHAR(123467890, 'FM999,999,999') --123,467,890
     , TO_CHAR(123467, 'FML999,999')  --₩123,467
FROM DUAL;

TO_DATE: 날짜형 변환 함수

 

6.일반 함수

NVL 첫 번째 인자로 받은 값이 NULL과 같으면 두 번째 인자 값으로 변경
DECODE 첫 번째 인자로 받은 값을 조건에 맞춰 변경(if와 유사)
CASE 조건에 맞는 문장을 수행한다.(switch와 유사)

 

1)NULL을 다른 값으로 변환: NVL, NVL2

NVL(컬럼 또는 표현식, 대체값)

SELECT FIRST_NAME, SALARY, COMMISSION_PCT, SALARY*NVL(COMMISSION_PCT,0) AS COMMISSION,
SALARY+(SALARY*NVL(COMMISSION_PCT,0)) AS TOTAL, JOB_ID
FROM EMPLOYEES
ORDER BY JOB_ID;

 

NVL2 함수

NVL2(컬럼 또는 표현식, NULL값이 아니면 처리할 구문, NULL 값이면 처리할 구문)

SELECT FIRST_NAME, SALARY, COMMISSION_PCT,
NVL2(COMMISSION_PCT, SALARY+(SALARY*COMMISSION_PCT), SALARY)TOTAL_SAL
FROM EMPLOYEES;
--<문제> 모든 직원은 자신의 상관(MANAGER_ID)이 있자.
--하지만 EMPLOYEES 테이블에 우일하게 상관이 없는 로우가 있는데 그 사원의 MANAGER_ID 칼럼 값이 NULL이다.
--상관이 없는 대표이사만 출력하되 MANAGER_ID 칼럼 값 NULL 대신 CEO로 출력
---01722: 수치가 부적합합니다
SELECT EMPLOYEE_ID, FIRST_NAME, NVL(MANAGER_ID, 'CEO')--MANAGER_ID=숫자타입
FROM EMPLOYEES
WHERE MANAGER_ID IS NULL;

SELECT EMPLOYEE_ID, FIRST_NAME, NVL(TO_CHAR(MANAGER_ID),'CEO')
FROM EMPLOYEES
WHERE MANAGER_ID IS NULL;

2) 선택을 위한 DECODE 함수

SWITCH CASE 문과 같이 여러가지 경우에 대해서 선택할 수 있는 함수

DECODE(표현식, 조건1, 결과1, 조건2, 결과2, 조건3, 결과3, ... 조건절외 나머지 결과n )

 

3)조건에 따라 서로 다른 처리가 가능한 case 함수

CASE WHEN 조건1 THEN 결과1

           WHEN 조건2 THEN 결과2 

           WHEN 조건3 THEN 결과3

           ELSE 결과n

END( AS 별칭) 

NULL ->NOT NULL

유일 해야하는 고유 키를 지정: UNIQUE (UK)

칼럼값은 반드시 존재, 유일: PRIMARY KEY (PK) //NULL 허용X, 중복 허용X, 기본키

해당 칼럼값은 참조되는 테이블의 칼럼에 하나 이상과 일치하도록 하려면 FOREIGN KEY (FK)

1) 무결성 제약 조건의 개념과 종류

데이터 무결성: 데이터베이스 내의 데이터에 대한 정확성, 일돤성, 유효성, 신뢰성을 보장하기 위해 데이터 변경 혹은 수정시 여러가지 제한을 두어 데이터의 정확성을 보증하는 것

 

NOT NULL NULL을 허용하지 않는다.
UNIQUE 중복된 값을 허용하지 않는다. 항상 유일한 값을 갖도록 한다.
PRIMARY KEY NULL을 허용하지 않고 중복된 값을 허용하지 않는다.
NOT NULL 조건과 UNIQUE 조건을 결합한 형태
FOREIGN KEY 참조되는 테이블의 칼럼의 값이 존재하면 허용
CHECK 저장가능한 데이터 값의 범위나 조건을 지정하여 설정한 값만을 허용한다.
DEFAULT 기본값을 설정

 

1)NOT NULL 제약조건

2)UNIQUE 제약조건

UNIQUE: 특정 칼럼에 대해 자료가 중복되지 않게 하는 것, 유일값

--NULL은 값에서 제외되므로 유일한 조건인지를 체크 하는 값에서 제외 //중복해서 들어갈 수 있다.
INSERT INTO EMP02(EMPNO, ENAME, JOB, DEPTNO)
VALUES(NULL, 'JONES', 'MANAGER', 20);

 

3)데이터 딕셔너리(시스템 테이블)

데이터 베이스 자원을 효율적으로 관리하기 위한 다양한 정보를 저장하는 시스템 테이블.

DBA_XXXX 데이터 베이스 관리자만 접근 가능한 객체 등의 정보 조회
ALL_XXXX 자신 계정 소유 또는 권한을 부여받은 객체 등에 관한 정보 조회
USER_XXXX 자신의 계정이 소유한 객체 등에 관한 정보 조회

4)제약조건 확인하기

제약조건(CONSTRAINTS)의 에러 메시지에 대한 정확한 원인을 알기 위해 

P PRIMARY KEY
R FOREIGN KEY
U UNIQUE
C CHECK, NOT NULL //구분하기 위해  SEARCH_CONDITION

5)데이터의 구분을 위한 PRIMARY KEY 제약 조건

식별 기능을 갖는 칼럼은 유일하면서도 NULL 값을 허용하지 말아야 한다.

UNIQUE 제약 조건과 NOT NULL 제약 조건을 모두 갖고 있어야 하는데 두가지 제약을 모두 갖는 것이 PRIMARY KEY.

 

6)참조 무결성을 위한 FOREIGN KEY 제약 조건

참조의 무결성은 두 테이블 사이의 주종 관계에서 설정

먼저 존재해야하는 테이블이 주체가 되는 테이블: 부모 테이블, 참조하는 테이블: 자식 테이블

=부모 키가 되기 위한 칼럼은 반드시 부모 테이블의 기본 키유일 키로 설정되어 있어야 한다.

 

7)CHECK 제약 조건

입력되는 값을 체크하여 설정된 값 이외의 값이 들어오면 오류 메시지와 함께 명령이 수행되지 못하게 하는 것

GENDER VARCHAR2(1) CHECK (GENDER IN('M', 'F')),

2>제약 조건명 지정하기

사용자가 의미 있게 제약 조건명을 명시하여 제약 조건명을 명시하여 제약 조건명만으로도 어떤 제약 조건을 위배 했는지 알 수 있제 지정하는 방법

 

[테이블명]_[칼럼명]_[제약 조건 유형]

 

3>테이블 레벨 방식으로 제약 조건 지정하기

복합키로 기본키를 지정

ALTER TABLE로 제약 조건을 추가

 

4>제약조건 변경하기

1)제약조건 추가하기

테이블 생성이 끝난 후에 제약 조건을 추가하기 위해서는 ALTER TABLE로 추가

2)제약조건 제거하기

 

5>외래키가 설정된 데이터 삭제

ON DELETE CASCADE 부모 테이블의 데이터가 삭제하면 자식 테이블의 데이터도 함께 삭제
ON DELETE SET NULL 부모 테이블의 데이터가 삭제되면 자식 테이블의 값 NULL

+ Recent posts