본문 바로가기

Data Science/SQLP

[프로그래머스] 3. GROUP BY

반응형

GROUP BY에는 ALIAS를 사용하지 못한다.

-> SELECT문이 처리 순서가 가장 마지막이기 때문에


: WHERE -> GROUP BY -> HAVING

-> 처리 속도를 위해 불필요한 조건들은 WHERE절에서 제외시키는 것이 좋다.


계층형 쿼리

더보기

계층형 질의

가상 칼럼 설명
LEVEL 루트 데이터이면 1, 그 하위 데이터이면 2.
리프 데이터까지 1씩 증가한다.
CONNECT_BY_ISLEAF 전개 과정에서 해당 데이터가
리프 데이터이면 1, 그렇지 않으면 0
CONNECT_BY_ISCYCLE 전개 과정에서 자식을 갖는데,
조상으로 존재시 1, 그렇지 않으면 0.
여기서 조상은 
자신으로부터 루트까지 경로에 존재하는 데이터
CYCLE 옵션 사용시에만 사용 가능

 

함수 설명
SYS_CONNECT_BY_PATH 루트 데이터부터 현재 전개할 데이터 까지 경로 표시
SYS_CONNECT_BY_PATH(칼럼, 경로분리자)
CONNECT_BY_ROOT 현재 전개할 데이터의 루트 데이터 표시 [단항 연산자]
CONNECT_BY_ROOT 칼럼

 

부모와 자식 관계 설명
CONNECT BY PRIOR 자식 = 부모 관리자 -> 사원 방향을 전개
순방향 전개
CONNECT BY 부모 = PRIOR 자식
CONNECT BY PRIOR 부모 = 자식   사원 -> 관리자 방향을 전개
역방향 전개
CONNECT BY 자식 = PRIOR 부모
SELECT LEVEL, LPAD(' ', 4 * (LEVEL-1)) || 사원 사원, 
관리자, CONNECT_BY_ISLEAF ISLEAF 
FROM 사원 START WITH 관리자 IS NULL 
CONNECT BY PRIOR 사원 = 관리자;
LEVEL  사원    관리자 ISLEAF
----- -------- ----- ------
1      A                 0 
2       B       A        1 
2       C       A        0 
3        D      C        1 
3        E      C        1
SELECT LEVEL, LPAD(' ', 4 * (LEVEL-1)) || 사원 사원, 
 관리자, CONNECT_BY_ISLEAF ISLEAF 
FROM 사원 START WITH 사원 = 'D' 
CONNECT BY PRIOR 관리자 = 사원;
LEVEL  사원    관리자 ISLEAF
----- -------- ----- ------
1      D         C      0 
2       C        A      0 
3         A             1

   
START WITH 칼럼 [='~' | IS NULL] 상하 관계 질의 시작 칼럼 설정
CONNECT_BY_LEVEL 연속된 숫자 조회시 사용
--1~10까지 연속된 숫자 조회

SELECT LEVEL AS NO
FROM DUAL

CONNECT BY LEVEL <=10
--2021년 1월부터 12월까지 출력
SELECT '2020 년 '||LPAD(LEVEL, 2, 0)||'월' AS NO
    FROM DUAL
CONNECT BY LEVEL <=12
--특정 날짜 구간으로 조회
SELECT TO_DATE('20200701', 'YYYYMMDD') + (LEVEL-1) AS DT
FROM DUAL CONNECT BY LEVEL <= (TO_DATE('20200717', 'YYYYMMDD') - TO_DATE('20200701', 'YYYYMMDD')) + 1 
---------------------------------------------------------------------------------------------
WITH WD AS
( SELECT TO_DATE('20200701', 'YYYYMMDD') 
AS FROM_DT -- 시작일자 , 
TO_DATE('20200717', 'YYYYMMDD') AS TO_DT -- 종료일자 
FROM DUAL ) 
SELECT DT, DT2 
FROM ( SELECT TO_CHAR(FROM_DT + (LEVEL-1), 'YYYYMMDD') AS DT , 
TO_CHAR(FROM_DT + (LEVEL-1), 'YYYY-MM-DD') AS DT2 
FROM WD CONNECT BY LEVEL <= (TO_DT - FROM_DT) + 1 )
--데이터 복제 [카테시안 곱 이용]

SELECT Y.NO, X.*
FROM(
    SELECT *
    FROM EMP e
    )X, (
         SELECT LEVEL AS NO
         FROM DUAL
         CONNECT BY LEVEL<=2
         )Y
         ORDER BY ENAME,JOB,NO

 


CASE문과 DECODE

더보기

DECODE와 CASE의 특징

DECODE(RAW,'%법','1','2')  -> DECODE로는 LIKE 불가능

-> CASE WHEN RAW LIKE '%법' TEHN '1' ELSE '2'

 

THEN 결과 값 앞에 이어 붙이거나 공백을 넣고 싶을땐

-> CASE WHEN RAW LIKE '%법' THEN ' '||'1' ELSE '2' ||

 

SELECT ANIMAL_ID, NAME,
CASE WHEN SEX_UPON_INTAKE LIKE 'Neutered%' OR SEX_UPON_INTAKE LIKE 'Spayed%' THEN 'O' 
          ELSE 'X'
          END AS 중성화
FROM ANIMAL_INS
ORDER BY ANIMAL_ID

 


무슨 서브쿼리를 써야할까?

1. 선수들 중 포지션이 미드필더인 선수들의 소속팀명 및 선수 정보 출력

2. 선수 정보와 해당 선수가 속한 팀의 평균 키 함께 출력

3. 평균키가 삼성 블루윙즈팀의 평균키보다 작은 팀의 이름과 해당 팀의 평균키 출력

 

 

 

 

 

1. 인라인뷰 (FROM절 서브쿼리)

2. 스칼라 서브쿼리 (SELECT절 서브쿼리)

3. HAVING 서브쿼리


1. SELECT절 서브쿼리 -> 스칼라 서브쿼리 (1로우 1칼럼)

2. FROM절 서브쿼리 -> 인라인뷰 (임시로 생성되는 동적 뷰로 조인과 같은기능)

SELECT PLAYER_NAME 선수명, POSITION 포지션, BACK_NO 백넘버, HEIGHT 키
FROM (SELECT PLAYER_NAME, POSITION, BACK_NO, HEGIHT)
      FROM PLAYER
      WHERE HEIGHT IS NOT NULL
      ORDER BY HEIGHT DESC)
WHERE ROWNUM<=5;

-> 같은 키인 선수가 존재해도 5명이 넘어가면 미출력 -> RANK() 함수 이용

 

3. HAVING 서브쿼리 -> 그룹핑된 결과에 대한 부가적인 조건

SELECT P.TEAM_ID 팀코드, T.TEAM_NAME 팀명, AVG(P.HEIGHT) 평균키
FROM PLAYER P, TEAM T
WHERE P.TEAM_ID = T.TEAM_ID
GROUP BY P.TEAM_ID, T.TEAM_NAME
HAVING AVG(P.HEGIHT) < (SELECT AVG(HEIGHT)
                        FROM PLAYER
                        WHERE TEAM_ID='K02');

 


 

1. 고양이와 개는 몇 마리 있을까

-- 코드를 입력하세요
SELECT ANIMAL_TYPE, COUNT(ANIMAL_TYPE)
FROM ANIMAL_INS
GROUP BY ANIMAL_TYPE
ORDER BY ANIMAL_TYPE ASC

 

2. 동명 동물 수 찾기

-- 코드를 입력하세요
SELECT NAME, COUNT(NAME)
FROM ANIMAL_INS
GROUP BY NAME
HAVING COUNT(NAME)>=2
ORDER BY NAME

 

4. 입양 시각 구하기 (1)

(1) 인라인뷰에서 처리 후, 해당 조건의 출력만 수행

SELECT hour, COUNT(*)
FROM
(
SELECT TO_NUMBER(TO_CHAR(datetime,'HH24')) as hour
FROM ANIMAL_OUTS
)
WHERE hour BETWEEN 9 AND 19
GROUP BY hour
ORDER BY hour;

 

(2) SELECT문이 가장 마지막으로 처리하므로, ALIAS사용 못해서 각각 시간변환 필요

SELECT to_char(datetime, 'HH24') as hour, count(*)
FROM ANIMAL_OUTS
where to_char(datetime, 'HH24') between 09 and 19
group by to_char(datetime, 'HH24')
order by hour

 

5. 입양 시각 구하기 (2)

 

보호소에서는 몇 시에 입양이 가장 활발하게 일어나는지 알아보려 합니다.

0시부터 23시까지, 각 시간대별로 입양이 몇 건이나 발생했는지 조회하는 SQL문을 작성해주세요.

이때 결과는 시간대 순으로 정렬

-> 계층형 쿼리 필요 [0시부터 23시를 만들어야 한다]

HOUR COUNT
0 0
1 0
2 0
3 0
4 0
5 0
6 0
7 3
8 1
9 1
10 2
11 13
12 10
13 14
14 9
15 7
16 10
17 12
18 16
19 2
20 0
21 0
22 0
23 0

정답

SELECT HOUR, COUNT(*)-1 COUNT
FROM
(
(SELECT TO_NUMBER(TO_CHAR(DATETIME, 'HH24')) HOUR FROM ANIMAL_OUTS)
UNION ALL
(SELECT LEVEL-1 FROM DUAL CONNECT BY LEVEL <=24)
)
GROUP BY HOUR
ORDER BY HOUR

 

(0) 계층형쿼리 + 외부조인

SELECT A.HOUR, COUNT(B.DATETIME) AS COUNT
  FROM (SELECT LEVEL-1 AS HOUR
        FROM DUAL
        CONNECT BY LEVEL <=24) A
        LEFT JOIN 
    	ANIMAL_OUTS B
        ON A.HOUR = TO_NUMBER(TO_CHAR(B.DATETIME,'HH24'))
GROUP BY A.HOUR
ORDER BY A.HOUR

 

다른 풀이들

(1) WITH문으로 계층형쿼리를 만들고, TO_CHAR로 시간을 가져온다.

WITH A AS(
        SELECT LEVEL-1 AS HOUR
    	FROM DUAL
    	CONNECT BY LEVEL <= 24
    	), 
    	B AS(
    	SELECT TO_CHAR(DATETIME, 'HH24') AS HOUR
    	FROM ANIMAL_OUTS
        )
SELECT A.HOUR AS HOUR, COUNT(B.HOUR) AS COUNT
FROM A,B
WHERE A.HOUR = B.HOUR(+)
GROUP BY A.HOUR
ORDER BY A.HOUR

 

(2) 가상테이블 이용

SELECT T.HOUR, 
       (SELECT COUNT(*) 
        FROM ANIMAL_OUTS 
        WHERE T.HOUR = TO_CHAR(DATETIME, 'HH24'))AS COUNT
FROM
(SELECT LEVEL-1 AS HOUR
FROM DUAL
CONNECT BY LEVEL <= 24)T

 

(3) DECODE 이용

SELECT LVL, 
       SUM(DECODE(SUBSTR(COUNT,0,2),LVL,SUBSTR(COUNT,3),0)) COUNT 
FROM
     (SELECT TO_CHAR(DATETIME,'HH24')||COUNT(*) COUNT 
      FROM ANIMAL_OUTS
      GROUP BY TO_CHAR(DATETIME,'HH24')) A,
     (SELECT LEVEL-1 LVL
      FROM DUAL
      CONNECT BY LEVEL <= 24) B
GROUP BY LVL
ORDER BY LVL;

 

(4) WITH문과 외부 조인

with tmp as (
    select level-1 as hour
      from dual
   connect by level < 25 
)

select a.hour
     , nvl(b.count, 0)
  from tmp a
  left outer join
     (
        select to_char(datetime, 'HH24')    as hour
             , count(*)                     as count
          from animal_outs
         group by to_char(datetime, 'HH24')
     ) b
    on a.hour = b.hour
 order by a.hour
;

 

(5) CASE문 이용

SELECT VT.HOUR, CASE WHEN CT.COUNT IS NULL THEN 0 WHEN CT.COUNT IS NOT NULL THEN CT.COUNT END COUNT
FROM (--카운팅
    SELECT TO_CHAR(DATETIME, 'HH24') HOUR, COUNT(TO_CHAR(DATETIME, 'HH24')) COUNT
    FROM ANIMAL_OUTS
    GROUP BY TO_CHAR(DATETIME, 'HH24')
    ) CT,
    (--가상테이블
    SELECT LEVEL-1 HOUR
    FROM DUAL
    CONNECT BY LEVEL <= 24
    ) VT
WHERE VT.HOUR = CT.HOUR(+)
ORDER BY VT.HOUR;

 

 

반응형

'Data Science > SQLP' 카테고리의 다른 글

[프로그래머스] 5. JOIN  (0) 2022.03.07
[프로그래머스] 4. IS NULL  (0) 2022.03.07
[프로그래머스] 2. SUM, MAX, MIN (+ 누적합계)  (0) 2022.03.07
[프로그래머스] 1.SELECT  (0) 2022.03.07
[Oracle] 1. SELECT문 처리과정  (0) 2022.02.06