본문 바로가기

Thinking in Data/[DL] DataSQL

[SQL수다] #003. SQL 스케치 (1)

0. 개요

앞으로 진행되는 단계별로 구분해서 설명하는 것이 어색할 수 있다. 하지만, 이 글은 SQL 개발을 시작하거나 중급으로 넘어가는 개발자들을 위한 것으로, 단계별 구분 동작은 한번쯤 자신의 SQL 작성하면서 어떤 기준으로 어떤 순서로 작성하는지를 한번쯤 되집어 볼 수 있는 기회가 될 수 있다고 본다. SQL은 JAVA, C++, C#, Python 과 같은 응용 프로그램을 개발하는 언어와 달리 RDBMS내 데이터 처리를 요구하는 언어이다. SQL이 나오기 전에는 C/C++,COBOL 과 같은 언어로 데이터베이스를 직접 액세스하여 데이터를 처리하였다. RDBMS가 발표되면서 응용프로그램 언어에서 구현되었던 데이터 처리 로직이 RDBMS에 내장되어 SQL만으로 논리적으로 처리할 수 있도록 설계되었다는 것을 이해하는 것도 도움이 될 수 있을 것이다.

우리는 SQL로 RDBMS에게 필요한 데이터를 요구하기만 하면 된다. 즉, 필요한 데이터에 대해 정확히 제시해야하는 책임은 SQL 작성자에게 있다는 의미이다.

SQL을 잘 개발하는 사람들은 어떤 생각을 갖고 작성하는 것일까? 이 질문에는 SQL syntex를 얼마나 알고 있는지를 묻는 것은 절대 아니다. 다양한 DBMS가 존재하고, DBMS별로 지원하는 기능들의 차이를 고려하면 다소 공부해야할 것이 크게 느껴질 수도 있다. 도서관의 SQL 관련 책들은 SQL syntex 와 함수들의 설명을 바탕으로 SQL 샘플이 가득하다. 하지만, 왠지 다 알고 있는 느낌이고 자신이 부족한 부분을 쉽게 체크되지 않는 것이 현실이라는 것이다.

이런 것들은 SQL 개발의 초보자라면 한번쯤 생각해볼 만한 고민일 수 있는데, 필자의 경험과 고수들의 생각을 들어본 결과, 그 공통점을 찾을 수 있었다. SQL 개발에 능숙한 개발자들은 주어진 문제에 대해 동시에 머릿속으로 데이터 그림을 그린다고 볼 수 있었다. 그들은 자연스럽게 채득한 개념을 설명하는데 어려워 한다. 어떤 이는 SQL을 많이 짜보면 알 것이라고 이야기하곤 한다. 하지만, 당장 직면한 문제를 해결해야하는 초급자들에게는 도움이 되지 않는 답일 뿐이다. 단순히 많이 작성해 보는 것이 틀린 방법은 아니며, 또한, 좋은 방법도 아닌 것 같다. 현실은 SQL을 작성하면서 스스로 어떻게 작성해야 하는지 방법을 찾아가는 노력이 뒷받침되어야 만 얻을 수 있는 개념을 쉽게 알려주지 않는다.

SQL 작성한다는 것은 데이터베이스의 테이블 데이터로 부터 데이터를 그리며 작성하는 것이다.

데이터를 그린다는 것이 무엇인가? 꼬리에 꼬리를 무는 질문이 계속될 것이다.

우리의 목표는 바로 데이터 그리는 연습이다.

기본적인 개념부터 다시 확인하면서, 기본 개념을 단단하게 만들어 보길 바란다.

1. 수학적 Ration = TABLE

RDBM의 태생을 고려하면, 테이블은 수학적으로 관계(Relation) 이라고 정의하고 있다. 테이블의 row 는 튜플(Tuple)이라고 정의하고 있다.

수학적인 관계는 고객명을 갖는 집합, 고객구분을 갖는 집합, 고객형태를 갖는 집합, 고객등급을 갖는 집합, 이렇게 5개의 집합의 조합이라고 설명한다. 현실적으로 다시 보면 컬럼의 고유한 갑들의 조합이라고도 이해할 수 있다.

우리가 사용하는 테이블의 1개의 row 는 5개 집합의 값에 대한 조합 중 하나라는 것이다.

2. 테이블과 데이터셋 개념 정리

테이블은 정확하게 표현하면 디스크상의 파일에 존재하는 상태를 의미한다. INSERT문을 처리하였더라도 테이블에 저장하더라도 commit 전까지는 물리적인 테이블의 파일에 기록되지 않는다. commit 이 아니라 rollback 처리로 이전 상태로 돌릴 수 있기 때문에, UNDO 파일에 이전 상태를 관리하고 있다. 이 부분은 RDBM의 아키텍처를 통해 상세하게 이해할 필요가 있다.

임의의 한 테이블에 10개의 컬럼이 존재한다고 하자, 이 테이블에 필요한 조건(WHERE)으로 테이블의 높이를 조정하고, 필요한 컬럼(폭)을 선택하여 SELECT 문을 작성하였다. 이 때 조회되는 데이터가 테이블이라고 할 수 있는가? 이 데이터는 더 이상 테이블의 데이터가 아니다. SELECT문에 의해 조회된 데이터는 디스크 상태가 아닌 메모리에 올라온 상태이며, 메모리 상태에서 컬럼들의 값 뿐 만아니라 새로운 값을 만들어 새로운 컬럼으로 명시할 수도 있다.

SELECT문에서 FROM 절에 명시된 테이블로 부터 데이터을 읽어온다는 의미를 갖는 것이며, 메모리에 로딩된 데이터는 메모리 상에서 작성자의 의도에 맞게 가동처리된다. 우리는 이 메모리상에 로딩되어 가동된 데이터를 가상셋 또는 논리 데이터라고 할수 있으며 논리적인 데이터셋으로 말할 것이다. 데이터셋은 전체 테이블의 데이터를 나타낼 수도 있으며, 테이블의 일부를 나타낼 수도 있다.

SQL 작성시, 명확한 대상은 소스 데이터와 결과 데이터이다.

주어진 문제에 대한 소스는 항상 제공되는 것은 아니지만, 대부분 소스 데이터는 명확하게 정의되어진다. 소스 데이터로 부터 결과 데이터로의 전환에서는 중간 단계의 데이터셋이 존재한다. 또한, 데이터셋은 WHERE에 의해 ROW 필터링이 완료된 상태의 데이터셋을 의미한다고 가정한다.

 

 

3. SQL Join 기본

RDBMS에서의 조인을 반드시 필요한 요소이다. 다양한 조인이 존재하지만, 기본적인 조인은 바로 Cartesian Product 조인이라고 할 수 있으며, 가장 중요한 조인으로 생각한다.

RDBMS에서의 Cartesian Product 조인은 가장 기본적인 연산이다. 모든 조인은 Cartesian Product 을 기반으로 데이터 처리 성능을 높이기 위해 확장되었을 뿐이다.

 

SELECT A.COL1, B.COL2 
FROM TAB1 A, TAB2 B

 

SELECT A.COL1, B.COL2
FROM TAB1 A, TAB2 B
WHERE A.COL1 = B.COL2
  AND A.COL1 = '1234'

 

 

두 SQL의 차이는 TAB1의 ROW를 선택하기 위한 조건과 조인 조건이 존재하는 것이다.

두번째 SQL 의 처리 흐름도 Cartesian Product 처리 흐름과 차이가 없다. 다만 TAB1의 데이터셋의 크기가 달라지고, 조인 조건에 의해 탐색할 범위가 달라졌을 뿐, 내부 처리 흐름은 차이가 없다.

모든 SQL 처리는 Cartesian Product 처리를 기반으로 처리한다는 것을 이해하는 것이 가장 중요한다. 이 특성을 이용하는 중요한 개념인 데이터셋을 증가시키거나 반복처리(Loop)에 활용하기 때문이다.

OLTP 업무 시스템에서의 SQL은 1~5초 이하의 데이터 액세스를 목적으로 하기 때문에, 적극적인 인덱스 활용으로 SQL은 분석 목적의 SQL에 비하면 쉽다고 볼 수 있다. DA로서 SQL을 다룰 때에는 OLTP성 SQL은 참고자료에 불과하며, 해당 데이터베이스의 데이터를 분석하기 위한 SQL을 주로 다루게 되며, 바로 다양한 데이터 가공 SQL을 요구하게 된다.

주) OLTP 상에서도 특수한 경우에는 수십개의 테이블의 조인에 의해 업무 처리되는 SQL도 존재하며 난이도 높은 경우도 존재함

4. 데이터셋 변화 유형

소스 데이터셋으로 부터 첫번째 타겟이 되는 데이터셋의 형태를 이해하는 것은 단순하지만, 제시된 문제에서 데이터셋의 형태가 어떻게 변화되어지는지를 인지하는 것은 중요하기 때문에, 반드시 이해하고 넘어가야한다고 생각한다.

모든 SELECT 문으로 나올 수 있는 데이터셋의 형태를 확인해 보자. 모든 SELECT의 결과는 높이의 변화로 3가지로 정의될 수 있으며, 폭에 대해서도 확장할 수 있다.

주어진 문제로 부터 목적지인 타겟 데이터셋의 형태가 한번에 변화될 수 있지만, 여러 단계의 데이터셋으로 변환되어 최종 데이터셋을 만들 수도 있다. 이때, 중간 데이터셋이 하나 이상 존재한다면, 한 데이터셋과 다음 데이터셋만 집중하면 된다.

4.1 데이터셋 연산 처리 시 ROW 관점

1) 데이터셋의 크기가 증가되는 경우

 

cartesian product , join, generate_series(1,10), hierarchy (with recursive , connect by)

 

2) 데이터셋 크기가 감소되는 경우

 

group by, where , distinct, minus

 

3) 데이터셋 크기가 동일한 경우

 

분석함수, left outer join

 

여기에서의 SQL 작성시, 소스 데이터셋에서 다음 데이터셋을 만드는데 3가지 유형중 어떤 유형인지를 반드시 이해해야 한다는 점이다.

4.2 데이터셋 연산 처리 시 COLUMN 관점

다음과 같은 테이블이 존재한다면 컬럼 관점에서 증가,유지,감소 되는 경우를 생각해 볼 필요 있다.

 

 

감소, 유지는 테이블에서 필요한 컬럼을 명시함으로서 컬럼 폭이 감소하게 된다.

 

 

다시, 컬럼 3개를 갖는 데이터셋에서 새로운 컬럼을 생성할 수 있다. 컬럼 3개가 전부가 아니며, 주어진 데이터로부터 언제든 새로운 가상 컬럼을 만들어 낼 수 있는 것을 충분히 활용해야 한다.

 

 

SQL 초보자들의 공통점 중, 주어진 테이블에 존재하는 컬럼들 외의 새로운 데이터를 바라보지 못한다. 즉, 가상 컬럼을 도출하는데 어려움을 느끼고 있다는 것이다. 가상 컬럼에서 무궁무진한 데이터를 만들어 낼 수 있다는 것만 이해해도 데이터 가공의 어려움은 낮아질 것이다.

주) DBMS별로 컬럼 개수는 제한이 존재한다. DBMS별로 컬럼 개수를 조사해서 확인할 필요가 있다.

주) 여기에서의 가상 컬럼은 SQL에서 자유롭게 만들어지는 개념이며, DBMS에서 지원하는 Virtual Column의 개념은 활용 목적이 서로 다르다.

 

5. 가공 데이터셋 처리의 기본 이해

데이터셋을 논리적인 데이터 연산 후의 메모리상의 데이터를 의미한다고 정의했다.

기본 개념을 이해하는데 복잡한 데이터가 필요하지 않으며, 앞으로도 기본 개념을 이해하는데 기본 데이터를 스스로 만들어 확인하는 작업을 수행해야 한다. 가공 데이터셋은 주어진 소스 데이터셋이 어떻게 가공되어야 결과셋을 만들 수 있는지를 이해하는 것이 가장 중요하다.

5.1 데이터셋 가공 : 행 레벨 확장

다음 데이터셋을 만들기 위해 SQL을 어떻게 작성할지 생각해 보자.

분석) 주어진 소스는 없고 결과만 제시되어 있다. 데이터 특징은 첫번째 컬럼은 1부터 순차적인 값이고, 두번째 컬럼은 그 값의 누적 값이다. 목적은 누적 데이터를 만들어내는 것을 이해하였다. 먼저, 1부터 10까지 만들어낼 수 있는가? 그리고 그 값으로 부터 누적된 값을 어떻게 만들 것인지, 두 단계로 구분될 수 있다.

 

 

/* postgresql */
SELECT row_number() over() val 
FROM pg_catalog.pg_tables
limit 10;

 

중요한 것은 1~10 값이 필요한 것이다. 10개 이상의 row를 갖는 테이블에서 row_number() 함수를 이용하여 1~10을 구할 수 있다.

 

SELECT ROWNUM VAL FROM ALL_TABLES WHERE ROWNUM <= 10;

 

Oracle 의 경우, ROWNUM 예약어를 사용하여 1~10 값을 구할 수 있다.

Postgresql에서는 generate_series함수가 존재하기도 한다. SQL에서 row를 증가시키는 경우가 많다는 것을 반영하고 있다고 생각할 수 있다.

 

/* postgresql */
SELECT generate_series(1,10) VAL;

 

Oracle에 익숙한 개발자들은 connect by를 즐겨 사용한다.

 

/* Oracle */
SELECT LEVEL VAL
FROM DUAL
CONNECT BY LEVEL <= 10;

 

이제 1~10까지의 값을 만들어내는 것은 이해했다.

1~10까지의 값으로 부터 누적값을 어떻게 만들어낼 것인가? 이미 학습한 사람의 경우, 윈도우함수 또는 분석함수라고 하는 sum() over () 함수를 떠올릴 수 있을 것이다. 우리의 학습 목표는 데이터셋의 가공되는 데이터를 그릴수 있어야 하기 때문에 분석함수를 제외시켜보자.

이제 남은 것은 중간 데이터를 어떻게 그릴 수 있는가?

아래 그림과 같이 1~10 의 값을 중간 가공 집합을 만들어 최종 결과 데이터셋을 만들 수 있다.

바로, 중간 가공 집합을 만들어내는 것이 핵심이다.

 

 

중간 데이터셋 확인하기 위하여 다음과 같이 SQL을 작성할 수 있다. 이 SQL 외에 다른 DBMS에서의 SQL도 고려해볼 수 있다.

 

/* Postgresql */
SELECT T.VAL,C.VAL
FROM (SELECT generate_series(1,10) VAL) T 
    ,(SELECT generate_series(1,10) VAL) C
WHERE T.VAL >= C.VAL
ORDER BY 1,2
;

 

최종 결과 데이터셋을 구하는 SQL은 다음과 같다.

 

/* PostgreSQL */
SELECT T.VAL,SUM(C.VAL) SUM_VAL
FROM (SELECT generate_series(1,10) VAL) T 
    ,(SELECT generate_series(1,10) VAL) C
WHERE T.VAL >= C.VAL
GROUP BY T.VAL
;

 

다음은 Oracle 버전의 SQL 이다.

 

/* Oracle */
SELECT T.VAL,SUM(C.VAL) SUM_VAL
FROM (SELECT LEVEL VAL FROM DUAL CONNECT BY LEVEL <=10) T
    ,(SELECT LEVEL VAL FROM DUAL CONNECT BY LEVEL <=10) C 
WHERE T.VAL >= C.VAL
GROUP BY T.VAL
;

 

5.2 데이터셋 가공 : 컬럼 레벨 확장

위 SQL을 참조하면 다음 데이터셋도 가능할 것을 판단된다.

 

 

위 데이터셋의 1~10 값은 5.1에서 사용한 데이터셋을 활용하자. 소스 데이터셋이 준비되었다면, 타겟 데이터셋은 1에서 시작해서 VAL 값 까지 순차적인 값을 갖고 있다. 중요한 것은 소스 데이터셋 1~10 을 갖는 row는 유지되면서 컬럼 레벨로 확장되었다는 것이다. 중간 데이터셋을 고려하지 않더라도 컬럼 확장을 위해서 필요한 처리가 무엇인지를 바로 떠올라야 한다.

첫번째 소스 데이터셋의 VAL 마다 필요한 값을 ROW 관점에서 생성하는 두번째 중간 데이터셋을 만든 후, 세번째 데이터셋에서 컬럼 레벨로 확장하고자 한다. 컬럼 레벨로 펼처진 데이터셋에서 데이터의 row가 축소 됨으로 group by 로 최종 데이터셋을 만들 수 있다. 이 과정의 데이터 셋을 그리면 아래와 같다.

 

 

데이터셋 가공 절차에서 두번째 데이터셋은 5.1 과 동일한 데이터셋을 활용하고 있으며, 세번째 데이터넷에서 컬럼을 확장하기 위해선 CASE 문이 필요하다는 것을 알 수 있다. Oracle이라면 DECODE 함수를 사용하였을 것이다.

 

/* postgresql 세번째 데이터셋 */
SELECT T.VAL
      ,CASE WHEN C.VAL =  1 THEN C.VAL END AS S01
      ,CASE WHEN C.VAL =  2 THEN C.VAL END AS S02
      ,CASE WHEN C.VAL =  3 THEN C.VAL END AS S03
      ,CASE WHEN C.VAL =  4 THEN C.VAL END AS S04
      ,CASE WHEN C.VAL =  5 THEN C.VAL END AS S05
      ,CASE WHEN C.VAL =  6 THEN C.VAL END AS S06
      ,CASE WHEN C.VAL =  7 THEN C.VAL END AS S07
      ,CASE WHEN C.VAL =  8 THEN C.VAL END AS S08
      ,CASE WHEN C.VAL =  9 THEN C.VAL END AS S09
      ,CASE WHEN C.VAL = 10 THEN C.VAL END AS S10
FROM (SELECT generate_series(1,10) VAL) T 
    ,(SELECT generate_series(1,10) VAL) C
WHERE T.VAL >= C.VAL
ORDER BY 1
;

 

컬럼을 CASE 또는 DECODE 로 확장한 다음, GROUP BY로 축소하여 원래 ROW의 형태를 유지하게 하였다.

 

SELECT T.VAL
      ,MAX(CASE WHEN C.VAL =  1 THEN C.VAL END) AS S01
      ,MAX(CASE WHEN C.VAL =  2 THEN C.VAL END) AS S02
      ,MAX(CASE WHEN C.VAL =  3 THEN C.VAL END) AS S03
      ,MAX(CASE WHEN C.VAL =  4 THEN C.VAL END) AS S04
      ,MAX(CASE WHEN C.VAL =  5 THEN C.VAL END) AS S05
      ,MAX(CASE WHEN C.VAL =  6 THEN C.VAL END) AS S06
      ,MAX(CASE WHEN C.VAL =  7 THEN C.VAL END) AS S07
      ,MAX(CASE WHEN C.VAL =  8 THEN C.VAL END) AS S08
      ,MAX(CASE WHEN C.VAL =  9 THEN C.VAL END) AS S09
      ,MAX(CASE WHEN C.VAL = 10 THEN C.VAL END) AS S10
FROM (SELECT generate_series(1,10) VAL) T 
    ,(SELECT generate_series(1,10) VAL) C
WHERE T.VAL >= C.VAL
GROUP BY T.VAL
ORDER BY T.VAL
;

 

5.3 데이터셋 가공 : 행/컬럼 레벨 확장

 

다음 데이터셋은 좀더 심플하면서 자주 사용되는 패턴이다.

 

 

행으로 존재하는 데이터를 열로 변경하고자 하는 의도를 확인할 수 있다.

먼저 체크할 것은 주어진 1~10의 10개의 row가 하나의 row로 변환되었다는 점을 이해해야 한다. 데이터셋의 row가 축소되었고, column은 확장된 되었다는 것을 이해해야 한다.

 

 

두번째 데이터셋에서는 컬럼을 확장하는 작업만 수행한다.

 

/* postgresql */
SELECT CASE WHEN T.VAL =  1 THEN T.VAL END AS S01
      ,CASE WHEN T.VAL =  2 THEN T.VAL END AS S02
      ,CASE WHEN T.VAL =  3 THEN T.VAL END AS S03
      ,CASE WHEN T.VAL =  4 THEN T.VAL END AS S04
      ,CASE WHEN T.VAL =  5 THEN T.VAL END AS S05
      ,CASE WHEN T.VAL =  6 THEN T.VAL END AS S06
      ,CASE WHEN T.VAL =  7 THEN T.VAL END AS S07
      ,CASE WHEN T.VAL =  8 THEN T.VAL END AS S08
      ,CASE WHEN T.VAL =  9 THEN T.VAL END AS S09
      ,CASE WHEN T.VAL = 10 THEN T.VAL END AS S10
FROM (SELECT generate_series(1,10) VAL) T 
ORDER BY T.VAL
;

 

세번째 데이터셋은 하나의 ROW로 축소되므로 GROUP BY 처리하여 한 ROW로 만들어 준다. 하나의 ROW로 만들어야 하기 때문에 GROUP BY 할 기준 컬럼이 필요없다. GROUP BY를 생략 하였지만, 만약 필요하다면 GROUP BY 1 로 처리해도 문제 없다.

 

/* PostgreSQL */
SELECT MAX(CASE WHEN T.VAL =  1 THEN T.VAL END) AS S01
      ,MAX(CASE WHEN T.VAL =  2 THEN T.VAL END) AS S02
      ,MAX(CASE WHEN T.VAL =  3 THEN T.VAL END) AS S03
      ,MAX(CASE WHEN T.VAL =  4 THEN T.VAL END) AS S04
      ,MAX(CASE WHEN T.VAL =  5 THEN T.VAL END) AS S05
      ,MAX(CASE WHEN T.VAL =  6 THEN T.VAL END) AS S06
      ,MAX(CASE WHEN T.VAL =  7 THEN T.VAL END) AS S07
      ,MAX(CASE WHEN T.VAL =  8 THEN T.VAL END) AS S08
      ,MAX(CASE WHEN T.VAL =  9 THEN T.VAL END) AS S09
      ,MAX(CASE WHEN T.VAL = 10 THEN T.VAL END) AS S10
FROM (SELECT generate_series(1,10) VAL) T 
;

 

5.4 의도적인 데이터 복제

TAB1에 대하여 해당 ROW를 유지 하면서 마지막 ROW에 합계를 구하고자 한다. TAB1에 대하여 가상 데이터셋에서 GROUP BY를 위한 가상 컬럼값을 만들어 내는 것이 핵심이다. SQL 작성 전에 주어진 테이블로 부터 가상 컬럼에 의해 해결되는 중간 데이터셋을 이해해야 한다.

 

 

COPY_T 테이블을 직접 생성한 후 데이터 복제에 활용할 수 있다. 데이터를 복제하면서 가상 컬럼을 식별하는 것으로 COPY_T의 값으로 판단하고 있다. COPY_T 의 값이 1 이면, 원래 값으로, 2이면 sum 처리할 목적으로 ‘total’ 값으로 지정하였다.

 

5.5 ROW to COLUMN 변환

데이터 가공 처리시, 자주 활용되는 패턴 처리가 ROW2COLUMN, COLUMN2ROW 변환이다. 두 변환에 대한 정의가 다소 차이가 있을 수 있으니, 개인별로 정의할 필요가 있다.

필자는 주어진 데이터셋의 한 ROW의 하나 이상의 컬럼을 하나의 컬럼으로 관리하고자 할 때, 다르게 표현하면 하나의 행을 컬럼으로 변환한다고 정의하고 있다. 크게 보면 행이 컬럼으로 변환된다고 할 수도 있지만, 다르게 보면 하나 이상의 컬럼이 하나로 통합되는 처리에도 활용될 수 있기 때문에, 데이터 처리되는 기준에 따라 해석할 수 있어야 한다.

데이터셋의 형태가 하나의 ROW 에서 컬럼 개수 만큼 ROW 증가하는 형태이다. 따라서, 데이터 ROW 증가 되는 형태로 JOIN에 의해 데이터를 증가 시킬 수 있다. 또한, 4개의 컬럼이 하나의 컬럼으로 축소됨으로 CASE 문이 필요함을 이해할 수 있다.

 

 

4개의 컬럼이 하나의 컬럼으로 변환될 때에는 CASE 문 하나로 한 컬럼으로 변환함을 알 수 있다. 데이터가 증가되는 형태이므로 GROUP BY 처리는 없다.

 

CASE WHEN COPY_T.RNO = 1 THEN TAB1.C1
     WHEN COPY_T.RNO = 2 THEN TAB1.C2
     WHEN COPY_T.RNO = 3 THEN TAB1.C3
     WHEN COPY_T.RNO = 4 THEN TAB1.C4
END  AS "C"

 

5.6 COLUMN to ROW 변환

하나의 컬럼을 하나의 행으로 전환하고자 하는 형태이다. 이 변환을 다르게 설명하면,한 컬럼을 하나 이상의 컬럼으로 분할되는 처리로 해석될 수 있다.

 

한 컬럼이 4개로 분할 됨에 따라 CASE 문이 4개가 필요하고, 4개의 ROW 가 1개로 축소 됨으로 GROUP BY가 필요함으로, ROW to COLUMN 변환에서는 없었던 MAX 함수 처리가 필요하다는 것을 이해해야 한다.

 

SELECT ... 
      ,MAX(CASE WHEN COPY_T.RNO=1 THEN TAB1.C END) AS "C1"
      ,MAX(CASE WHEN COPY_T.RNO=2 THEN TAB1.C END) AS "C2"
      ,MAX(CASE WHEN COPY_T.RNO=3 THEN TAB1.C END) AS "C3"
      ,MAX(CASE WHEN COPY_T.RNO=4 THEN TAB1.C END) AS "C4"
FROM 
GROUP BY ...

 

5.7 정리

이번 장에서는 SQL 개발시, 소스 데이터셋으로 부터 타겟 데이터셋을 만들기 위해, 가공된 데이터셋들이 소스와 타겟 데이터셋 사이에 존재할 수 있다는 개념을 이해시키고자 하였다. 또한, 데이터 가공에 필요한 개념에서 ROW 관점에서의 데이터 증가 시키는 개념과 COLUMN 관점에서의 확장하는 방식을 설명하였다. SQL 처리시 ROW와COLUMN 으로 데이터를 어떻게 가공할 것인지 이해하는 연습이 필요하며, 기본적인 ROW를 확장하는 방식에선 Cartesian Product 개념을 바탕으로 데이터을 증가 시킬 수 있으며, COLUMN에 대하여 통합하거나 분할 할때에는 CASE 문이 반드시 필요한 것을 이해하였다.

여기에서 중요한 것은 SQL을 기억하는 것이 아니다. 데이터가 어떻게 가공되어지는지 중간에 위치한 가공된 데이터셋을 기억해야 하는 것이 중요한 것이다.

주어진 문제에 대해 막힌다면, 중간 가공 집합을 이해하지 못하고 있는 것이 분명하다.

데이터 가공 처리를 다시 확인해야 한다. 정확하게 이해하지 못한다면 결과 데이터에 대해 자신할 수 없다. 검증에서 실패하면서도 중간 가공 집합에서 잘못된 것을 찾을 수 없다. 시간만 지나가는 것이다.

데이터셋이 어떤 패턴으로 가공되는지를 이해한다면, 복잡한 데이터 처리를 요구하는 문제에서도 단계별로 데이터셋의 가공되는 데이터를 찾아가면서 문제를 해결할 수 있다.

데이터셋을 가공하는 패턴에 대해 자신만의 기준을 정의하고, 명확히 이해하고 있는지 반드시 체크해야 한다. 눈으로 바라본다고 문제가 나타나지 않는다. 데이터로 확인하고 데이터 정리하고 데이터로 완성해야 한다. 이 과정이 습관화될 때 쯤 머릿속에 데이터가 그려질 것이다.

 

6. SQL 개발에 필요한 지식들

오라클 아키텍처 관련

전문가를 위한 오라클 데이터베이스 아키텍처

https://product.kyobobook.co.kr/detail/S000001550645 (절판)

원서) https://product.kyobobook.co.kr/detail/S000031318841