백엔드 Back-end/데이터베이스 Database

Q. MySQL에서 count(*)과 count()의 차이는?

Tap to restart 2023. 1. 29. 16:00
반응형

A. count(*)은 WHERE 조건이 있다면 조건에 해당하는 모든 행수를 얻을 수 있고, count(칼럼명)의 경우는 해당 칼럼명의 데이터를 조회해서 NULL이 아닌 데이터가 들어 있는 행수를 얻을 수 있다.

InnoDB 엔진 예

간단히 coffee란 테이블을 만들어서 5개의 데이터를 넣었다. 맨 마지막 5번 데이터는 NULL을 넣었다.

이 상태에서 SELECT count(*) FROM coffee; 를 실행하면 5가 나온다.

만약 칼럼명인 name으로 SELECT count(name) FROM coffee; 하게 되면 4가 결과로 나온다.

두 경우 모두 Query Cost를 살펴보면 0.75이고 Full Table Scan이 발생한다.

SELECT count(*) FROM coffee;

SELECT count(name) FROM coffee;

MyISAM 엔진 예

위 예제는 모두 테이블이 InnoDB 엔진을 사용하는 경우다. 만약 MyISAM 엔진을 쓴다면 어떨까?

MyISAM 엔진으로 설정해서 coffee_myisam이란 테이블을 만들고 같은 데이터를 넣었다.

SELECT count(*) FROM coffee_myisam; 의 경우 테이블 풀스캔이 발생하지 않은 것을 확인할 수 있다.

참고로 count(1)과 count(*)은 동일하다.

SELECT count(name) FROM coffee_myisam; 의 경우 풀스캔이 발생한다. 그리고 Query cost가 1.01로 InnoDB보다 더 비용이 발생했다.

그렇다면 count(*)로 조건을 넣으면 어떻게 될까?

Full Table Scan이 발생하고 쿼리 코스트는 같다.

위 결과를 통해서 알 수 있는 것은 MyISAM이 전체 행 조회 때만 빠르고 다른 경우에는 InnoDB보다 느리다는 점이다.

공식 문서 살펴보기

공식 문서에서 위와 같은 결과가 나타나는 이유를 확인할 수 있다.

COUNT(expr) [over_clause]
Returns a count of the number of non-NULL values of expr in the rows retrieved by a SELECT statement. The result is a BIGINT value.
If there are no matching rows, COUNT() returns 0. COUNT(NULL) returns 0.
...
COUNT(*) is somewhat different in that it returns a count of the number of rows retrieved, whether or not they contain NULL values.
For transactional storage engines such as InnoDB, storing an exact row count is problematic. Multiple transactions may be occurring at the same time, each of which may affect the count.
InnoDB does not keep an internal count of rows in a table because concurrent transactions might “see” different numbers of rows at the same time. Consequently, SELECT COUNT(*) statements only count rows visible to the current transaction.
...
InnoDB handles SELECT COUNT(*) and SELECT COUNT(1) operations in the same way. There is no performance difference.
For MyISAM tables, COUNT(*) is optimized to return very quickly if the SELECT retrieves from one table, no other columns are retrieved, and there is no WHERE clause. For example:
mysql> SELECT COUNT(*) FROM student;
This optimization only applies to MyISAM tables, because an exact row count is stored for this storage engine and can be accessed very quickly. COUNT(1) is only subject to the same optimization if the first column is defined as NOT NULL.
출처: Mysql 8.0 Documentation 12.20.1 Aggregate Function Descriptions

MyISAM과 InnoDB에서 COUNT(*)의 차이

위 내용을 보면 왜 MyISAM에서는 조건 없이 SELECT COUNT(*) 할 경우 테이블 풀 스캔이 없고, InnoDB에서는 발생하는지 알 수 있다. InnoDB는 트랜젝션이 지원되어서 테이블의 행수rows를 저장하지 않는다. MyISAM은 트랜젝션이 지원되지 않기 때문에 테이블 행수를 저장해둔다.

COUNT 사용 주의사항

위에서 살펴봤듯이 InnoDB 엔진을 사용할 경우 COUNT 사용하면 항상 테이블 풀 스캔이 발생한다. 쿼리 코스트가 높다. 그래서 굉장히 데이터가 많은 경우라면 COUNT 사용을 자제해야 한다.

관련 글

Q. MySQL 데이터베이스 쿼리 비용Query Cost를 확인하는 방법은?

반응형