내가 보려고 정리하는/SQL

조인 JOIN과 서브쿼리 subQuery

보동이용용 2023. 3. 27. 04:48
반응형

JOIN :  관계형 데이터베이스의 특징인 관계(Relatrionship)를 이용한 연산

- 테이블의 정규화 과정을 거치면 자료가 여러 테이블에 분산되어 저장되고 이러한 자료를
   외래키관계를 이용하여 조회할 수 있는 연산을 조인이라 함

- 종류
  . 외부조인(Outer Join), 내부조인(Inner Join)   
  . 일반조인, ANSI JOIN                       
  . 동등조인, 비동등조인, 셀프조인, Cartesian Product 등 


조인을 하려면?

1. 관계파악

2. 누가 부모인가 부모쪽으로 방향 


1. Cartesian Product
 - 조인조건이 없거나 조인조건이 잘못된 경우
 - n행 m열의 테이블과 a행 b열의 테이블이 카타시안 곱이 수행되면 최악의 경우
   (조인 조건이 없는 경우) n x a 행, m+b열의 결과를 반환함
 - 반드시 필요한 경우를 제외하고 수행해서는 안됨

(사용형식-일반조인)
    SELECT 컬럼List
      FROM 테이블명1 [별칭1], 테이블명2 [별칭2] [,테이블명3 [별칭3], ...]
   [WHERE] 조인조건;
   
(사용형식-ANSI조인)  CROSS JOIN = 안시의 카타시안 프로덕트
    SELECT 컬럼List
      FROM 테이블명1 [별칭1]
     CROSS JOIN 테이블명2 [별칭2] [ON 조인조건]
    [CROSS JOIN 테이블명3 [별칭3] [ON 조인조건]]
                   :
   [WHERE] 조인조건;

2. 내부 조인
 - 조인조건을 만족하는 데이터만 결과로 반환하고 조인조건을 만족하지 않는 자료는 무시
 1) 동등조인(Equi Join)
  - 조인조건에 '='연산자를 사용한 조인
  - 대부분의 내부 조인이 이에 속함

 (사용형식-일반조인)
    SELECT 컬럼 list  --별칭.컬럼명 
      FROM 테이블명1 [별칭1],테이블명2 [별칭2][,테이블명3 [별칭3],...]
     WHERE [별칭1].컬럼명=[별칭2].컬럼명  --공통의 컬럼 값이면 조인 가능. 꼭 외래키,기본키 관계가 아니어도 됨.
                        :
       AND
      [AND 일반조건]
     . 사용된 테이블이 n개 일때 조인조건은 적어도 n-1개 이상이어야 함
     . 일반조건과 조인조건은 AND로 연결되며 기술 순서는 실행과 관계없음
     
 (사용형식-ANSI조인)
    SELECT 컬럼 list
      FROM 테이블명1 [별칭1]    --내부조인일때는 크게 상관 없지만 외부조인할때는 웨어절에 조건을 기술하면 내부조인으로 변경되어버림
     INNER JOIN 테이블명2 [별칭2] ON([별칭1].컬럼명=[별칭2].컬럼명 [AND 일반조건1])  --테이블명1과 2는 반드시 직접조인해야한다.
    [INNER JOIN 테이블명3 [별칭3] ON([별칭].컬럼명=[별칭3].컬럼명 [AND 일반조건2])]  --테이블 1,2의 결과와 3이 조인
                        :     
    [WHERE 일반조건]
     . '테이블명1'과 '테이블명2'는 반드시 직접 조인 가능한 관계이어야 함
     . '일반조건1'은 '테이블명1'과 '테이블명2'에 관련된 조건이어야 함 
     . '테이블명3' 부터는 '테이블명1'과 '테이블명2'의 조인 결과와 조인
     . 'WHERE 일반조인':모든 테이블에 공통적으로 부여되는 조건 기술

3. 외부조인
 - 자료의 종류가 부족한 테이블에 NULL 값을 갖는 행을 삽입하여 수행하는 조인

 (일반 외부조인형식)  --많은쪽을 기준으로 하기에 자료가 너무 많아지므로, 되도록 사용 x
     SELECT 컬럼list
       FROM 테이블명1 [별칭1], 테이블명2 [별칭2],...
      WHERE 컬럼명1[(+)]=컬럼명2 --컬러명1이 자료가 부족한 테이블에 속한 경우
                 :
      . 일반 외부조인인 경우 외부조인 연산자 '(+)'를 부족한 자료를 가지고 있는 테이블
        쪽에 붙임
      . 여러 조인조건이 모두 외부조인조건인 경우 모두 외부조인연산자를 사용해야 함
      . 하나의 테이블은 여러 테이블과 동시에 외부조인 될 수 없다.
        즉, A,B,C테이블이 연산에 참여하고 A를 기준으로 B가 학장되고, 동시에 C를 기준으로 --C=B(+)는 안됨
        B가 확장될 수 없음(A(B(+) AND C=B(+)는 허용 안됨) --A=B(+), B=C(+) 이거는 가능 셋다 같아짐
      . 외부조인의 SELECT절에 사용되는 컬럼은 반드시 자료가 많은쪽 컬럼을 사용해야함
      . COUNT함수가 사용될 경우 '*' 대신 해당 테이블의 기본키를 기술하는 것이 안전함
      . 외부조인조건과 일반조건이 동시에 수행되면 외부조인 결과를 반환하지 못함=>서브쿼리나
        ANSI외부조인을 사용해야 함  --2020년 6월 구매한 모든 회원/ 서브쿼리 내부에서 일반조건 해결
        
 (ANSI 외부조인형식)
     SELECT 컬럼list
       FROM 테이블명1 [별칭1]
   LEFT|RIGHT FULL OUTER JOIN 테이블명2 [별칭2] ON(조인조건 [AND 일반조건])
   LEFT|RIGHT FULL OUTER JOIN 테이블명3 [별칭2] ON(조인조건 [AND 일반조건2])
                      :
     [WHERE 일반조건]
    . '테이블명1'과 '테이블명 2'는 반드시 직접 조인될 수 있어야함
    . '테이블명3'은 '테이블명1'과 '테이블명2'의 조인 결과와 조인
    . '일반조건1', '일반조건2'등은 해당 JOIN에 사용된 테이블과 관련된 조건만 기술
    . LEFT : FROM절쪽의 결과가 더 많은 종류를 가지고 있는 경우 사용
    . RIGHT : FROM절쪽의 결과가 더 적은 종류를 가지고 있는 경우 사용
    . FULL : 양쪽 모두 부족한 경우 사용
    . 'WERE 일반조건;' : 외부조인을 수행한 후 조건처리가 되기 때문에 보통 내부조인 결과로
      변환됨
 사용예)2020년 2월 모든 제품별 매입집계를 조회하시오
      Alias는 상품코드,상품명,매입수량합계,매입금액합계
**2020년 2월 매입정보
    SELECT COUNT(*)
      FROM BUYPROD
     WHERE BUY_DATE BETWEEN TO_DATE('20200201') AND LAST_DAY(TO_DATE('20200201'))  
   -------------------------------------------------------------------------------   
    SELECT P.PROD_ID AS 상품코드,  --외부조인 할 때에는 더 큰곳에서의 컬럼을 가져온다.
           P.PROD_NAME AS 상품명,
           SUM(B.BUY_QTY) AS 매입수량합계,
           SUM(B.BUY_QTY*P.PROD_COST) AS 매입금액합계
      FROM BUYPROD B, PROD P
     WHERE B.BUY_DATE BETWEEN TO_DATE('20200201') AND LAST_DAY(TO_DATE('20200201')) 
       AND P.PROD_ID=B.BUY_PROD(+)  --일반조인과 같이 써서 내부조인으로 바뀜. 안시나 서브쿼리로 한다.
     GROUP BY P.PROD_ID, P.PROD_NAME
     ORDER BY 1;
     
(서브쿼리 : 2020년 제품별 매입집계-제품코드, 매입수량, 매입금액)     
     SELECT B.BUY_PROD AS BID,
            SUM(B.BUY_QTY) AS SQTY,
            SUM(B.BUY_QTY*P.PROD_COST) AS SAMT
       FROM BUYPROD B, PROD P
      WHERE P.PROD_ID=B.BUY_PROD
        AND B.BUY_DATE BETWEEN TO_DATE('20200201') AND LAST_DAY(TO_DATE('20200201')) 
      GROUP BY B.BUY_PROD

(메인쿼리)
    SELECT P.PROD_ID AS 상품코드,
           P.PROD_NAME AS 상품명,
           NVL(A.SQTY,0) AS 매입수량합계,
           NVL(A.SAMT,0) AS 매입금액합계
      FROM PROD P, 
           (SELECT B.BUY_PROD AS BID,
                  SUM(B.BUY_QTY) AS SQTY,
                  SUM(B.BUY_QTY*P.PROD_COST) AS SAMT
             FROM BUYPROD B, PROD P
            WHERE P.PROD_ID=B.BUY_PROD
              AND B.BUY_DATE BETWEEN TO_DATE('20200201') AND LAST_DAY(TO_DATE('20200201'))
            GROUP BY B.BUY_PROD)A
     WHERE P.PROD_ID=A.BID(+)
     ORDER BY 1;
      
 (ANSI OUTER JOIN)
     SELECT P.PROD_ID AS 상품코드, 
           P.PROD_NAME AS 상품명,
           NVL(SUM(B.BUY_QTY),0) AS 매입수량합계,
           NVL(SUM(B.BUY_QTY*P.PROD_COST),0) AS 매입금액합계
      FROM BUYPROD B
     RIGHT OUTER JOIN PROD P ON(P.PROD_ID=B.BUY_PROD
           AND B.BUY_DATE BETWEEN TO_DATE('20200201') AND LAST_DAY(TO_DATE('20200201')))
     GROUP BY P.PROD_ID, P.PROD_NAME
     ORDER BY 1;

4. SELF JOIN
 - 하나의 테이블에 2개 이상의 별칭을 부여하여 다른 테이블로 간주한 후 수행하는 조인

사용예)회원테이블에서 '오철희'회원의 마일리지보다 많은 마일리지를 보유한 회원들을 조회하시오.
      Alias는 회원번호,회원명,마일리지 --NON EQUI JOIN,  이혜나 있는 것 없는 것 별칭 따로 써서 조인
      SELECT B.MEM_ID AS 회원번호,
             B.MEM_NAME AS 회원명,
             B.MEM_MILEAGE AS 마일리지
       FROM MEMBER A, MEMBER B
      WHERE A.MEM_NAME='오철희' --일반조건
        AND B.MEM_MILEAGE>A.MEM_MILEAGE; --조인조건
사용예)회원테이블에서 20대 여성회원 중 마일리지가 많은 3명의 회원이 2020년 4월 ~ 6월 까지 
      구매한 집계를 조회하시오.
메인쿼리 : 20대 여성회원의 2020년 4~6 구매집계
    SELECT S. AS 회원번호,
           M.MEM_NAME AS 회원명,
           SUM(C.CART_QTY*D.PROD_PRICE) AS 구매금액집계
      FROM MEMBER M, CART C, PROD D,
           (     ) S
     WHERE S.회원번호=M.MEM_ID
       AND S.회원번호=C.CART_MEMBER
       AND C.CART_PROD=D.PROD_ID
       AND SUBSTR(C.CART_NO,1,6) BETWEEN '202004' AND '202006'
     GROUP BY S.회원번호,  M.MEM_NAME  
       
서브쿼리 : 20대 여성회원 > 마일리지가 많은 상위 3명 회원(서브쿼리 속 서브쿼리,오더바이 순서) 
    SELECT A.MEM_ID AS MID
      FROM (SELECT MEM_ID
              FROM MEMBER
             WHERE TRUNC(EXTRACT(YEAR FROM SYSDATE)-EXTRACT(YEAR FROM MEM_BIR),-1)=20--첫째자리를 잘라버리니 무조건 21
               AND SUBSTR(MEM_REGNO2,1,1) IN ('2','4')
             ORDER BY MEM_MILEAGE DESC) A
     WHERE ROWNUM<=3;

결합
    SELECT S.MID AS 회원번호,
           M.MEM_NAME AS 회원명,
           SUM(C.CART_QTY*D.PROD_PRICE) AS 구매금액집계
      FROM MEMBER M, CART C, PROD D,
           (SELECT A.MEM_ID AS MID
              FROM (SELECT MEM_ID
                      FROM MEMBER
                     WHERE TRUNC(EXTRACT(YEAR FROM SYSDATE)-EXTRACT(YEAR FROM MEM_BIR),-1)=20
                       AND SUBSTR(MEM_REGNO2,1,1) IN ('2','4')
                     ORDER BY MEM_MILEAGE DESC) A
             WHERE ROWNUM<=3) S
     WHERE S.MID=M.MEM_ID
       AND S.MID=C.CART_MEMBER
       AND C.CART_PROD=D.PROD_ID
       AND SUBSTR(C.CART_NO,1,6) BETWEEN '202004' AND '202006'
     GROUP BY S.MID,M.MEM_NAME;

3. DML 명령과 서브쿼리
 - 서브쿼리를 이용하여 데이터의 삽입, 변경, 삭제 수행


 1) 자료 삽입

  (사용형식)
   INSERT INTO 테이블명[(컬럼명,...)]
    서브쿼리                        --서브쿼리 괄호 생략은 INSERT, CREATE만
    . INSERT 문에 사용되는 서브쿼리는 '( )'와 VALUES절을 생략함 --VALUES절 대신 서브쿼리 
    .'(컬럼명,...)'이 사용된 경우 서브쿼리의 SELECT절의 컬럼의 갯수, 순서, 타입이 동일해야 함
사용예)재고수불테이블(REMAIN)에 상품테이블의 자료 중 상품코드, 적정재고를 이용하여
      모든 상품정보를 입력하시오
      [자료]
      REMAIN_YEAR에는 '2020'
      PROD_ID : 상품테이블의 PROD-ID
      REMAIN_J_00 : 상품테이블의 적정재고(PROD_PROPERSTOCK)
      REMAIN_I : 0
      REMAIN_O : 0
      REMAIN_J_99 : 상품테이블의 적정재고(PROD_PROPERSTOCK)
      REMAIN_DATE : '2019/12/31'
      
    INSERT INTO REMAIN(REMAIN_YEAR,PROD_ID,REMAIN_J_00,REMAIN_I,REMAIN_O,
                        REMAIN_J_99,REMAIN_DATE)
      SELECT '2020',PROD_ID,PROD_PROPERSTOCK,0,0,PROD_PROPERSTOCK,TO_DATE('2019/12/31')
        FROM PROD;

 

2) 자료 갱신

  (사용형식)
   UPDATE 테이블명 [별칭]
      SET (컬럼명,...)=(서브쿼리)
   [WHHERE 조건];
   
   .SET 절에 기술된 컬럼의 갯수,순서,타입과 서브쿼리의 SELECT문의 갯수,순서,타입은 일치해야함
   .'WHERE 조건'이 사용되지 않으면 모든 자료가 갱신 대상이 됨(주의를 요함)
 사용예)매입테이블에서 2020년 1월 제품별 매입수량을 구한뒤 재고 수불테이블에
       매입수량을 반영하시오.
       
(서브쿼리 : 2020년 1월 제품별 매입수량)
    SELECT BUY_PROD,
           SUM(BUY_QTY) AS SQTY
      FROM BUYPROD
     WHERE BUY_DATE BETWEEN TO_DATE('20200101') AND TO_DATE('20200131')
     GROUP BY BUY_PROD;
 
(메인쿼리 : UPDATE문)
    UPDATE REMAIN R
       SET (R.REMAIN_I,R.REMAIN_J_99,R.REMAIN_DATE)=
           (SELECT R.REMAIN_I+A.SQTY,R.REMAIN_J_99+A.SQTY,TO_DATE('20200331')
              FROM (SELECT BUY_PROD,
                           SUM(BUY_QTY) AS SQTY
                      FROM BUYPROD
                     WHERE BUY_DATE BETWEEN TO_DATE('20200201') 
                           AND TO_DATE('20200331')
                     GROUP BY BUY_PROD)A
             WHERE A.BUY_PROD=R.PROD_ID)
     WHERE R.PROD_ID IN(SELECT DISTINCT BUY_PROD
                        FROM BUYPROD
                       WHERE BUY_DATE BETWEEN TO_DATE('20200201') 
                             AND TO_DATE('20200331'));

 

 

sub query

: 하나의 SQL 문에 포함되어 있는 또 다른 SQL 문.  (성능은 좋지 않으나 사용방법이 쉽다.)

. 서브쿼리를 괄호로 감싸서 사용한다.
. 서브쿼리는 단일 행 또는 복수 행 비교 연산자와 함께 사용 가능하다.
. 서브쿼리에서는 ORDER BY 를 사용하지 못한다.
서브쿼리가 사용 가능한 곳
1. SELECT 절
2. FROM 절
3. WHERE 절
4. HAVING 절
5. ORDER BY 절
6. INSERT 문의 VALUES 절
7. UPDATE 문의 SET 절

https://mozi.tistory.com/233

 

[DATABASE] 서브쿼리란? 서브쿼리 사용해보기

서브쿼리란? 하나의 SQL 문에 포함되어 있는 또 다른 SQL 문을 말합니다. 서브쿼리 사용시 주의사항 1. 서브쿼리를 괄호로 감싸서 사용한다.2. 서브쿼리는 단일 행 또는 복수 행 비교 연산자와 함께

mozi.tistory.com

 

반응형

'내가 보려고 정리하는 > SQL' 카테고리의 다른 글

[postgreSql] 다중커서 단축키, Array함수  (0) 2024.05.20
DML, DDL, DCL명령  (0) 2022.12.05