SQL 에서 데이터에 대해 어떠한 조작이나 계산을 수행하려면 '함수' 라는 도구를 사용한다.
함수는 크게 2 종류로 나뉜다.
복수 행에 대해 집계를 수행하는 함수
단일 행의 값에 대해 조작이나 계산을 수행하는 함수
전자의 경우 '집약함수(집계함수)' 라 부른다.
COUNT 테이블의 행수를 알려주는 함수
SUM 테이블의 수치 데이터를 합계하는 함수
AVG 테이블의 수치 데이터 평균을 구하는 함수
MAX 테이블의 임의열 데이터 중 최대값을 구하는 함수
MIN 테이블의 임의열 데이터 중 최소값을 구하는 함수
이러한 집약함수는 기본적으로 NULL을 제외하고 집계하는데, COUNT 함수만은 'COUNT(*)' 로 표기하여 NULL을 포함한 전체 행을 집계한다. 또한, SUM, AVG 이외의 집약함수는 수치 데이터 외에도 이용 가능하다. 예를 들어, 도시명 최대값은 'Y' 로 시작하는 'Yosu', 최소값은 'A' 로 시작하는 'Andong' 이 된다.
mysql> select name from city where countrycode = 'KOR';
+------------+
| name |
+------------+
| Seoul |
| Pusan |
| Inchon |
~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~
| Yongchon |
| Sachon |
| Uiwang |
| Naju |
| Namwon |
| Tonghae |
| Mun-gyong |
+------------+
70 rows in set (0.00 sec)
mysql> select max(name) from city where countrycode = 'KOR';
+-----------+
| max(name) |
+-----------+
| Yosu |
+-----------+
1 row in set (0.00 sec)
mysql> select min(name) from city where countrycode = 'KOR';
+-----------+
| min(name) |
+-----------+
| Andong |
+-----------+
1 row in set (0.00 sec)
문자열을 집약하는 GROUP_CONCAT
문자열에 대한 집약함수는 SQL 표준에는 없으나 MySQL에는 'GROUP_CONCAT' 함수가 있다. 'GROUP_CONCAT' 함수는 콤마로 구분되는 문자열의 결합을 돌려준다. 만약 애플리케이션 측에서 사용시에는 함수 결과가 매우 길 수 있어 주의해야 한다.
mysql> select group_concat(name) from city where countrycode = 'KOR' and district = 'Chollabuk';
+-------------------------------------------+
| group_concat(name) |
+-------------------------------------------+
| Chonju,Iksan,Kunsan,Chong-up,Kimje,Namwon |
+-------------------------------------------+
1 row in set (0.00 sec)
DISTINCT로 중복 회피
DISTINCT 는 선택한 행의 중복을 배제하고 싶은 경우에 이용하는데, 집약함수에도 이용할 수 있다.
district(행정구역) 이 중복 결합
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| group_concat(district) |
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Seoul,Pusan,Inchon,Taegu,Taejon,Kwangju,Kyongsangnam,Kyonggi,Kyonggi,Kyonggi,Kyonggi,Chollabuk,Chungchongbuk,Kyonggi,Kyonggi,Kyongsangbuk,Kyongsangnam,Kyongsangnam,Kyonggi,Chungchongnam,Kyongsangnam,Chollabuk,Kyonggi,Kyongsangbuk,Kyonggi,Kyongsangbuk,Chollabuk,Cheju,Kyongsangnam,Chollanam,Chollanam,Kyonggi,Kang-won,Kyonggi,Kang-won,Kyonggi,Kang-won,Chungchongbuk,Kyongsangbuk,Chollanam,Kyongsangbuk,Kyonggi,Kyongsangnam,Kyonggi,Chungchongnam,Kyongsangnam,Kyongsangbuk,Chungchongnam,Kyonggi,Chollabuk,Chungchongbuk,Chungchongnam,Kyonggi,Kyongsangnam,Chungchongnam,Kyongsangbuk,Kyongsangnam,Kyongsangbuk,Chungchongnam,Chollanam,Kyongsangnam,Kyonggi,Chollabuk,Kyongsangbuk,Kyongsangnam,Kyonggi,Chollanam,Chollabuk,Kang-won,Kyongsangbuk |
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
DISTINCT 키워드로 중복을 회피한 경우
mysql> select group_concat(distinct district) from city where countrycode = 'KOR';
+------------------------------------------------------------------------------------------------------------------------------------------+
| group_concat(distinct district) |
+------------------------------------------------------------------------------------------------------------------------------------------+
| Cheju,Chollabuk,Chollanam,Chungchongbuk,Chungchongnam,Inchon,Kang-won,Kwangju,Kyonggi,Kyongsangbuk,Kyongsangnam,Pusan,Seoul,Taegu,Taejon |
+------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
데이터를 그룹으로 나누는 GROUP BY
대상이 되는 데이터를 몇 개의 그룹으로 나눠서 집약하는 것도 가능하다. 예를 들어, '행정구역(district) 마다', '국가(country) 마다' 집약하는 식이다.
SELECT ~ FROM 테이블명 GROUP BY 열명1 [ , 열명2, ... ]
GROUP BY 로 지정한 열을 '집약 키' 또는 '그룹화 키' 로 부르며, 복수 열을 콤마로 구분해 지정할 수 도 있다.
GROUP BY 없을 경우
mysql> select count(*) from city where countrycode = 'KOR';
+----------+
| count(*) |
+----------+
| 70 |
+----------+
1 row in set (0.00 sec)
'조건'이 있다면 WHERE 구문에 조건을 추가하고 싶지만 오류가 발생한다. COUNT 같은 집약함수를 작성할 수 있는 경우는 SELECT와 ORDER BY, HAVING 뿐이다.
mysql> select district, count(*) from city where countrycode = 'KOR' and count(*) = 4 group by district;
ERROR 1111 (HY000): Invalid use of group function
집약한 결과에 조건 지정
그룹마다 집약한 값을 조건으로 선택하고 싶다면 HAVING 뒤에 조건을 지정해야 한다.
SELECT ~ FROM ~ GROUP BY ~ HAVING 그룹의 값에 대한 조건
mysql> select district, count(*) from city where countrycode = 'KOR' group by district having count(*) = 6;
+---------------+----------+
| district | count(*) |
+---------------+----------+
| Chollabuk | 6 |
| Chungchongnam | 6 |
+---------------+----------+
2 rows in set (0.00 sec)
또한, 앞에서 말한대로 SELECT와 ORDER BY 에도 집약함수를 기술 할 수 있다.
mysql> select district, count(*) from city where countrycode = 'KOR' group by district having count(*) > 6 order by count(*) desc;
+--------------+----------+
| district | count(*) |
+--------------+----------+
| Kyonggi | 18 |
| Kyongsangnam | 11 |
| Kyongsangbuk | 10 |
+--------------+----------+
3 rows in set (0.00 sec)