쿼리 짜는데 있어 조언을 구합니다 0 7 1,814

by 지누기 [SQL Query] 논리적구조 [2013.08.13 16:08:00]


안녕하십니까,  매일 고수님들 실력 훔쳐보면서 공부하고 있는 초짜개발자입니다.

프로젝트를 진행하다가 다음과 같은 요구사항이 들어와서, 고민하다가 조심스레 올려봅니다.

Table A Table B Table C
USERID TESTID TESTID
TESTID SEQ SEQ
GERMCD MEDCD
RSLTCD
INSDATE

다음의 형태로 3개의 테이블이 있고,

A 테이블이 유저 기본 정보,

B 테이블이 검사 세균 정보,

C 테이블이 검사에 사용한 약품의 결과 정보를 가지고 있습니다.

각 테이블의 PK는 TESTID로 동일하고,

B 테이블과 C 테이블은 SEQ가 동일합니다.

조인시에는 B.TESTID = C.TESTID AND B.SEQ = C.SEQ 이런 식으로 연결하고 있습니다.

예를 들어, 1명의 A테이블 USERID가 B테이블에 3개의 검사를 하면 C테이블에 검사별로 5개씩 15개의 결과 로우가

인서트 되는 구조입니다.

C.INSDATE의 기간 사이에 행해진 검사 중에서 A.USERID 별로 기간 내 맨 처음 C.RSLTCD를 가진 값만 보이게 해달라는

요구사항이 들어와서, 이를 작업중인데 쿼리를 어떻게 짜야 중복되는 값을 제거하고 원하는 결과값을 얻을 수

있을지 감이 오질 않아서 답답합니다. 

어떤 방식으로 쿼리를 짜야될런지 고수님들의 조언 부탁드리고, 혹시 이와 비슷한 예제를 알고 계시면

주소라도 남겨주시길 부탁드리겠습니다.

항상 많이 배우고 있습니다. 감사합니다.





by 아린 [2013.08.13 16:35:59]
아래와 같이   
샘플데이터와 원하시는 결과값을 데이터로 올려주세요.

WITH a(userid, testid) AS(
SELECT 'usr1', 'txt1' FROM dual UNION ALL
SELECT 'usr2', 'txt2' FROM dual UNION ALL
SELECT 'usr3', 'txt3' FROM dual
), c(testid, seq, meccd, rsltcd, insdate) AS(
SELECT 'txt1', 1, '1mec1', '1rsl1', '20130811' FROM dual UNION ALL
SELECT ..................
)

by 지누기 [2013.08.13 17:37:32]
데이터
 
WITH A(USERID, TESTID) AS(
SELECT '2247', '000001' FROM DUAL UNION ALL
SELECT '2345', '000002' FROM DUAL UNION ALL
SELECT '2247', '000003' FROM DUAL 
), B(TESTID, SEQ, GERMCD) AS(
SELECT '000001', '1', 'PAE' FROM DUAL UNION ALL
SELECT '000001', '1', 'ABA' FROM DUAL UNION ALL
SELECT '000002', '1', 'PAE' FROM DUAL UNION ALL
SELECT '000002', '1', 'ABA' FROM DUAL UNION ALL
SELECT '000003', '1', 'PAE' FROM DUAL UNION ALL
SELECT '000003', '1', 'KOX' FROM DUAL 
), C(TESTID, SEQ, MEDCD, RSLTCD, INSDATE) AS(
SELECT '000001', '1', 'AMP', '+', '20130101' FROM DUAL UNION ALL
SELECT '000001', '1', 'AMB', 'R', '20130101' FROM DUAL UNION ALL
SELECT '000001', '1', 'AMK', 'I', '20130101' FROM DUAL UNION ALL
SELECT '000001', '1', 'AMS', 'S', '20130101' FROM DUAL UNION ALL
SELECT '000002', '1', 'AMR', 'R', '20130101' FROM DUAL UNION ALL
SELECT '000002', '1', 'AMP', 'S', '20130201' FROM DUAL UNION ALL
SELECT '000002', '1', 'TGC', '-', '20130101' FROM DUAL UNION ALL
SELECT '000003', '1', 'AMP', 'R', '20130301' FROM DUAL UNION ALL
SELECT '000003', '1', 'VAN', 'I', '20130301' FROM DUAL 
)
SELECT 
       A.USERID,
       B.GERMCD, 
       C.MEDCD, 
       C.RSLTCD, 
       C.INSDATE
 FROM A
 INNER JOIN B ON A.TESTID = B.TESTID
 INNER JOIN C ON B.TESTID = C.TESTID
           AND B.SEQ = C.SEQ
 WHERE C.INSDATE BETWEEN '20130101' AND '20130301'
   AND B.GERMCD = 'PAE'
   AND C.MEDCD = 'AMP'
   AND A.USERID = '2247'
   ;
위 쿼리로 결과를 구하면,

userid germcd medcd rlstcd  insdate
2247	PAE	AMP	+	20130101
2247	PAE	AMP	R	20130301

이렇게 나오는데요. 제가 원하는 결과는

userid germcd medcd rlstcd  insdate
2247	PAE	AMP	+	20130101

이렇게 조회기간 내에서 유저별, 세균별, 약품별로 가장 첫번째 결과만 보는 것입니다.

by 아린 [2013.08.13 18:02:28]
쿼리 결과 2건중의 순서가 바뀌면 결과값도 바뀔것 같은데요.

둘 중 아무거나 나와도 되는건가요?  아니면, 정렬순서가 있는건가요?

그리고 테이블 데이터 값중 seq 값들이 모두 1 이네요.

by 아린 [2013.08.13 18:10:10]
질문에 유저별로 보여달라고 해서 유저별로만 했습니다. 
특정한 정렬순서를 가지고 있지 않아서 ORDER BY ROWNUM 으로 했고요.

SELECT *
  FROM (SELECT a.userid, b.germcd, c.medcd, c.rsltcd, c.insdate
             , ROW_NUMBER() OVER(PARTITION BY a.userid ORDER BY ROWNUM) rn 
          FROM a 
          JOIN b ON a.testid = b.testid
          JOIN c ON b.testid = c.testid AND b.seq = c.seq
         WHERE c.insdate BETWEEN '20130101' AND '20130301'
        )
 WHERE rn = 1   

by 마농 [2013.08.13 18:20:56]
부연 설명...
userid, germcd, medcd 에 대한 조건이 필수라면 단순 ORDER BY c.insdate 하시면 되구요.
그렇지 않다면 PARTITION BY a.userid, b.germcd, c.medcd ORDER BY c.insdate 하시면 됩니다.

by 지누기 [2013.08.13 18:13:49]
정렬은 useerid 순으로 정렬을 하구요.

실 데이터는 개인정보가 들어있는 관계로 제가 임의로 수정했습니다.

b.seq = c.seq 인 관계라 일부러 1로 만들었습니다.

by 지누기 [2013.08.13 18:32:39]
감사합니다 아린님, 마농님

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