[MYSQL] SELECT 시 데이터들이 동일한 옵션 값을 가지고 있는 경우 1개만 가져오고 null이라면 모두 출력하고 싶어요. 0 15 1,161

by 쿼리냠냠 [SQL Query] [2020.05.28 15:40:38]


구루비 회원님들 안녕하세요. 

쿼리를 짜다가 궁금한게 있어 문의드립니다. 

테이블 부터 알려드리겠습니다.
 

테이블명 : 상품테이블

id name optionStatus optionId price
1 바나나 true A 2000
2 수박 true A 1000
3 사과 false null 10
4 참치 false A 10
5 고등어 false B 10
6 치킨 true B 100
7 피자 true B 50
8 햄버거 true B 200
9 족발 false null 10


[질문]

1. 여기서 SELECT * FROM 상품테이블을 하면 위와같이 나옵니다.

2. 제가 원하는 결과값은 다음과 같습니다. 
 - optionStatus값이 true 면서(AND) optionId가 같으면 1개만 표시
    =>  (true인상태에서) optionId가 여러개라면 1개만표시
 - optionStatus값이 false라면 optionId값과 상관없이 모두 출력

id name optionStatus optionId
1 바나나 true A
3 사과 false null
4 참치 false A
5 고등어 false B
6 치킨 true B
9 족발 false null

==>
GROUP BY 절도써보다가 잘안되서 질문드립니다.


도와주세요!


*추가*
위의 조건을 기준으로 최저가로 데이터를 가져오기

 

id name optionStatus optionId price
2 수박 true A 1000
3 사과 false null 10
4 참치 false A 10
5 고등어 false B 10
7 피자 true B 50
9 족발 false null 10

 

 

 

감사합니다. 좋은하루 보내세요!

 

by pajama [2020.05.28 16:21:10]

id는 row 번호인가요? 일단 id를 그대로 했습니다..

 

with t as (
select 1 id, '바나나' name,  'true' optionStatus, 'A' optionId from dual
union all select 2, '수박'  ,  'true' , 'A'  from dual
union all select 3, '사과'  ,  'false', null from dual
union all select 4, '참치'  ,  'false', 'A'  from dual
union all select 5, '고등어',  'false', 'B'  from dual
union all select 6, '치킨'  ,  'true' , 'B'  from dual
union all select 7, '피자'  ,  'true' , 'B'  from dual
union all select 8, '햄버거',  'true' , 'B'  from dual
union all select 9, '족발'  ,  'false', null from dual
)
select id, name, optionStatus, optionId
from (select id, name, optionStatus, optionId, row_number() over (partition by optionStatus,optionId order by id) rn from t) t1
where (t1.optionStatus = 'true' and t1.rn = 1 ) or (t1.optionStatus = 'false')
order by id
;

 


by 쿼리냠냠 [2020.05.28 16:58:52]

안녕하세요. 답변감사드리니다.
row는 단순한 순번이 아닌 유니크한 id값이라고 생각하시면 될 것 같습니다.
(수정해놨습니다 감사합니다~)

현재 저는 MYSQL을 사용하고있습니다. 
구글에서 검색해보니 over partition 이 순번을 만들때 사용하는 것 같더라구요. 

순번은 필요없을 것 같아서 그부분을 지우고 돌려봤는데 잘 되지않네요 ㅠㅠ


by 마농 [2020.05.28 17:37:55]

답변 해준 정성을 봐서라도 그대로 한번 수행해 보세요. 임의로 빼지 말고.


by 마농 [2020.05.28 17:41:13]
WITH t AS
(
SELECT 1 id, '바나나' name, 'true' optionStatus, 'A' optionId
UNION ALL SELECT 2, '수박',   'true',  'A'
UNION ALL SELECT 3, '사과',   'false', null
UNION ALL SELECT 4, '참치',   'false', 'A'
UNION ALL SELECT 5, '고등어', 'false', 'B'
UNION ALL SELECT 6, '치킨',   'true',  'B'
UNION ALL SELECT 7, '피자',   'true',  'B'
UNION ALL SELECT 8, '햄버거', 'true',  'B'
UNION ALL SELECT 9, '족발',   'false', null
)
SELECT *
  FROM (SELECT id, name, optionStatus, optionId
             , RANK() OVER(PARTITION BY optionStatus, optionId
               ORDER BY CASE optionStatus WHEN 'true' THEN id END) rk
          FROM t
        ) a
 WHERE rk = 1
 ORDER BY id
;

 


by 쿼리냠냠 [2020.05.28 18:03:12]

안녕하세요. 답변감사합니다ㅠ

mysql버전이 낮아 사용하지 못하는 문법들이 있어 생략하고 실행해봤었습니다.  (WITH, OVER  ... )
단순히 순위를 매기는 건줄 알고요.( mysql 5.6버전대입니다. ) 

알려주신 쿼리문을 가지고 다른 방법으로 활용해보고 이것저것 시도해보고있습니다ㅠ

 

현재 해보려고하는건 
1. (1번)optionStatus값이 true 면서(AND) optionId가 같으면 1개만 추출하는 데이터를 조회하고 
2. (1번)에 데이터를 서브쿼리에 넣어서 값을 가져와 볼까 생각하고 있어요. 
 

id name optionStatus optionId subqueryResult
1 바나나 true A true
2 수박 true A  
3 사과 false null  
4 참치 false A  
5 고등어 false B true
6 치킨 true B  
7 피자 true B  
8 햄버거 true B  
9 족발 false null  

 


by 마농 [2020.05.28 18:17:46]
SELECT a.* 
  FROM t a
  LEFT OUTER JOIN t b
    ON a.optionStatus = 'true'
   AND a.optionStatus = b.optionStatus
   AND a.optionId = b.optionId
   AND a.id > b.id
 WHERE b.id IS NULL
;

 


by 쿼리냠냠 [2020.05.28 18:32:49]

감사드립니다. 

해당 쿼리문을 활용하여
덕분에 실제 테이블에 데이터를 가져왔습니다. 

좋은 하루보내세요!


by 쿼리냠냠 [2020.06.05 13:11:45]

 

 

마농님 안녕하세요. 

제가 질문했던 글에서 price라는 컬럼을 추가하였고,
기존에 알려주신 쿼리를 응용해보려하였지만 잘되지않아 문의드립니다.
( 기존 쿼리를 a.id > b.id라서 이거 대신 다른 조건문을 써서 가져와바야겠다 했는데 잘 되지 않더라구요 ㅠ...)

감사합니다. 좋은 하루보내세요!

조건은 다음과 같습니다.
 1.(기존과동일) optionStatus값이 true 면서(AND) optionId가 같으면 1개만 표시
    =>  (true인상태에서) optionId가 여러개라면 1개만표시
    (신규) => 1개만 표시하되, 최저가인 데이터를 가져오기.

 2.(기존과동일) optionStatus값이 false라면 optionId값과 상관없이 모두 출력


결과값 

id name optionStatus optionId price
2 수박 true A 1000
3 사과 false null 10
4 참치 false A 10
5 고등어 false B 10
7 피자 true B 50
9 족발 false null 10



 


by pajama [2020.05.28 20:42:22]

제가 mysql 버전을 고려하질 않았군요 ^^; 저도 덕분에 배워갑니다~


by 쿼리냠냠 [2020.05.29 09:42:28]

제가 버전을 표기 못한게 죄송합니다ㅠ
열심히 알려주셨는데 활용을 하지 못했네요. 

늘 쓰는 예약어나 문법을 사용하다보니 생소해서 
구글링해보니 버전문제더라구요! 

감사합니다. 좋은 하루보내세요!


by 마농 [2020.06.05 15:05:58]

간단하게 생각하면 다음과 같이 변경하면 됩니다.
 - 변경전 : AND a.id > b.id
 - 변경후 : AND a.price > b.price
다만, id 의 경우엔 유일값이지만, price 는 중복이 가능한 값이므로 단순하게 변경해선 안됩니다.
중복 값에 대한 표현 방법을 정해야 합니다.
중복값이 없다거나 중복이 있다면 모두 보여준다라고 하면? AND a.price > b.price 하면 됩니다.
중복이 있을 경우 그중 하나를 선택해서 보여줘야 한다면?
선택 기준을 제시해야 합니다. (예 : ID 가 작은거 우선)
최저가 중에 ID 가 작은 거 우선이라면 다음과 같이.
AND ( (a.price > b.price) OR (a.price = b.price AND a.id > b.id) )


by 쿼리냠냠 [2020.06.05 15:41:45]

 

 

늘 친절한 답변 감사드립니다.

최저가(a.price>b.price)를 했을때 중복값이 발생가능성이 있으니 기준값을 조건문에 추가를 해야한다는 말을 이해했습니다.

(id와 달리 price는 유니크값이 아니니!)

 

다만 알려주신 쿼리문으로는 원하는 결과값을 가져올 수가 없어 고민하고있었습니다. 

쿼리를 돌리면 다음과 같습니다.

SELECT a.* ,b.*
  FROM t1 a
  LEFT OUTER JOIN t1 b
    ON a.optionStatus = 'true'
   AND a.optionStatus = b.optionStatus
   AND a.optionId = b.optionId
   AND ( (a.price > b.price) OR (a.price = b.price AND a.id > b.id) )
   order by a.id asc;
 #WHERE b.id IS NULL;

==> 일단 b.id is null은 주석처리를 하였습니다.

*결과데이터*

a.id

a.name

a.optionStatus

a.optionId

a.price

b.id

b.name

b.optionStatus

b.optionId

b.price

1

바나나

TRUE

A

3000

2

수박

TRUE

A

2000

2

수박

TRUE

A

2000

NULL

NULL

NULL

NULL

NULL

3

사과

FALSE

NULL

1000

NULL

NULL

NULL

NULL

NULL

4

참치

FALSE

A

1000

NULL

NULL

NULL

NULL

NULL

5

고등어

FALSE

B

20

NULL

NULL

NULL

NULL

NULL

6

치킨

TRUE

B

100

NULL

NULL

NULL

NULL

NULL

7

피자

TRUE

B

50

8

햄버거

TRUE

B

200

7

피자

TRUE

B

50

6

치킨

TRUE

B

100

8

햄버거

TRUE

B

200

6

치킨

TRUE

B

100

9

족발

FALSE

NULL

1000

NULL

NULL

NULL

NULL

NULL

 

1. id값이 [6/7/8]의 경우 option값이 true면서 optionId가 동일하기 때문에 1개만 가져와합니다.
2. 기존 쿼리는 WHERE b.id IS NULL;이기 때문에 최저가인 '피자'가 빠지게 됩니다. 
==> a,b 비교하는 데이터자체를 변경한 후 말씀해주신 중복값을 제거해야되는데 
      비교대상의 데이터자체를 변경하는데 어려움이 있습니다ㅠ
 

 


by 마농 [2020.06.05 16:05:12]

50 보다 100 이 적은 걸로 나왔네요.
이유는 숫자가 아닌 문자이기 때문입니다.
숫자 50 은 100 보다 작지만
문자 '50' 은 '100' 보다 크죠. '5' 가 '1' 보다 크니.
애초에 price 항목이 숫자로 생성되었어야 합니다.
테이블 속성을 변경하면 좋고.
이제 와서 그렇게는 안된다면? 쿼리에서 숫자로 변형하여 비교하면 됩니다.


by 쿼리냠냠 [2020.06.05 16:11:48]

감사합니다. 

타입변환을 해보니 잘되군요. 

문자/숫자의 대소비교를 주의하면서 짜야겠네요.

구글에 검색했을때 
마뇽님과 비슷하게 쿼리짜신포스팅이 있었는데
왜 난 안되지했었는데.... 

감사합니다. 좋은 하루보내세요!


by 마농 [2020.06.05 16:19:33]

"잘 안되요" 식으로 질문하면 해결 못합니다.
구체적으로 질문해주셔야 답이 보입니다.
작성 쿼리와 수행결과까지 상세하게 올려주시니 바로 원인이 보이네요.

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