자료 추출시 아이디어 도움 구합니다 0 12 876

by 와니와플 [SQL Query] [2020.11.10 19:45:43]



안녕하세요, 아래와 같이 사람의 이름이 있고 지점이 10개가 있다고 가정 할 때

김길동의 경우 8번 지점을 첫번째로, 7번 지점을 두번째로 해서 10번 지점을 마지막으로 방문했다고 가정하면 

이렇게 몇번 지점을 순차적으로 방문했는지를 먼저 보여주는 쿼리는 어떻게 접근하면 좋을까요?

툴이 있는 것 같기도 한데.. 일단 고민하다가 고민 올려봅니다...!

꼭 쿼리 답안이 아니더라도 더 고수분들의 좋은 조언을 구해 봅니다!

 

이름 1번쨰 2번쨰 3번쨰 4번쨰 5번쨰 6번쨰 7번쨰 8번쨰 9번쨰 10번쨰
김길동 3 4 6 8 9 5 2 1 11 14
김XX 13 11 3 7 6 5 2      
박XX 2 6 7 8 3 10 5 9    
최XX 3 5 1 2 9 8 14 15 10  
이XX 13 15 14 2 9 8 6 4 2 1
by 마농 [2020.11.10 21:11:51]

1. 우선 테이블 설계가 위와 같이 된 건지 의문입니다.
 - 테이블 정규화를 한다면? 이름 하나당 10개 행으로 구성되도록 설계될 것입니다.
 - 이렇게 되면 구현하기도 쉽습니다.
2. 결과를 글로 설명하셨는데.
 - 글로 적는 것 보다는 눈으로 보여주시는 게 이해하기 좋습니다.
 - 결과표를 보여주세요.
3. DBMS 에 따라 구현방법이 달라집니다.
 - DBMS 가 뭔가요?


by 마농 [2020.11.11 07:52:20]
-- Oracle --
WITH t(id, nm, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10) AS
(
          SELECT 1, '김길동',  3,  4,  6, 8, 9,  5,  2,    1,   11,   14 FROM dual
UNION ALL SELECT 2, '김XX'  , 13, 11,  3, 7, 6,  5,  2, null, null, null FROM dual
UNION ALL SELECT 3, '박XX'  ,  2,  6,  7, 8, 3, 10,  5,    9, null, null FROM dual
UNION ALL SELECT 4, '최XX'  ,  3,  5,  1, 2, 9,  8, 14,   15,   10, null FROM dual
UNION ALL SELECT 5, '이XX'  , 13, 15, 14, 2, 9,  8,  6,    4,    2,    1 FROM dual
)
SELECT id, nm
     , LISTAGG(no, ',') WITHIN GROUP(ORDER BY seq) x
  FROM t
 UNPIVOT (seq FOR no IN ( c1  AS  1
                        , c2  AS  2
                        , c3  AS  3
                        , c4  AS  4
                        , c5  AS  5
                        , c6  AS  6
                        , c7  AS  7
                        , c8  AS  8
                        , c9  AS  9
                        , c10 AS 10
                        ) )
 GROUP BY id, nm
;

 


by 케빈재브라위너 [2020.11.11 10:04:57]

마농님

서브쿼리를 select 절에 쓰면 무조건 1줄짜리 '단일건 조회'

from 절에 쓰면 join 의 개념이니까 그냥 일반테이블 조인한다생각하면 되나요? on절에 무엇을 기술하냐에따라서 조회갯수가 달라진다고 보면되나요?

만드시는 쿼리보면 거의다 서브쿼리를 이용하고계셔서 어떻게 서브쿼리를 적재적소에 쓰시는지 약간의 팁이 있을까요


by 마농 [2020.11.11 12:44:07]

To. 케빈재브라위너
1. 서브쿼리를 select 절에 쓰면 무조건 1줄짜리 '단일건 조회'
  - 네. 맞습니다. 1줄 1개 항목 조회되는 (스칼라서브쿼리) 입니다.
2. from 절에 쓰면 join 의 개념이니까 그냥 일반테이블 조인한다생각하면 되나요?
  - 일반 테이블처럼 생각하면 됩니다. 중간집합이라고 보시면 됩니다.
  - 다만, 조인의 개념은 아니죠. 조인 없이 단일 테이블 조회도 가능하니까요.
  - From 절의 서브쿼리를 (인라인뷰)라고 합니다.
3. 만드시는 쿼리보면 거의다 서브쿼리를 이용하고계셔서?
  - 글쎄요? 제가 거의 다 서브쿼리를 사용한다구요? 그럴리가요?
  - 위 댓글 쿼리만 봐도 서브쿼리를 사용하지 않았습니다.
  - 저는 어떻게든 쿼리를 간략화 하려고 노력합니다. 그러려면 서브쿼리가 그만큼 적어야 하죠.
4. 어떻게 서브쿼리를 적재적소에 쓰시는지 약간의 팁이 있을까요
  - 적재적소는 서브쿼에만 국한된 얘기는 아닙니다. 모든 기능들에 적용해야 하는 말이네요.
  - 각 기능들의 특징을 정확하게 알아야 적재적소에 사용 할 수 있습니다.
  - 요리와 마찬가지 인 듯 합니다.
  - 똑같은 재료가 주어진다고 해도 각 재료의 특성을 정확히 알고 요리하는 고수와
  - 레시피만 보고 따라하는 초보 요리사의 음식의 퀄리티는 차이가 나기 마련이죠.


by 와니와플 [2020.11.11 11:31:14]

mysql 입니다. ^^

저는 프로시져로 만들어서 랭크를 주긴 했었는데 두 개 비교해 봐야 겠네요^^

CREATE DEFINER=`root`@`%` PROCEDURE `test1`()
BEGIN 

    DECLARE v_name varchar(10);
    DECLARE done1 BOOLEAN DEFAULT FALSE;
    DECLARE cursor1 CURSOR FOR select name_a from temp_josa;
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done1 = TRUE;
    
    OPEN cursor1;
    GET_T1: LOOP
            FETCH cursor1 INTO v_name;
            IF done1 THEN
                LEAVE GET_T1;
            END IF;
                    
insert into temp_tot_josa 
                    select
    k.gubun,
    k.first_sp
from
    (
        select
            'first' as gubun,
            first_sp,
            rank() over(order by first_sp asc) as ranking
        from
            temp_josa
        where
            name_a = v_name
        union all
        select
            'second' as gubun,
            second_sp,
            rank() over(order by second_sp asc) as ranking
        from
            temp_josa
        where
            name_a = v_name
        union all
        select
            'third' as gubun,
            third_sp,
            rank() over(order by third_sp asc) as ranking
        from
            temp_josa
        where
            name_a = v_name
        union all
        select
            'fourth' as gubun,
            fourth_sp,
            rank() over(order by fourth_sp asc) as ranking
        from
            temp_josa
        where
            name_a = v_name
        union all
        select
            'fifth' as gubun,
            fifth_sp,
            rank() over(order by fifth_sp asc) as ranking
        from
            temp_josa
        where
            name_a = v_name
        union all
        select
            'sixth' as gubun,
            sixth_sp,
            rank() over(order by sixth_sp asc) as ranking
        from
            temp_josa
        where
            name_a = v_name
        union all
        select
            'sev_sp' as gubun,
            sev_sp,
            rank() over(order by sev_sp asc) as ranking
        from
            temp_josa
        where
            name_a = v_name
        union all
        select
            'eight_sp' as gubun,
            eight_sp,
            rank() over(order by eight_sp asc) as ranking
        from
            temp_josa
        where
            name_a = v_name
        union all
        select
            'nine_sp' as gubun,
            nine_sp,
            rank() over(order by nine_sp asc) as ranking
        from
            temp_josa
        where
            name_a = v_name
        union all
        select
            'ten_sp' as gubun,
            ten_sp,
            rank() over(order by ten_sp asc) as ranking
        from
            temp_josa
        where
            name_a = v_name
    ) k
order by k.first_sp;
commit;

    END LOOP GET_T1;
    CLOSE cursor1;
    SET done1= FALSE;
END


by 마농 [2020.11.11 12:52:34]

rank 분석함수를 사용해 ranking 이라고 별칭을 주었지만.
해당 ranking 항목은 전혀 사용이 되질 않고 있습니다.
불필요한 부분이네요.


by 마농 [2020.11.11 12:28:38]
-- MySQL --
WITH temp_josa
( name_a
, first_sp, second_sp, third_sp, fourth_sp, fifth_sp
, sixth_sp, sev_sp, eight_sp, nine_sp, ten_sp
) AS
(
          SELECT '김길동',  3,  4,  6, 8, 9,  5,  2,    1,   11,   14
UNION ALL SELECT '김XX'  , 13, 11,  3, 7, 6,  5,  2, null, null, null
UNION ALL SELECT '박XX'  ,  2,  6,  7, 8, 3, 10,  5,    9, null, null
UNION ALL SELECT '최XX'  ,  3,  5,  1, 2, 9,  8, 14,   15,   10, null
UNION ALL SELECT '이XX'  , 13, 15, 14, 2, 9,  8,  6,    4,    2,    1
)
SELECT *
  FROM (SELECT a.name_a
             , b.gubun
             , CASE b.gubun
               WHEN 'first'   THEN a.first_sp 
               WHEN 'second'  THEN a.second_sp
               WHEN 'third'   THEN a.third_sp 
               WHEN 'fourth'  THEN a.fourth_sp
               WHEN 'fifth'   THEN a.fifth_sp 
               WHEN 'sixth'   THEN a.sixth_sp 
               WHEN 'seventh' THEN a.sev_sp   
               WHEN 'eighth'  THEN a.eight_sp 
               WHEN 'nineth'  THEN a.nine_sp  
               WHEN 'tenth'   THEN a.ten_sp   
                END sp
          FROM temp_josa a
             , (SELECT 'first' gubun
                UNION ALL SELECT 'second'  
                UNION ALL SELECT 'third'   
                UNION ALL SELECT 'fourth'  
                UNION ALL SELECT 'fifth'   
                UNION ALL SELECT 'sixth'   
                UNION ALL SELECT 'seventh' 
                UNION ALL SELECT 'eighth'  
                UNION ALL SELECT 'nineth'  
                UNION ALL SELECT 'tenth'   
                ) b
        ) c
 WHERE sp IS NOT NULL
 ORDER BY name_a, sp
;

 


by 케빈재브라위너 [2020.11.11 13:30:34]

- 저는 어떻게든 쿼리를 간략화 하려고 노력합니다. 그러려면 서브쿼리가 그만큼 적어야 하죠. 

이말을 명심하겠습니다. 개발자로서 올해 3월에 입사했는데 실질적으로 쿼리문 작성을 하긴하되, 여기 게시판에 질문올라오는 수준이 아닌 그보다 한참 밑에 수준의 쿼리문을 개발하는 초보 개발자입니다. 

처음에는 그냥 구글링해서 찾아서 되는대로 이거저거 해보다가 결과만 맞으면 그쿼리를 사용했었는데

반복적으로 쿼리를 하다보니 이제 쿼리문 작성시에 F9를 눌러서나오는  조회결과가 미리 어느정도 머릿속에 그려져서 짜기전에 먼저 계획적으로 ' 뭐를 셀렉트해서 어떤거랑 조인해서 on절에 뭘 기술하면 내가원하는 어떠한 형태의 결과가  나오겠다 ' 라는 생각을 저도 모르게 하게되더라고요.. (with, unpivot ,partition 같이 어려운 기술없이 그냥 select from join 으로만 짜는쿼리)

그래서 자신감이 좀 붙었는데...

서브쿼리를 만나고나서는 헷갈려서질문드렸었습니다.  덕분에 어떤마음자세로 쿼리를 접해야 할지 조금더 기준이 잡히게됬습니다. 더 공부해보고 조금더 수준이 올라가면 게시판에 질문도 해보겠습니다 감사합니다.


by 마농 [2020.11.11 14:03:01]

To. 케빈재브라위너
1. 댓글이 여기 저기 관련 없이 붙어 있는 느낌이네요.
 - 남의 집에서 서로 대화하는 느낌.
2. 제말만 듣고 한쪽으로 치우치는 것은 아닌지 걱정입니다.
 - "서브쿼리는 안좋은 거니 쓰지 말아야 겠다" 식의 결론을 내는 건 아니겠죠?
 - 모든 것은 장단점이 있고, 장점만 보거나 단점만 봐서는 안됩니다.
 - 적절히 조율해서 사용해야 합니다.
 - 불필요한데도 사용하거나, 필요한데도 사용 안하는 일은 없어야 합니다.


by 와니와플 [2020.11.11 13:37:33]

도움 감사 드립니다!

제가 짠 것 보다 훨씬 효율적이네요, 공부가 절로 됩니다6^

한 가지만,, 더 여쭈면,, 여기서 사람들이 많이 간 패턴을 찾으려면 즉

1,2,3,4,6,10이 많은지 (이XX같은 사람이 2000명이라고 가정), 1,3,4,5,6,10 등 가장 많이 간 패턴을 쿼리로는 찾기 힘들겠지요?

PYTHON으로 짜면 순열로 가능할 것 같긴 한데, 너무 비효율적이라 혹 의견 있으신지 여쭙습니다.


by 마농 [2020.11.11 13:53:49]
SELECT gubun
     , COUNT(*) cnt
  FROM (SELECT GROUP_CONCAT(gubun ORDER BY sp) gubun
          FROM (SELECT a.name_a
                     , b.gubun
                     , CASE b.gubun
                       WHEN '01' THEN a.first_sp 
                       WHEN '02' THEN a.second_sp
                       WHEN '03' THEN a.third_sp 
                       WHEN '04' THEN a.fourth_sp
                       WHEN '05' THEN a.fifth_sp 
                       WHEN '06' THEN a.sixth_sp 
                       WHEN '07' THEN a.sev_sp   
                       WHEN '08' THEN a.eight_sp 
                       WHEN '09' THEN a.nine_sp  
                       WHEN '10' THEN a.ten_sp   
                        END sp
                  FROM temp_josa a
                     , (SELECT '01' gubun
                        UNION ALL SELECT '02'
                        UNION ALL SELECT '03'
                        UNION ALL SELECT '04'
                        UNION ALL SELECT '05'
                        UNION ALL SELECT '06'
                        UNION ALL SELECT '07'
                        UNION ALL SELECT '08'
                        UNION ALL SELECT '09'
                        UNION ALL SELECT '10'
                        ) b
                ) c
         WHERE sp IS NOT NULL
         GROUP BY name_a
        ) d
 GROUP BY gubun
 ORDER BY cnt DESC
 LIMIT 10
;

 


by 와니와플 [2020.11.11 14:23:41]

감사합니다. 코드 연구해 보아야겠네요! 와 정말 대단하십니다..!

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