생활코딩에 올라왔던 질문을 해결해 보도록 하겠습니다. google 코드 잼 등을 보면, 라운드들이 있어요. 그 중, 1차 라운드, 2차 라운드, 3차 라운드가 있어서, 이들 중 한 라운드에 통과하면, 다음 라운드에 진출한다는 규칙도 있습니다. 이런 것은 SQL로 어떻게 처리하면 좋을까요?

 

 test table은 아래와 같이 주어져 있습니다.

 

 

 여기서 name은 유저 이름, _check는 각 시험에 통과했는지를 나타냅니다. 여기서 _check는 'Y'나 'N'만 나올 수 있습니다. null 값은 허용이 되지 않는다고 해 봅시다. 어떠한 유저가 x차 라운드 중 최소한 하나 이상 통과했다면, 통과 여부를 'Y'로, 아니면 'N'으로 표기하고 싶습니다.

 

 


  다시 조건을 보면, 'Y'가 하나라도 있으면, 통과라고 했습니다. 그러면, 'Y'가 하나라도 있는지 없는지가 중요합니다. 이것을 어떻게 처리하면 좋을까요? 가장 쉽게 생각할 수 있는 방법은, 'Y'의 가중치를 1로, 'N'의 가중치를 0으로 두는 것입니다.

 

 

 그러면 y가 k회 출현하였다면, 그들의 가중치를 합한 값은 k가 됩니다. k가 0이라면 어떻게 될까요?

 

 

 0이 됩니다. 그러면, 이 방법을 쓸 수 있습니다.

 

 

 먼저, _check 필드의 값이 'Y'이면 1이고, 그렇지 않으면 0을 리턴하는 if 함수를 만들겠습니다. 그러면 이 쿼리는 무엇을 의미하나요? _check가 'Y'이면 1을, 아니면 0을 val이라는 필드에 뿌려주는 쿼리입니다.

 

 

 그러면, 임시 테이블 하나가 만들어 질 겁니다. 이 테이블의 이름을 t라고 하겠습니다. 그러면, 우리는 name에 따라서 묶어야 한다는 발상을 할 수 있습니다. 이는 group by 절로 가능한 부분입니다. 따로, group화가 된 결과에서 필터링을 해야 하는 조건이 없으므로, 그냥 그룹화만 시키겠습니다.

 

 무엇을 기준으로? name을. 그리고 각 그룹에 대해서, val의 합을 결과물로 낼 겁니다.

 

 

 7번째 줄을 보면, group by name이라고 되어 있어요. 이는 name 이라는 필드를 기준으로 그룹화를 시키겠다는 의미입니다. 그리고, name과 sum(val)을 봅시다. 이는 유저마다 가중치 val의 합을 구하는 것입니다. 여기까지의 결과는 아래와 같습니다.

 

 

 여기서 나온 임시 테이블을 t2라고 하겠습니다. t2.s의 값이 0이라면, 시험에 통과하지 않았음을 의미합니다. 따라서, 'N'으로 할 겁니다. 그렇지 않다면 'Y'로 적어주면 됩니다. 이는, 다시 if 함수를 써주면 가능한 부분입니다.

 

 

 1번째 줄과 11번째 줄만 바뀌었습니다. 이 부분만 잘 보시면 됩니다. 최종 쿼리의 결과는 다음과 같습니다.

 

 

 결과가 예상대로 나왔음을 알 수 있습니다. 그런데 쿼리가 꽤 기네요. 더 간단히 할 수 있는 방법이 없을까요?

 

 


 사실, name으로 그룹을 나누는 건 먼저 해야 하는 일이긴 합니다. 그러면, test로부터 name을 기준으로 grouping을 해 보면, 대략 아래와 같이 그룹핑이 될 겁니다.

 

 

 물론, 정렬이 된다는 보장은 없습니다. 그냥 그룹화만 하기 때문입니다. 이것을 가지고 어떻게 하면 좋을까요? 일단, 'Y'이면 가중치 1을, 'N'이면 가중치 0으로 잡고 sum 함수를 돌리면 됩니다.

 

 

 그림으로 그려보면 이런 상황입니다. 일단 여기까지 쿼리로 작성해 보겠습니다.

 

 

 여기서, 우리는 from과 group by가 먼저 해석되고, 다음에 select가 해석된다는 것을 보시면 됩니다. 이미 name을 기준으로 그룹화를 한 상태입니다. 그리고, 각각의 그룹들이 별개로 계산된다는 것을 보아야 합니다. 즉, sum(if(...))도 테이블 전체가 아니라, 각각의 그룹화된 대상들을 기준으로 묶입니다.

 

 

 그러면, 이 결과값을 기준으로 'Y'나 'N'을 내면 되겠네요.

 

 

 다시 if문을 써줘서, sum값을 기준으로 'Y'와 'N'을 출력하게 해 보겠습니다.

 

 

 그러면 위와 같은 결과가 나옵니다. 그룹끼리, if 함수, sum 함수, if 함수가 실행되어서 결과값으로 나왔다는 것이 중요합니다. 11줄짜리 쿼리에서 3줄짜리 쿼리로 줄었습니다. 그리고, 여기까지가 제가 생활코딩에 답변했던 내용입니다.

 

 


 그런데, max 함수나 min 함수가 어떠한 방식으로 동작하는지 안다면, 이것 역시 많이 줄일 수 있습니다.

 

 

 테이블 collation은 utf8mb4_0900_ai_ci입니다.

 

 

 각 필드의 Character Set은 utf8mb4, Collation은 utf8mb4_0900_ai_ci입니다. 'Y'는 'N'에 비해서, 사전순으로 뒤에 있습니다. 그리고 'Y'나 'N'은 대문자이기 때문에, 설령 대소문자 구분 안 하고 정렬한다는 게 있더라도, 사전순에 영향을 받지 않습니다. 따라서, 'Y'와 'N'의 max는 'Y'가 됩니다.

 

 

 따라서, group by를 하고, 그룹별로 나온 _check 값들을 모두 max한 값을 구해도 됩니다. 'Y'와 'N'의 max는 'Y'이고, 'Y'가 단 한 번이라도 나오지 않았다면 그룹화된 값은 'N'이기 때문입니다.

 

 

 groupping을 한 다음에, max 함수를 써 보겠습니다.

 

 

 쿼리의 실행 결과는 위와 같습니다.