DISTINCT를 사용하지 않고 각 그룹 별 중복되지 않는 사용자를 구하고 싶습니다. 0 8 1,262

by daviraba [MySQL] [2021.05.18 23:14:19]


안녕하세요. 지난번 질문드린이후 SQL관련 기초책을 찾아 열심히 공부를 하고 있습니다.

공부를 하는도중 다음과 같은 값을 구하려고 하는데 DISTINCT를 사용하면 쉽게 구할 수 있다고 

온라인에서 찾은 예제를 보니 DISTINCT가 실행되는 과정에서 GROUP BY를 한 결과를 토대로 보여준다고 해서

GROUP BY로 여러 시도를 해 보았지만 결과가 정확하게 나오지 않아 좋은 방법이 있을까 하여 질문을 드립니다.


대상 테이블

|--------------------------------------------------|
|     id      |    user_id       |    access_ip    |    
|--------------------------------------------------|
|     1       |      test        |    1.2.3.4      | 
|--------------------------------------------------|
|     2       |      test        |    1.2.1.1      |
|--------------------------------------------------|
|     3       |      have        |    1.2.3.4      |
|--------------------------------------------------|
|     4       |      test        |    1.2.1.1      |
|--------------------------------------------------|
|     5       |      have        |    1.2.3.4      |
|--------------------------------------------------|
|     6       |      abcd        |     1.2.3.4     |
|--------------------------------------------------|

 

시도한 쿼리

SELECT * FROM `tables` WHERE (user_id) IN (
    SELECT user_id FROM `tables` GROUP BY access_ip
) GROUP BY user_id ...

하는 도중 GROUP_CONCAT도 시도해 보고 있고, 마농님이 알려주신 BY PARTITION_BY

도 활용해서 해보고 있지만 쉽지 않네요.

 

 

목표

|----------------------------------------------------------------|
|     id      |     access_ip      |        group_member         |
|----------------------------------------------------------------|
|     1       |      1.2.3.4       |       test,have,abcd        |
|----------------------------------------------------------------|
|     2       |      1.2.1.1       |            test             |
|----------------------------------------------------------------|

 

자주 쓰일법한 쿼리문같아 많이 검색을 해봤는데 딱히 참고가 안되는 결과들만 나와 이렇게 부탁아닌 질문을 드립니다.

 

고견부탁드립니다.

 

 

----------------------------------------------------------------------------------------------------------------------------------

SELECT ip.access_ip, GROUP_CONCAT(DISTINCT id.user_id ORDER BY id.user_id ASC) AS user_list
	FROM user_accesslogs AS ip JOIN user_accesslogs AS id ON ip.access_ip = id.access_ip
GROUP BY ip.access_ip;

 

책을보고 천천히 해보다 보니 다음과 같은 쿼리로 원하는 결과가 나온것 같습니다.

질문 주제와는 좀 동떨어졌지만요.. ㅜ

이렇게 구하는것이 맞는지.. 그리고 혹시 이것보다 더 나은 방법으로 쿼리하는방법이 있는지 궁금합니다.

 

더불어 user_list 에 출력되는 유저의 수를 구하려고 다음과 같이 추가로 쿼리를 만들었는데 더 좋게 수정할 수 있는 방법이 있을까요?

SELECT ip.access_ip, GROUP_CONCAT(DISTINCT id.user_id ORDER BY id.user_id ASC) AS user_list, COUNT(DISTINCT id.user_id) AS count
	FROM user_accesslogs AS ip JOIN user_accesslogs AS id ON ip.access_ip = id.access_ip
GROUP BY ip.access_ip;

 

 

고견 부탁드립니다..

by 뉴비디비 [2021.05.19 12:06:43]

대상테이블 기준으로 작성하면 JOIN 은 필요 없지만,, 중복데이터는 GROUP BY 나 DISTINCT 는 사용하셔야 될꺼예요. 

SELECT 
	access_ip
	, GROUP_CONCAT(DISTINCT user_id ORDER BY user_id DESC) user_list
	, COUNT(DISTINCT user_id) user_cnt
FROM user_accesslogs 
GROUP BY access_ip
ORDER BY access_ip DESC

by daviraba [2021.05.19 13:13:52]

감사합니다. 정말 JOIN을 빼도 동일한 값이 나오네요.

제가 쿼리 작성시 생각한 바로는

1. 아이피를 그룹화(유일값) 한다.

2. 아이피에 속해있는 유저도 그룹화(유일값) 한다.

3. 두 값을 하나의 행에 정렬한다

개념으로 생각을 해서 조인을 각각해 별도로 구했습니다.

저것 조인하는것을 셀프조인이라고 부르던데 혹시 셀프조인은 어떤경우에 사용해야하는지 가능하시다면

예를 좀 들어주실 수 있으신가요?

 

 

저기 답변주신 내용에 덧붙혀 현재 유저가 존재하는지 여부도 필요하고 block_ip 컬럼에 null 인경우

block 상태가 아닌것으로 판단하게 하는 기능이 필요해 제가 추가로 작성한 쿼리는 다음과 같습니다.

SELECT
    lg.access_ip, black.block_ip
    , GROUP_CONCAT(DISTINCT CONCAT(lg.user_id,'|',usr.nickname) ORDER BY lg.user_id ASC) AS user_list, COUNT(DISTINCT lg.user_id) user_cnt
    , COUNT(DISTINCT lg.user_id) user_cnt
FROM user_accesslogs AS lg
JOIN users AS usr ON lg.user_id = usr.user_id
LEFT JOIN ipblocks AS black ON black.block_ip = lg.access_ip
GROUP BY lg.access_ip
ORDER BY user_cnt DESC;

 

이 코드에서도 혹시 무언가 놓치고있는 부분이 있을까요?

고견을 부탁드립니다.


by 뉴비디비 [2021.05.19 23:19:17]

 처음 쿼리는 그룹화 만으로 처리가 가능하니까 JOIN이 필요없는데, Self JOIN 은 동일한 테이블에서 어떤 데이터와 다른 데이터와 관계 재구성이 필요할때 사용됩니다. 
카테고리나 하위부서 등 계층을 구성하는데 대표적으로 쓰이구요, 검색하시면 많은 예시와 자료들이 있을꺼예요.
 block_ip 추가된 쿼리는 회원정보, ipblock 하고 각각 관계가 필요하니까 작성하신 쿼리대로 하시면 되는데, user_cnt를 두번 쓰셨다거나 그런 세부적인 항목은 쿼리 결과를 보면서 맞추시면 될꺼같아요.


by daviraba [2021.05.21 11:49:31]

안녕하세요 답을 주셨는데 답이 좀 늦었습니다.

어제 중간 보고일이라 너무 일이 바빳어요.

 

말씀데로 계층구성할때 사용을 한 경험이 있습니다. 저희 조직도를 구성하는데 사용을 했었습니다.

사실 그것도 쿼리를 어디서 배껴서 고쳐 쓴거라 개념적으로 이해가 안갔는데

뉴비디비님 말씀을 보고 생각해보니 데이터셋을 만들때 테이블 컬럼이 다른 테이블 컬럼을 참조할때 쓰이는 느낌으로 

이해를 하면 될까요?

 

뉴비디비님도 업무가 많이 바쁘실텐데 바쁘신 와중 제 고민에 관심 가져주셔서 너무 감사합니다.

좋은하루되세요


by 마농 [2021.05.20 08:56:13]

그룹바이 기본 개념에 위배되는 쿼리입니다.
MySQL 에서는 에러가 안나지만, 원래는 에러나는 쿼리입니다.
GROUP BY 기준항목이 아닌 일반항목은 집계함수와 함께 사용되어야 합니다.
집계함수 없이 바로 black.block_ip 가 사용되었네요. <- MySQL 에서만 에러 안남.
최종 쿼리엔 없지만 질문의 목표 결과에 id 가 포함된 부분도 같은 맥락으로 잘못된 부분입니다.
lg.access_ip 와 black.block_ip 가 1:1 관계라면?
GROUP BY 에 black.block_ip 를 추가하면 됩니다. -> GROUP BY lg.access_ip, black.block_ip
lg.access_ip 와 black.block_ip 가 1:m 관계라면?
어떤 기준을 세워서 어떤 방식으로 풀어야 할지 고민이 필요합니다.


by daviraba [2021.05.21 11:57:36]

안녕하세요 마농님.

지난번 알려주신 쿼리덕에 그날 업무이후 서점에 들러 SQL 기본서를 구매해서 공부했습니다.

계기에 용기를 불어넣어주셔서 감사합니다.

 

말씀대로 지금 사용하는 DBMS는 MYSQL입니다. 말씀데로 GROUP BY 관련 오류가 나지 않아서

문제해결은 했으나, 전혀 몰랐던 내용에다가 제가 읽었던 책에는 있지 않았던 내용인것 같아,

알려주신 지식을 이해하고자 마농님 댓글을 다시 읽어봐도 아직 제 지식이 부족해

이해가 잘 가지 않습니다. 공부를 더 해야할 것 같습니다.

 

말씀주신 쿼리대로 오늘 연습을 해 보고 새로운 지식을 쌓을 수 있도록 해보겠습니다.

 

업무에 바쁘실텐데 매번 관심 가져주셔서 너무 감사합니다.

좋은 하루되세요.


by 마농 [2021.05.21 13:05:17]

간단한 개념입니다.
예를 들어 학급의 성별 인원수를 구한다고 할때
여기에 이름을 함깨 출력한다고 생각해 보세요.
 

-- 1. 정상 쿼리
SELECT 성별
     , COUNT(*) 인원
  FROM 학급
 GROUP BY 성별
;
-- 결과 --
성별 : 인원
낭성 : 10명
여성 : 11명

-- 2. 비정상 쿼리(이름 추가)
SELECT 성별
     , COUNT(*) 인원
     , 이름
  FROM 학급
 GROUP BY 성별
;
-- 결과 --
성별 : 인원 : 이름
낭성 : 10명 : ???
여성 : 11명 : ???

 


by daviraba [2021.05.22 06:47:09]

안녕하세요 마농님.

어제 댓글은 봤는데 너무 바빠 대댓글을 못남겼습니다.

 

처음 말씀주셨던 기본개념에 위배되는 쿼리라는 말씀이 이해가 확실히 됬습니다.

여느 경우와 같이 원칙적으론 뭔가 출력이 되면 안되는데 워낙 이런실수를 하는사람들이 자주있다보니

두리뭉실 편의상 출력을 해준 느낌이네요..

 

짧은 경험상 이런 기준을 무시하고 습관들이면 나중에 큰 보답(?)을 받더라구요.. ㅡㅡ;

말씀 너무 감사드리며 더 열심히 공부하겠습니다.

오늘도 좋은하루되세요 감사합니다.

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