퀴즈로 배우는 SQL
[퀴즈] 스터디 가입현황을 보여주는 쿼리를 작성하는 문제입니다. 3 31 99,999+

by 마농 CROSS JOIN LEFT OUTER JOIN ORA-01417 ORA-01719 퀴즈 [2012.04.17]


문제

다음과 같이 테이블이 3개가 있습니다.
  • 1. 스터디 학생 테이블은 s_id가 PK입니다.
  • 2. 스터디 과목 테이블은 c_id가 PK입니다.
  • 3. 스터디마스터테이블은 학생과 과목 테이블을 각각 참조하며 PK는 s_id, c_id, chasu 3개 항목의 조합입니다.

스터디학생(student)
S_ID S_NM
001 기민용
002 이현석
003 김정식
004 강정식

스터디과목(course)
C_ID C_NM
001 Database
002 Java

스터디마스터(study)
S_ID C_ID CHASU
001 001 1
001 001 3
001 002 2
002 001 1
002 001 2
002 001 3
003 002 1
003 002 2
004 001 1

위 3개 테이블을 이용하여 다음과 같은 결과를 도출하는 쿼리를 작성해 보세요.
ID 성명 스터디 1차 2차 3차 참여횟수
001 기민용 Database 2
Java 1
002 이현석 Database 3
Java 0
003 김정식 Database 0
Java 2
004 강정식 Database 1
Java 0

쿼리 작성 조건입니다.
  • - 학생별로 2개행씩(스터티과목수만큼) 조회되어야 합니다.
  • - 스터디 차수별로 가입여부에 '○'표로 표시합니다. 3차까지 표시.
  • - 과목별 참여건수를 표시합니다.
  • - 서브쿼리 없이 작성하세요.

다음 with절을 이용하여 작성해 보세요.
WITH student AS
(
	SELECT '001' s_id, '기민용' s_nm FROM dual
	UNION ALL SELECT '002', '이현석' FROM dual
	UNION ALL SELECT '003', '김정식' FROM dual
	UNION ALL SELECT '004', '강정식' FROM dual
)
, course AS
(
	SELECT '001' c_id, 'Database' c_nm FROM dual
	UNION ALL SELECT '002', 'Java' FROM dual
)
, study AS
(
	SELECT '001' s_id, '001' c_id, 1 chasu FROM dual
	UNION ALL SELECT '001', '001', 3 FROM dual
	UNION ALL SELECT '001', '002', 2 FROM dual
	UNION ALL SELECT '002', '001', 1 FROM dual
	UNION ALL SELECT '002', '001', 2 FROM dual
	UNION ALL SELECT '002', '001', 3 FROM dual
	UNION ALL SELECT '003', '002', 1 FROM dual
	UNION ALL SELECT '003', '002', 2 FROM dual
	UNION ALL SELECT '004', '001', 1 FROM dual
)
-- 이곳에 들어갈 쿼리를 작성해 주세요.

정답

SELECT CASE WHEN b.c_id = '001' THEN a.s_id END AS "ID"
     , CASE WHEN b.c_id = '001' THEN a.s_nm END AS "성명"
     , b.c_nm AS "스터디"
     , MIN(CASE WHEN c.chasu = 1 THEN '○' END) AS "1차"
     , MIN(CASE WHEN c.chasu = 2 THEN '○' END) AS "2차"
     , MIN(CASE WHEN c.chasu = 3 THEN '○' END) AS "3차"
     , COUNT(c.s_id) AS "참여횟수"
 FROM student a
CROSS JOIN course b
 LEFT OUTER JOIN study c
   ON a.s_id = c.s_id
  AND b.c_id = c.c_id
GROUP BY a.s_id, a.s_nm, b.c_id, b.c_nm
ORDER BY a.s_id, b.c_id;

해설

  이 문제는 3개의 테이블을 조인하여 결과를 도출하는 문제입니다.

  위의 문제에 제시된 여러 단서 조항들을 한꺼번에 모두 적용하지 않고 한단계, 한단계 차근차근 접근하면서 문제를 해결해 나가는 방법으로 설명하겠습니다.

1단계. 조인

SELECT a.s_id, a.s_nm, b.c_id, b.c_nm, c.chasu
  FROM student a, course b, study c
 WHERE a.s_id = c.s_id
   AND b.c_id = c.c_id
ORDER BY a.s_id, b.c_id, c.chasu;

S_ID S_NM C_ID C_NM CHASU
001 기민용 001 Database 1
001 기민용 001 Database 3
001 기민용 002 Java 2
002 이현석 001 Database 1
002 이현석 001 Database 2
002 이현석 001 Database 3
003 김정식 002 Java 1
003 김정식 002 Java 2
004 강정식 001 Database 1

2단계. 행을 열로

  조인을 한 후 s_id, c_id 로 그룹핑하여 차수를 옆으로 나열하는 단계입니다.

SELECT a.s_id, a.s_nm, b.c_id, b.c_nm
     , MIN(CASE WHEN c.chasu = 1 THEN '○' END) AS "1차"
     , MIN(CASE WHEN c.chasu = 2 THEN '○' END) AS "2차"
     , MIN(CASE WHEN c.chasu = 3 THEN '○' END) AS "3차"
     , COUNT(c.s_id) AS "참여횟수"
FROM student a
   , course b
   , study c
WHERE a.s_id = c.s_id
  AND b.c_id = c.c_id
GROUP BY a.s_id, a.s_nm, b.c_id, b.c_nm
ORDER BY a.s_id, b.c_id;

  원하던 결과와 매우 유사한 형태의 결과가 나왔습니다.

S_ID S_NM C_ID C_NM 1차 2차 3차 참여횟수
001 기민용 001 Database 2
001 기민용 002 Java 1
002 이현석 001 Database 3
003 김정식 002 Java 2
004 강정식 001 Database 1

3단계. 아우터 조인 시도

  자, 이제 없는 스터디 과목도 나오도록 해야겠습니다. 아우터 조인을 이용하면 되겠죠. 다음과 같이 study 테이블에 아우터조인을 걸어 쿼리를 실행해 보겠습니다.

SELECT a.s_id, a.s_nm, b.c_id, b.c_nm
     , MIN(CASE WHEN c.chasu = 1 THEN '○' END) AS "1차"
     , MIN(CASE WHEN c.chasu = 2 THEN '○' END) AS "2차"
     , MIN(CASE WHEN c.chasu = 3 THEN '○' END) AS "3차"
     , COUNT(c.s_id) AS "참여횟수"
FROM student a
   , course b
   , study c
WHERE a.s_id = c.s_id(+)
  AND b.c_id = c.c_id(+)
GROUP BY a.s_id, a.s_nm, b.c_id, b.c_nm
ORDER BY a.s_id, b.c_id;

  ORA-01417 : 하나의 테이블은 하나의 다른 테이블과 포괄 조인할 수 있습니다

  예기치 못한 에러가 발생했습니다. c를 a, b 두개 집합과 아우터조인을 하려고 해서 발생한 에러입니다. 이를 해결하기 위해서는 a, b를 하나의 집합으로 만들어야 합니다.

4단계. a, b를 하나의 집합으로 만들기(인라인뷰)

SELECT a.s_id, a.s_nm, a.c_id, a.c_nm
     , MIN(CASE WHEN c.chasu = 1 THEN '○' END) AS "1차"
     , MIN(CASE WHEN c.chasu = 2 THEN '○' END) AS "2차"
     , MIN(CASE WHEN c.chasu = 3 THEN '○' END) AS "3차"
     , COUNT(c.s_id) AS "참여횟수"
FROM (SELECT a.s_id, a.s_nm, b.c_id, b.c_nm
        FROM student a, course b
     ) a
   , study c
WHERE a.s_id = c.s_id(+)
  AND a.c_id = c.c_id(+)
GROUP BY a.s_id, a.s_nm, a.c_id, a.c_nm
ORDER BY a.s_id, a.c_id

S_ID S_NM C_ID C_NM 1차 2차 3차 참여횟수
001 기민용 001 Database 2
001 기민용 002 Java 1
002 이현석 001 Database 3
002 이현석 002 Java 0
003 김정식 001 Database 0
003 김정식 002 Java 2
004 강정식 001 Database 1
004 강정식 002 Java 0

  a 와 b를 조인하려고 했는데 원래부터 둘 사이엔 아무런 관계가 없었네요. 조인조건을 줄 수가 없습니다. 조건 없이 조인을 하게 되는 것입니다.

  위 결과 중 노란색으로 표시한 부분이 바로 인라인뷰의 결과입니다. 우리가 원하던 바로 그 결과가 나왔습니다.

5단계. 두번 중복되어 나오는 s_id 와 s_nm 항목은 한번만 나타내기

  이는 c_id 값 두개중 한개만 선택적으로 출력되도록 Case문을 사용하면 됩니다.

SELECT CASE WHEN a.c_id = '001' THEN a.s_id END s_id
     , CASE WHEN a.c_id = '001' THEN a.s_nm END s_nm

  이제 거의 모든 조건을 만족하게 되었습니다. 단, 한가지 조건을 만족하지 못하네요. 바로, 서브 쿼리 사용 안하기 조건입니다.

  아우터조인 시 에러를 해결하기 위해 부득이하게 서브쿼리를 사용했습니다. 만약, 서브쿼리를 사용 안한다면 아우터조인 에러를 해결할 수 없습니다.

  오라클에서 사용하는 를 이용한 (+) 아우터조인에는 몇가지 제약사항이 있습니다.

  • - ORA-01417 : 하나의 테이블은 하나의 다른 테이블과 포괄 조인할 수 있습니다
  • - ORA-01719 : 포괄 조인 운영 (+)는 OR 또는 IN의 연산수를 허용하지 않습니다

  위 제약사항은 ANSI SQL 의 Outer Join을 이용하면 해결이 가능합니다.

6단계. ANSI SQL 의 Outer Join 사용하기

SELECT CASE WHEN b.c_id = '001' THEN a.s_id END AS "ID"
     , CASE WHEN b.c_id = '001' THEN a.s_nm END AS "성명"
     , b.c_nm AS "스터디"
     , MIN(CASE WHEN c.chasu = 1 THEN '○' END) AS "1차"
     , MIN(CASE WHEN c.chasu = 2 THEN '○' END) AS "2차"
     , MIN(CASE WHEN c.chasu = 3 THEN '○' END) AS "3차"
     , COUNT(c.s_id) AS "참여횟수"
 FROM student a
CROSS JOIN course b
 LEFT OUTER JOIN study c
   ON a.s_id = c.s_id
  AND b.c_id = c.c_id
GROUP BY a.s_id, a.s_nm, b.c_id, b.c_nm
ORDER BY a.s_id, b.c_id;

  드디어 정답을 완성했습니다.

  오라클에서 사용하던 Sql문법과는 많이 생소한 구문입니다.

  • - CROSS JOIN 은 a 와 b를 조건 없이 조인할 때 사용이 되는 구문입니다.
  • - LEFT OUTER JOIN 은 왼쪽 집합을 기준으로 오른쪽을 포괄조인하는 구문입니다.
  • - a, b 두개 집합과 c를 아우터조인하고 있지만 에러는 발생하지 않습니다.

  이번 퀴즈의 정답은 매우 간단하지만 결과 도출과정은 그리 만만하지 않습니다.

  기본적으로 테이블간의 관계를 알고 조인에 대한 기본개념을 알고 있어야 하며 그룹바이를 이용하여 행을 열로 바꾸어 표시하는 방법 아우터 조인의 개념과 제약사항 ANSI SQL의 사용법까지 알아야 합니다.

  이번 퀴즈를 통해 이 모든 것들을 자기 것으로 만들 수 있는 계기가 되었으면 합니다.

- 강좌 URL : http://www.gurubee.net/lecture/2190

- 구루비 강좌는 개인의 학습용으로만 사용 할 수 있으며, 다른 웹 페이지에 게재할 경우에는 출처를 꼭 밝혀 주시면 고맙겠습니다.~^^

- 구루비 강좌는 서비스 제공을 위한 목적이나, 학원 홍보, 수익을 얻기 위한 용도로 사용 할 수 없습니다.

by 손님 [2012.04.18 10:52:43]
강좌 잘봤습니다. 감사합니다. 안시 조인은 잘 안써서 생소하네요 ^^ 앞으로도 좋은 강좌 부탁드려요~

by bskwon03 [2012.04.18 10:53:16]
강좌 잘봤습니다. 감사합니다. 안시 조인은 잘 안써서 생소하네요 ^^ 앞으로도 좋은 강좌 부탁드려요~

by 김용한 [2012.04.18 15:58:14]

강좌잘봤습니다 오라클문법으로하면 어렵지않게풀었는데 표준쿼리로 하려니 조금 어렵네요 ~

by 마농 [2012.05.14 18:16:02]

퀴즈를 낼 당시엔 몰랐던 내용이 있는데.
기존 오라클 쿼리에서는 아우터 조인에 제약사항이 있었는데...
1. in 이나 or 조건이 사용안되구요.
2. 두개이상의 테이블과 아우터 조인이 안됩니다.
이 두가지 제약이 버전업되면서 풀린 모양입니다.
11G에서는 제약이 없는것 확인했구요.
10G에서도 뒷쪽 버전인 경우에는 제약이 풀렸다고 하네요.(10.2.0.5 부터인걸로 추정됨)
참고하세요.


by 갱이 [2012.06.12 17:24:55]

퀴즈로 푸는~~ 때문에 요즘 오라클이 재미있어져요 ^^ 감사합니다~
또다른 퀴즈가 막~ 기다려져요~

by Always [2013.03.08 10:43:11]

출력해두고 시간날때마다 퀴즈 풀고 있거든요~ 굉장히 유익한거 같아요.
특히, 이 경우는 현업에서 실 통계뽑을때 주로 쓰여요~~ㅎ 감사합니다.


by ybu [2014.03.18 16:12:17]
 
WITH student AS
( 
 SELECT '001' s_id, '기민용' s_nm FROM dual 
 UNION ALL SELECT '002', '이현석' FROM dual 
 UNION ALL SELECT '003', '김정식' FROM dual 
 UNION ALL SELECT '004', '강정식' FROM dual 
) 
, course AS
( 
 SELECT '001' c_id, 'Database' c_nm FROM dual 
 UNION ALL SELECT '002', 'Java' FROM dual 
) 
, study AS
( 
 SELECT '001' s_id, '001' c_id, 1 chasu FROM dual 
 UNION ALL SELECT '001', '001', 3 FROM dual 
 UNION ALL SELECT '001', '002', 2 FROM dual 
 UNION ALL SELECT '002', '001', 1 FROM dual 
 UNION ALL SELECT '002', '001', 2 FROM dual 
 UNION ALL SELECT '002', '001', 3 FROM dual 
 UNION ALL SELECT '003', '002', 1 FROM dual 
 UNION ALL SELECT '003', '002', 2 FROM dual 
 UNION ALL SELECT '004', '001', 1 FROM dual 
)
select CASE WHEN c.c_id = '001' THEN s.s_id END id 
 , CASE WHEN c.c_id = '001' THEN s.s_nm END 이름 
 , c.c_nm 스터디,
 CASE when st.chasu = 1 then 'O'
 END "1차",
 CASE when st.chasu = 2 then 'O'
 END "2차",
 CASE when st.chasu = 3 then 'O'
 END "3차", 
 count(st.s_id) 참여회수 
 from student s, course c, study st
 where s.s_id = st.s_id
 and c.c_id=st.c_id
 group by c.c_id, st.chasu, s.s_id, s.s_nm, c.c_nm
 order by s.s_id

공부하면서 이렇게 작성을 해봤는데 결과가 1차와 2차 3차가 한줄로 나오는 것이 아니라
따로 다른 줄로 출력이 됩니다 고민을 하다가 어떻게 해야될 지를 몰라 이렇게 질문을 남깁니다.

               
           

by 마농 [2014.03.18 17:17:24]
Group By 에 chasu 가 있어서 그래요.
Group By 에서 chasu 를 빼시고
Select 절에 chasu가 포함된 Case 구문은 Min 등의 집계함수로 감싸주셔야 합니다.
본문에 이미 시행착오를 해결해 나가는 과정으로 상세설명이 되어 있습니다.

by ybu [2014.03.18 17:44:28]

답변 감사합니다 ^^
근대 한가지 더 궁금 한것이 있는데
MIN은 최소값을 구하는 함수로 알고 있는데
CASE구문에서 왜 MIN으로 감싸 주는 것인가요???? MIN이 무슨 역할을 하는 것인가요?

by 마농 [2014.03.18 17:52:03]

Min 은 가장 작은 값을 가져오는 집계함수죠.
어렵게 생각하실 필요 없습니다. 그 의미 그대로 보시면 됩니다.
여러줄로 나왔을 때의 결과를 보시고, 한줄로 집계했을 때의 결과를 비교해 보세요.
여러줄로 나왔을 때의 여러개의 값중 가장 작은 값 하나를 선택하는 것입니다.
여러개의 값중 1개를 제외한 나머지는 모두 널이므로 널이 아닌 값이 선택되는 것이구요.
꼭 Min 일 필요는 없습니다. Max 를 써도 됩니다.


by ybu [2014.03.18 18:09:41]

답변 감사합니다. 쿼리문을 공부를 시작한지 얼마 되지 않아서 어려운 것이 많은데 정말 도움이 많이 됩니다 

by 아이엔유 [2015.07.03 13:18:00]

with문을 사용하지 않고 피벗을 사용해봤습니다. 피벗으로 짜도 서브쿼리가 들어가네요ㅠ

select s_id, s_nm, c_nm,
       (case when "'1'"  = 1 then 'O' end)   as "1차"
       , (case when "'2'"  = 1 then 'O' end) as "2차"
       , (case when "'3'"  = 1 then 'O' end) as "3차"
       , total  as "참여횟수"
from
(
       select stent.s_id, stent.s_nm, cors.c_nm, stdy.chasu,
                           (select count(s_id) from tstudy where s_id =stent.s_id and c_id = cors.c_id and chasu = stdy.chasu) as count1,
                           (select count(s_id) from tstudy where s_id =stent.s_id and c_id = cors.c_id ) as total
       from tstudent stent, tcourse cors, tstudy stdy
       group by stdy.chasu,stent.s_id, stent.s_nm, cors.c_nm, cors.c_id
      
)
pivot(
     sum(count1)
     for chasu in ('1' , '2', '3')
     )
order by s_id;


by 백순성 [2015.09.25 13:52:48]

감사합니다. 마농님.


by 엘시아 [2016.03.31 17:18:31]
SELECT S_ID, S_NM, C_NM, MAX(CASE WHEN CHASU='1' AND LV=1 THEN 'O' ELSE NULL END) AS CHASU1,
                         MAX(CASE WHEN CHASU='2' AND LV=2 THEN 'O' ELSE NULL END) AS CHASU2,
                         MAX(CASE WHEN CHASU='3' AND LV=3 THEN 'O' ELSE NULL END) AS CHASU3 ,
                         MAX(CASE WHEN CHASU='1' AND LV=1 THEN 1 ELSE 0 END)
                       + MAX(CASE WHEN CHASU='2' AND LV=2 THEN 1 ELSE 0 END)
                       + MAX(CASE WHEN CHASU='3' AND LV=3 THEN 1 ELSE 0 END) AS SUM
  FROM 
        (WITH student AS
        (
            SELECT '001' s_id, '기민용' s_nm FROM dual
            UNION ALL SELECT '002', '이현석' FROM dual
            UNION ALL SELECT '003', '김정식' FROM dual
            UNION ALL SELECT '004', '강정식' FROM dual
        )
        , course AS
        (
            SELECT '001' c_id, 'Database' c_nm FROM dual
            UNION ALL SELECT '002', 'Java' FROM dual
        )
        , study AS
        (
            SELECT '001' s_id, '001' c_id, 1 chasu FROM dual
            UNION ALL SELECT '001', '001', 3 FROM dual
            UNION ALL SELECT '001', '002', 2 FROM dual
            UNION ALL SELECT '002', '001', 1 FROM dual
            UNION ALL SELECT '002', '001', 2 FROM dual
            UNION ALL SELECT '002', '001', 3 FROM dual
            UNION ALL SELECT '003', '002', 1 FROM dual
            UNION ALL SELECT '003', '002', 2 FROM dual
            UNION ALL SELECT '004', '001', 1 FROM dual
        )
        select a.s_id,s_nm, b.c_nm, c.chasu
          from student a, course b, study c
         where a.s_id = c.s_id
           and b.c_id = c.c_id
           ) A
           , (SELECT LEVEL LV FROM DUAL CONNECT BY LEVEL <=3) B
GROUP BY S_ID, S_NM, C_NM
ORDER BY S_ID, C_NM   

답안을 안보고 짠건데.. 답을보니 많이 배운거같습니다.

제가짠거는 java나 database과목이 신청건이 0인건 나오지 않아서 left조인을 과목기준으로 걸어보고 그랬는데..안되더라구요 cross 조인을 이럴때 쓰는지 확실히 알고가요

행을 열로 바꿀때 level을 조인걸어서 자주사용했는데, 위 문제같은경우는 chasu자체로 level역할을해서 필요가 없었네요 ^^;

case문으로 한번만 나타나는것도 직접짤땐 생각이 안나다가 답을 보니 아.. 가 절로나오네요 ㅎㅎ;;


by 갈매기 [2016.04.27 06:30:49]

case문으로 한번만 나타나는것이 저는 도무지 이해가 되지 않습니다. 

CASE WHEN b.c_id = '001' THEN a.s_id END AS "ID"
     , CASE WHEN b.c_id = '001' THEN a.s_nm END AS "성명"

구문이 이해가 안되는데, 자세한 설명 좀 부탁 합니다.


by 마농 [2016.04.27 08:26:16]

한명의 학생(s_id, s_nm)에 대해 2건의 과목(c_id : 001, 002)이 연결되어 출력이 되죠.
두번 출력되는 학생의 ID 와 이름을 첫 과목(c_id = 001) 일때만 출력하는 것입니다.


by CHOZZA [2016.05.04 09:26:02]

MIN을 왜쓰는지 묻는 질문에 마농님께서 답변해주신 윗 댓글을 봤는데요, 

말씀하신,

'여러줄로 나왔을 때의 결과를 보시고, 한줄로 집계했을 때의 결과를 비교해 보세요.
여러줄로 나왔을 때의 여러개의 값중 가장 작은 값 하나를 선택하는 것입니다.
여러개의 값중 1개를 제외한 나머지는 모두 널이므로 널이 아닌 값이 선택되는 것이구요.'

이 부분이, 저는 이해가 가질 않습니다.

혹시 더 쉽게 말씀해주실 수 있으십니까? 죄송합니다...


by 마농 [2016.05.04 10:07:13]

위 답변글은
Min 을 사용했을 때 나타나는 결과에 대한 설명인 듯 하네요.
Min 을 왜 쓰는지? 사용 이유에 대한 설명은 아니네요.


Min 을 쓰는 이유는 Group By 때문입니다.
여러줄로 나온 데이터를 하나로 합쳐서 보여줘야 합니다.
그러려면 Group By 를 써야 합니다.
Group By 의 기준항목들 이외의 항목을 표시하려면
반드시 집계함수와 함께 사용해야 하구요.
집계 함수중 Min 을 사용했을 뿐이구요.
큰 의미는 없습니다. Max 를 써도 되는 부분이구요.
그 위후 나머지 결과에 대한 설명은 위 답변과 같구요.


by CHOZZA [2016.05.04 10:12:58]

이해가 바로 갑니다. 쉽게 설명해 주셔서 감사합니다. 


by 고수가되고싶어요 [2017.09.05 15:32:38]
SELECT 
       DECODE(a.s_id,LAG(a.s_id) OVER(ORDER BY a.s_id),'',a.s_id) ID
      ,DECODE(a.s_nm,LAG(a.s_nm) OVER(ORDER BY a.s_id),'',a.s_nm) "성명"
      ,b.c_nm	"스터디"
      ,MIN(DECODE(c.chasu,1,'O')) "1차"
      ,MIN(DECODE(c.chasu,2,'O'))"2차"
      ,MIN(DECODE(c.chasu,3,'O')) "3차"
      ,COUNT(c.chasu) "참여횟수"
FROM student a, course b, study c 
WHERE a.s_id = c.s_id(+)
AND	  b.c_id = c.c_id(+)
GROUP BY A.S_ID,A.S_NM,B.C_ID,B.C_NM
ORDER BY A.S_ID

 

이렇게 짯는데 조금 달라영... 이래도대나염?


by 마농 [2017.09.05 15:44:06]

위 풀이과정 중 3단계 부분 참고하세요.


by 박군two [2017.09.05 15:36:48]
SELECT MAX(DECODE(C.C_ID, '001', S.S_ID, NULL)) AS "ID",
       MAX(DECODE(C.C_ID, '001', S.S_NM, NULL)) AS "성명",
       C.C_NM                                   AS "스터디",
       MAX(DECODE(ST.CHASU, 1, '○', NULL))      AS "1차",
       MAX(DECODE(ST.CHASU, 2, '○', NULL))      AS "2차",
       MAX(DECODE(ST.CHASU, 3, '○', NULL))      AS "3차",
       COUNT(ST.S_ID)                           AS "참여횟수"
FROM   STUDENT S 
       CROSS JOIN COURSE C
       LEFT OUTER JOIN STUDY ST
       ON  S.S_ID = ST.S_ID
       AND C.C_ID = ST.C_ID
GROUP BY S.S_ID, S.S_NM, C.C_ID, C.C_NM
ORDER BY S.S_ID, C.C_ID

 


by 고수가되고싶어요 [2017.12.15 13:43:25]
SELECT DECODE(A.S_ID,LAG(A.S_ID) OVER(ORDER BY A.S_ID),NULL,A.S_ID) ID
	  ,DECODE(A.S_NM,LAG(A.S_NM) OVER(ORDER BY A.S_ID),NULL,A.S_NM) NAME
      ,B.C_NM
      ,MIN(DECODE(CHASU,1,0)) "1차"
      ,MIN(DECODE(CHASU,2,0)) "2차"
      ,MIN(DECODE(CHASU,3,0)) "3차"
      ,COUNT(CHASU) CNT 
FROM STUDENT A RIGHT JOIN COURSE B  PARTITION BY (C_NM) ON A.S_NM != B.C_NM
LEFT JOIN STUDY C ON A.S_ID = C.S_ID AND B.C_ID = C.C_ID 
GROUP BY A.S_ID,A.S_NM,B.C_ID,B.C_NM
ORDER BY A.S_ID,A.S_NM,B.C_ID
      

여기 문제들 푼지 3개월 후 다시 풀어 보려구요! 

3개월 전이랑은 다른쿼리를 짯네여 ㅋㅋ 

다시풀면서 제가 얼마나 늘었는지 테스트해볼게여


by 구골 [2018.01.16 11:16:20]

해설을 단계별로 설명해 주시니까 많은 도움이 되는것 같습니다.

재미있기도 하구요. 시간 날때마다 퀴즈를 풀어봐야 겠네요.

감사합니다.


by by 궁그메 [2018.05.24 17:08:55]


WITH student AS
(
    SELECT '001' s_id, '기민용' s_nm FROM dual
    UNION ALL SELECT '002', '이현석' FROM dual
    UNION ALL SELECT '003', '김정식' FROM dual
    UNION ALL SELECT '004', '강정식' FROM dual
)
, course AS
(
    SELECT '001' c_id, 'Database' c_nm FROM dual
    UNION ALL SELECT '002', 'Java' FROM dual
)
, study AS
(
    SELECT      '001' s_id, '001' c_id, 1 chasu FROM dual
    UNION ALL SELECT '001', '001', 3 FROM dual
    UNION ALL SELECT '001', '002', 2 FROM dual
    UNION ALL SELECT '002', '001', 1 FROM dual
    UNION ALL SELECT '002', '001', 2 FROM dual
    UNION ALL SELECT '002', '001', 3 FROM dual
    UNION ALL SELECT '003', '002', 1 FROM dual
    UNION ALL SELECT '003', '002', 2 FROM dual
    UNION ALL SELECT '004', '001', 1 FROM dual
)
select b.s_id as "ID", MAX(b.s_nm) 성명 , c.c_nm as "스터디"
      ,MIN(decode (a.chasu, '1', decode(a.c_id, c.c_id,'O',''),'' )) AS "1차"
      ,MIN(decode (a.chasu, '2', decode(a.c_id, c.c_id,'O',''),'' )) AS "2차"
      ,MIN(decode (a.chasu, '3', decode(a.c_id, c.c_id,'O',''),'' )) AS "3차"
      ,count(decode(a.c_id, c.c_id,1,null)) as  "참여횟수"
from study a  , student b , course c
where a.s_id =  b.s_id
GROUP BY b.S_ID , C.C_NM
ORDER BY b.s_id, C.C_NM

 

decode를 사용해서 만들어 봤어요. 이거 정답 맞나요 ?


by 우울하지않아 [2018.07.16 14:11:32]

어렵지만 이렇게 공부하니까 신선하고 재밌네요 좋은 강좌 감사합니다!


by FuLLy [2018.08.03 13:50:07]

WITH student AS
(
    SELECT '001' s_id, '기민용' s_nm FROM dual
    UNION ALL SELECT '002', '이현석' FROM dual
    UNION ALL SELECT '003', '김정식' FROM dual
    UNION ALL SELECT '004', '강정식' FROM dual
)
, course AS
(
    SELECT '001' c_id, 'Database' c_nm FROM dual
    UNION ALL SELECT '002', 'Java' FROM dual
)
, study AS
(
    SELECT '001' s_id, '001' c_id, 1 chasu FROM dual
    UNION ALL SELECT '001', '001', 3 FROM dual
    UNION ALL SELECT '001', '002', 2 FROM dual
    UNION ALL SELECT '002', '001', 1 FROM dual
    UNION ALL SELECT '002', '001', 2 FROM dual
    UNION ALL SELECT '002', '001', 3 FROM dual
    UNION ALL SELECT '003', '002', 1 FROM dual
    UNION ALL SELECT '003', '002', 2 FROM dual
    UNION ALL SELECT '004', '001', 1 FROM dual
)
select case when b.c_id = '001' then a.s_id end as "ID",
       case when b.c_id = '001' then min(a.s_nm) end as "성명",
       min(b.c_id) as "성별",
       min(b.c_nm) as "스터디",
       min((case when c.chasu = 1 then 'o' end))  as "1차",
       min((case when c.chasu = 2 then 'o' end)) as "2차",
       min((case when c.chasu = 3 then 'o' end)) as "3차",
       count(c.chasu)
  from student a , course b,study c
 where a.s_id = c.s_id (+)
   and b.c_id = c.c_id (+)
 group by a.s_id, b.c_id
 order by a.s_id, b.c_id
;

 

어렵습니다 열심히 공부하겠습니다.


by FuLLy [2018.08.03 13:50:42]
WITH student AS
(
    SELECT '001' s_id, '기민용' s_nm FROM dual
    UNION ALL SELECT '002', '이현석' FROM dual
    UNION ALL SELECT '003', '김정식' FROM dual
    UNION ALL SELECT '004', '강정식' FROM dual
)
, course AS
(
    SELECT '001' c_id, 'Database' c_nm FROM dual
    UNION ALL SELECT '002', 'Java' FROM dual
)
, study AS
(
    SELECT '001' s_id, '001' c_id, 1 chasu FROM dual
    UNION ALL SELECT '001', '001', 3 FROM dual
    UNION ALL SELECT '001', '002', 2 FROM dual
    UNION ALL SELECT '002', '001', 1 FROM dual
    UNION ALL SELECT '002', '001', 2 FROM dual
    UNION ALL SELECT '002', '001', 3 FROM dual
    UNION ALL SELECT '003', '002', 1 FROM dual
    UNION ALL SELECT '003', '002', 2 FROM dual
    UNION ALL SELECT '004', '001', 1 FROM dual
)
select case when b.c_id = '001' then a.s_id end as "ID", 
       case when b.c_id = '001' then min(a.s_nm) end as "성명",
       min(b.c_id) as "성별",
       min(b.c_nm) as "스터디", 
       min((case when c.chasu = 1 then 'o' end))  as "1차",
       min((case when c.chasu = 2 then 'o' end)) as "2차",
       min((case when c.chasu = 3 then 'o' end)) as "3차",
       count(c.chasu)
  from student a , course b,study c
 where a.s_id = c.s_id (+)
   and b.c_id = c.c_id (+)
 group by a.s_id, b.c_id
 order by a.s_id, b.c_id
;


 


by sarahpark [2020.02.26 11:40:01]
--듣지 않은 강의는 제외한 쿼리
WITH student AS
(
    SELECT '001' s_id, '기민용' s_nm FROM dual
    UNION ALL SELECT '002', '이현석' FROM dual
    UNION ALL SELECT '003', '김정식' FROM dual
    UNION ALL SELECT '004', '강정식' FROM dual
)
, course AS
(
    SELECT '001' c_id, 'Database' c_nm FROM dual
    UNION ALL SELECT '002', 'Java' FROM dual
)
, study AS
(
    SELECT '001' s_id, '001' c_id, 1 chasu FROM dual
    UNION ALL SELECT '001', '001', 3 FROM dual
    UNION ALL SELECT '001', '002', 2 FROM dual
    UNION ALL SELECT '002', '001', 1 FROM dual
    UNION ALL SELECT '002', '001', 2 FROM dual
    UNION ALL SELECT '002', '001', 3 FROM dual
    UNION ALL SELECT '003', '002', 1 FROM dual
    UNION ALL SELECT '003', '002', 2 FROM dual
    UNION ALL SELECT '004', '001', 1 FROM dual
)
SELECT   ST.S_ID
       , MAX(ST.S_NM)
       , C.C_ID
       , MAX(C.C_NM)
       , MAX(CASE WHEN S.CHASU = '1' THEN TO_CHAR('O') ELSE NULL END) AS "1차" 
       , MAX(CASE WHEN S.CHASU = '2' THEN TO_CHAR('O') ELSE NULL END) AS "2차" 
       , MAX(CASE WHEN S.CHASU = '3' THEN TO_CHAR('O') ELSE NULL END) AS "3차" 
       , COUNT(CASE WHEN S.CHASU = '1' THEN '1' 
                  WHEN S.CHASU = '2' THEN '2'
                  WHEN S.CHASU = '3' THEN '3'
                  ELSE '0' END) AS "참여횟수"
FROM     STUDENT ST
       , COURSE C
       , STUDY S
WHERE    1 = 1
AND      ST.S_ID = S.S_ID(+)
AND      C.C_ID = S.C_ID(+)
GROUP BY ST.S_ID, C.C_ID
ORDER BY ST.S_ID

;

여기서 막히네요..

+

OUTER JOIN을 사용하기 위한 조건, 

CASE문에 대해 잘 알 수 있었습니다.

이하 공부 후 정답 도출한 쿼리

WITH student AS
(
    SELECT '001' s_id, '기민용' s_nm FROM dual
    UNION ALL SELECT '002', '이현석' FROM dual
    UNION ALL SELECT '003', '김정식' FROM dual
    UNION ALL SELECT '004', '강정식' FROM dual
)
, course AS
(
    SELECT '001' c_id, 'Database' c_nm FROM dual
    UNION ALL SELECT '002', 'Java' FROM dual
)
, study AS
(
    SELECT '001' s_id, '001' c_id, 1 chasu FROM dual
    UNION ALL SELECT '001', '001', 3 FROM dual
    UNION ALL SELECT '001', '002', 2 FROM dual
    UNION ALL SELECT '002', '001', 1 FROM dual
    UNION ALL SELECT '002', '001', 2 FROM dual
    UNION ALL SELECT '002', '001', 3 FROM dual
    UNION ALL SELECT '003', '002', 1 FROM dual
    UNION ALL SELECT '003', '002', 2 FROM dual
    UNION ALL SELECT '004', '001', 1 FROM dual
)
SELECT   CASE WHEN A.c_id = '001' THEN a.s_id END AS "ID"
       , MAX(CASE WHEN A.c_id = '001' THEN A.s_nm END) AS "성명"
       , MAX(CASE WHEN A.C_NM = 'Database' THEN 'Database' 
                  WHEN A.C_NM = 'Java' THEN 'Java'
                  ELSE '0' END) AS 스터디
       , MAX(CASE WHEN S.CHASU = '1' THEN TO_CHAR('O') ELSE NULL END) AS "1차" 
       , MAX(CASE WHEN S.CHASU = '2' THEN TO_CHAR('O') ELSE NULL END) AS "2차" 
       , MAX(CASE WHEN S.CHASU = '3' THEN TO_CHAR('O') ELSE NULL END) AS "3차" 
       , COUNT( S.CHASU) AS "참여횟수"
FROM     (SELECT ST.S_ID, ST.S_NM, C.C_ID, C.C_NM FROM student st, course C) A
       , study S
WHERE    1 = 1
AND      A.S_ID = S.S_ID(+)
AND      A.C_ID = S.C_ID(+)
GROUP BY A.S_ID, A.C_ID
ORDER BY A.S_ID
;

 


by 얼쿨 [2020.03.06 10:55:13]

SQL 패턴이 많이 바뀌었군요 

제가 배울때랑 많이 달라서 ....

아래 퀴리로 전 해결했읍니다 참고하세요

WITH student AS

(

    SELECT '001' s_id, '기민용' s_nm 

    UNION ALL SELECT '002', '이현석' 

    UNION ALL SELECT '003', '김정식' 

    UNION ALL SELECT '004', '강정식' 

)

, course AS

(

    SELECT '001' c_id, 'Database' c_nm 

    UNION ALL SELECT '002', 'Java' 

)

, study AS

(

    SELECT '001' s_id, '001' c_id, 1 chasu 

    UNION ALL SELECT '001', '001', 3 

    UNION ALL SELECT '001', '002', 2 

    UNION ALL SELECT '002', '001', 1 

    UNION ALL SELECT '002', '001', 2 

    UNION ALL SELECT '002', '001', 3 

    UNION ALL SELECT '003', '002', 1 

    UNION ALL SELECT '003', '002', 2 

    UNION ALL SELECT '004', '001', 1 

)

select case when c.c_id = '001' then s.s_id end s_id,

   case when c.c_id = '001' then s.s_nm end s_nm,

   c.c_id, c.c_nm,

   (select top 1 'O' from study st where st.c_id = c.c_id and st.s_id = s.s_id and st.chasu = 1) chasu1, 

   (select top 1 'O' from study st where st.c_id = c.c_id and st.s_id = s.s_id and st.chasu = 2) chasu2,

   (select top 1 'O' from study st where st.c_id = c.c_id and st.s_id = s.s_id and st.chasu = 3) chasu3,

   (select count(st.c_id) from study st where st.c_id = c.c_id and st.s_id = s.s_id ) as totalcnt

from student s, course c

order by s.s_id, c.c_id


by 밤부 [2021.03.11 14:25:50]

문제 풀어보며 SQL짜는 능력이 느는것같아요 감사합니다 :)

댓글등록
SQL문을 포맷에 맞게(깔끔하게) 등록하려면 code() 버튼을 클릭하여 작성 하시면 됩니다.
로그인 사용자만 댓글을 작성 할 수 있습니다. 로그인, 회원가입