전에 COUNT 함수로 행 개수를 구할 때 COUNT의 인수로는 집합을 지정하였는데 이제까지 봐온 예제에서는 테이블 전체 혹은 WHERE 구로 검색한 행이 그 대상이였습니다.
지금부터는 그룹화를 하여 집계함수로 넘겨줄 집할을 그룹으로 나누는 방법을 설명하겠습니다. 이 같은 그룹화를 통해 집계함수의 활용범위를 넓힐 수 있습니다. 먼저 그룹화에 관해 간단히 살펴보겠습니다.
GROUP BY
그룹화 하기 위해서는 GROUP BY 구를 사용합니다. 전에 사용했던 sample51 테이블의 내용을 다시 확인해보겠습니다.
전에 DISTINCT함수를 사용할때 name 열에 중복된것을 제거한 적이 있었죠? 이제는 그룹화를 사용하여 하나로 묶어줄 것입니다.
SELECT name FROM sample51
GROUP BY name;
결과가 DISTINCT를 지정했을 때와 같은 것을 볼 수 있습니다. 각 그룹으로 묶인 값들은 서로 동일합니다. 즉, 결과적으로 각각의 그룹의 값이 반환됩니다. 따라서 DISTINCT와 같이 중복을 제거하는 효과가 있습니다.
그럼 GROUP BY는 DISTINCT로 중복을 제거하는 것과 어떤 차이가 있을까요? GROUP BY 구를 지정하는 경우에는 집계함수와 함께 사용하지 않으면 별 의미가 없습니다. GROUP BY 구로 그룹화된 각각의 그룹이 하나의 집합으로 집계함수의 인수로 넘겨지기 때문입니다. 구체적으로 어떻게 처리되는지 집계합수를 사용해 알아보겠습니다.
SELECT name, COUNT(name), SUM(quantity) FROM sample51
GROUP BY name;
GROUP BY에 의해 name 열 값이 A,B,C,NULL 이렇게 네 개 그룹으로 나뉩니다. A 그룹에는 두 개의 행이 있는데, COUNT는 행의 개수를 반환하므로 2가 되고, A 그룹에 해당하는 2개 행의 quantity 열 값은 각각 1과2인데 SUM은 합계를 구하는 집계함수 이므로 3이 됩니다.
HAVING
집계함수는 WHERE 구의 조건식에서는 사용할 수 없습니다. 예시를 들어 설명해보겠습니다.
SELECT name, COUNT(name) FROM sample51
WHERE COUNT(name) = 1 GROUP BY name;
위 코드는 name열을 그룹화하여 행 개수가 하나인 그룹을 검색하고 싶었지만 에러가 발생하였습니다. 이유는 GROUP BY 와 WHERE 구의 내부처리 순서와 관계있습니다. 즉, WHERE 구로 행을 검색하는 처리가 GROUP BY로 그룹화하는 처리보다 순서상 앞서기 때문입니다.
그렇다면 집계한 결과에서 조건에 맞는 값을 따로 걸러내려면 어떻게 해야할까요? 바로 HAVING 구를 이용하는 것입니다.
HAVING 구는 GROUP BY 구의 뒤에 기술하며 WHERE 구와 동일하게 조건식을 지정할 수 있습니다. 조건식에는 그룹별로 집계된 열의 값이나 집계함수의 계산결과가 전달된다고 생각하시면 됩니다. 이때 조건식이 참인 그룹값만 클라이언트에 반환됩니다.
그럼 앞에 에러가 발생한 SELECT 명령을 HAVING 구를 사용해 수정해보겠습니다.
SELECT name, COUNT(name) FROM sample51
GROUP BY name
HAVING COUNT(name) = 1;
name열로 그룹화 한 후 HAVING 구에서 COUNT(name) = 1 이라는 조건을 넣어 그룹화한 열의 개수가 1인 것만을 뽑았습니다. 한마디로 HAVING 구는 GROUP BY 로 지정한 것에 조건을 달아주는 것입니다.
그룹화보다도 나중에 처리되는 ORDER BY 구에서는 문제없이 집계함수를 사용할 수 있습니다. 즉, ORDER BY COUNT(name)과 같이 지정할 수 있습니다.
내부처리 순서 : WHERE -> GROUP BY -> HAVING -> SELECT -> ORDER BY
이게 지금까지 배운 구들의 내부처리 순서입니다. 이걸 보면 COUNT(name)에 별명을 붙히면 SELECT 보다 앞에있는 구들은 별명을 사용하지 못하는 것을 알 수있습니다. SELECT 보다 나중에 처리되는 ORDER BY 에서는 별명을 사용할 수 있습니다.
복수열의 그룹화
GROUP BY 를 사용할 때 주의할 점은 GROUP BY에 지정한 열 이외의 열은 집계함수를 사용하지 않은 채 SELECT 구에 기술해서는 안됩니다. 더 자세히 설명하자면 sample51에서 GROUP BY name으로 name열을 그룹화 했을 때, SELECT 구에 name을 지정하는 것은 문제없지만 no열이나 quantity열을 구에 그래도 지정하면 에러가 발생할 수 있습니다.
SELECT no, name, quantity FROM sample51
GROUP BY name;
GROUP BY로 그룹화 하면 클라이언트로 반환되는 결과는 그룹당 하나의 행이 됩니다. 하지만 name 열 값이 A인 그룹의 no 열 값이나 quantity 열 값은 두개입니다. 이때 그룹마다 하나의 값만을 반환해야 하므로 어느 것을 반환하면 좋을지 몰라 에러가 발생합니다.
SELECT MIN(no), name, SUM(quantity) FROM sample51
GROUP BY name;
하지만 이렇게 집계함수를 사용하면 집합은 하나의 값으로 계산되므로, 그룹마다 하나의 행을 출력할 수 있습니다.
SELECT no, quantity FROM sample51
GROUP BY no, quantity;
만약 이렇게 no와 quantity로 그룹화 한다면 GROUP BY no, quantity로 지정합니다. 이처럼 GROUP BY에서 지정한 열이라면 SELECT 구에 그대로 지정해도 됩니다.
결괏값 정렬
GROUP BY로 그룹화해도 실행결과 순서를 정렬할 수는 없습니다. 데이터베이스 내부 처리에서 같은 값을 그룹으로 나누는 과정에서 순서가 서로 바뀌는 부작용이 일어날 수도 있습니다. 이는 데이터베이스 내부처리 문제로 데이터베이스 제품에 따라 다릅니다. 확실한 것은 GROUP BY로 지정을 해도 정렬되지 않는다는 점입니다.
이럴 때는 ORDER BY 구를 사용해 결과를 정렬할 수 있습니다. GROUP BY 구로 그룹화 한 경우에도 ORDER BY 구를 사용해 정렬할 수 있습니다. 결괏값을 순서대로 정렬하기위해 ORDER BY 구를 이용해 지정해보겠습니다.
SELECT name, COUNT(name), SUM(quantity) FROM sample51
GROUP BY name
ORDER BY SUM(quantity) DESC;
quantity 의 합계를 큰 순서대로 보기위해 DESC를 이용해 내림차순으로 정렬했습니다.
'SQL' 카테고리의 다른 글
상관 서브쿼리 (0) | 2024.07.15 |
---|---|
서브쿼리 (0) | 2024.07.14 |
집계함수 (0) | 2024.07.14 |
데이터 추가, 삭제, 갱신 (0) | 2024.07.14 |
CASE 문으로 데이터 변환하기 (0) | 2024.07.13 |