카운트(*)를 해야 하나요, 말아야 하나요?
일반적으로 다음과 같은 질문을 하는 것은 좋지 않은 생각입니다.
SELECT * FROM `group_relations`
그러나 카운트만 원하는 경우 테이블을 변경할 수 있지만 결과는 같기 때문에 이 쿼리를 사용해야 합니까?
SELECT COUNT(*) FROM `group_relations`
또는 보다 구체적일수록
SELECT COUNT(`group_id`) FROM `group_relations`
후자가 더 빠를 수도 있다고 생각합니다만, 그 밖에 고려해야 할 것이 있습니까?
업데이트: 이 경우 InnoDB를 사용하고 있습니다.구체적으로 말씀 드리지 못해 죄송합니다.
문제의 열이 NOT NULL이 아닌 경우 두 쿼리는 모두 동일합니다.group_id에 늘 값이 포함되어 있는 경우
select count(*)
모든 행이 카운트되지만,
select count(group_id)
group_id가 늘이 아닌 행만 카운트합니다.
또한 MySQL과 같은 일부 데이터베이스 시스템은 사용자가 카운트(*)를 요청할 때 최적화를 채택하므로 이러한 쿼리는 특정 쿼리보다 약간 더 빠릅니다.
개인적으로는 숫자만 셀 때 null과 함께 안전하기 위해 카운트(*)를 하고 있습니다.
내 기억이 맞는다면 MYSQL COUNT(*)에서는 모든 행을 카운트하는 반면 COUNT(column_name)에서는 지정된 열에 NULL이 아닌 값이 있는 행만 카운트합니다.
COUNT(*)는 모든 행을 카운트하는 반면 COUNT(column_name)는 지정된 열에 NULL 값이 없는 행만 카운트합니다.
MySQL에서 주의해야 할 사항:
COUNT()는 행 수가 캐시되기 때문에 MyISAM 테이블에서 * 또는 null이 아닌 컬럼의 속도가 매우 빠릅니다.InnoDB에는 행 개수 캐시가 없으므로 열이 null일 수 있는지 여부에 관계없이 COUNT(*) 또는 COUNT(column_name)에 대한 성능 차이는 없습니다.MySQL Performance 블로그에서 이 게시물의 차이점에 대한 자세한 내용을 볼 수 있습니다.
SELECT COUNT(1) FROM
group_relations에서 에 조금 더.
편집: 몇 가지 조사를 해보니 일부 DB에서만 이러한 현상이 발생한다는 것을 알 수 있었습니다.sqlserver에서는 1 또는 *를 사용하는 것이 동일하지만 Oracle에서는 1을 사용하는 것이 더 빠릅니다.
sqlserver와 같이 select(1)로 쿼리를 변경하는 것처럼 mysql에서는 차이가 없는 것 같습니다.제가 당신을 오해했다면 죄송합니다.
저도 궁금했어요.문서와 이론적 답을 읽는 것은 괜찮지만, 나는 그것들을 경험적 증거와 균형을 맞추는 것을 좋아한다.
MySQL 테이블(InnoDB)에는 5,607,997개의 레코드가 포함되어 있습니다.테이블은 개인 샌드박스에 있기 때문에 내용은 정적이며 다른 누구도 서버를 사용하지 않습니다.저는 이것이 실적에 대한 외부의 영향을 효과적으로 제거한다고 생각합니다.auto_increment primary key 필드(Id)가 있는 테이블은 where 구 테스트에 사용할 null이 되지 않습니다(WHERE ID는 NULL이 아닙니다).
테스트 실행 시 발생할 수 있는 다른 문제는 캐시뿐입니다.처음 쿼리를 실행할 때는 항상 동일한 인덱스를 사용하는 후속 쿼리보다 속도가 느립니다.아래를 캐시 시드 호출이라고 합니다.조금 혼동하기 위해 데이터에 관계없이 항상 true로 평가된다는 것을 알고 있는 절과 함께 실행했습니다(TRUE = TRUE).
결과는 다음과 같습니다.
쿼리 타입
| w/o WHERE | where id is not null | where true=true
카운트()
| 9 min 30.13 sec ++ | 6 min 16.68 sec ++ | 2 min 21.80 sec ++
| 6 min 13.34 sec | 1 min 36.02 sec | 2 min 0.11 sec
| 6 min 10.06 se | 1 min 33.47 sec | 1 min 50.54 sec
카운트(Id)
| 5 min 59.87 sec | 1 min 34.47 sec | 2 min 3.96 sec
| 5 min 44.95 sec | 1 min 13.09 sec | 2 min 6.48 sec
카운트(1)
| 6 min 49.64 sec | 2 min 0.80 sec | 2 min 11.64 sec
| 6 min 31.64 sec | 1 min 41.19 sec | 1 min 43.51 sec
++이것은 캐시 시드 호출로 간주됩니다.그것은 다른 것들보다 느릴 것으로 예상된다.
그 결과가 말해주고 싶군요.COUNT(Id)는 보통 다른 항목보다 엣지 처리됩니다.Where 절을 추가하면 true로 평가되는 절인 경우에도 액세스 시간이 크게 단축됩니다.스위트 스팟이 카운트(Id)인 것 같습니다...여기서 ID는 null이 아닙니다.
다른 사람의 결과를 보고 싶다.테이블이 작거나 당신이 세고 있는 분야보다 다른 분야에 대한 조항이 있으면 좋을 것 같다.제가 고려하지 않은 다른 변형이 있을 겁니다.
대체 수단 모색하다
이 커집니다.COUNT
쿼리가 느려집니다.가장 중요한 것은 해결하려는 문제의 성격을 고려하는 것이라고 생각합니다.를 들어,가 '아예', '아예'를 사용하고 있습니다.COUNT
결과 세트의 총 페이지 수를 결정하기 위해 큰 레코드 세트의 페이지 수를 생성할 때 쿼리합니다.
COUNT
쿼리가 느려집니다. 느린 쿼리를 사이드스텝할 수 있는 페이지 지정 컨트롤을 표시하는 다른 방법을 고려할 수 있습니다.을 사용법
디노멀라이즈
특정 카운트에 일치하는 레코드의 수를 반드시 알아야 하는 경우 데이터 정규화 해제의 고전적인 기법을 고려하십시오.조회 시 행 수를 카운트하는 대신 레코드 삽입 시 카운터를 증가시키고 레코드 삭제 시 카운터를 감소시키는 것을 검토하십시오.
이 작업을 수행할 경우 idempotent 트랜잭션 연산을 사용하여 정규화 해제된 값을 동기화하는 것을 고려해 보십시오.
BEGIN TRANSACTION;
INSERT INTO `group_relations` (`group_id`) VALUES (1);
UPDATE `group_relations_count` SET `count` = `count` + 1;
COMMIT;
또는 RDBMS가 데이터베이스 트리거를 지원하는 경우 데이터베이스 트리거를 사용할 수 있습니다.
사용하시는 아키텍처에 따라서는 memcached와 같은 캐싱 레이어를 사용하여 정규화 해제된 값을 저장, 증가 및 감소시키고 캐시 키가 없을 때 단순히 느린 카운트 쿼리로 넘어가는 것이 타당할 수 있습니다.이렇게 하면 휘발성이 매우 높은 데이터가 있는 경우 전반적인 쓰기 경합을 줄일 수 있지만, 이러한 경우에는 개 파일 효과에 대한 솔루션을 고려해야 합니다.
MySQL ISAM 테이블은 COUNT(*)용으로 최적화되어 있어야 하며 전체 테이블 검색을 건너뜁니다.
COUNT 내의 아스타리스크는 테이블의 모든 필드를 선택하기 위한 아스타리스크와는 관계가 없습니다.COUNT(*)가 COUNT(필드)보다 느리다고 하는 것은 완전히 헛소리입니다.
COUNT(*)를 선택하는 것이 COUNT(필드)를 선택하는 것보다 빠르다고 직감합니다.필드 대신 COUNT에 "*"를 지정한 것이 RDBMS에 의해 검출된 경우 RDBMS는 카운트를 증가시키기 위해 아무것도 평가할 필요가 없습니다.한편, COUNT 로 필드를 지정하면, RDBMS 는 필드가 늘인지 아닌지를 항상 평가합니다.
그러나 필드가 null인 경우 COUNT에서 필드를 지정합니다.
(*) 사실과 신화 카운트:
속설: "InnoDB가 카운트(*) 쿼리를 제대로 처리하지 않습니다.":
WHERE 절이 있는 경우 대부분의 카운트(*) 쿼리는 모든 스토리지 엔진에서 동일한 방식으로 실행됩니다. 그렇지 않은 경우 InnoDB가 전체 테이블 검색을 수행해야 합니다.
팩트: InnoDB는 where 절이 없으면 카운트(*) 쿼리를 최적화하지 않습니다.
기본 키와 같은 인덱스된 열을 사용하여 계산하는 것이 가장 좋습니다.
SELECT COUNT(`group_id`) FROM `group_relations`
이것은 Sebastian이 이미 말한 것처럼 실제로 무엇을 달성하려고 하는가에 달려 있습니다. 즉, 당신의 의도를 명확히 하는 것입니다.행만 셀 경우 카운트(*)로 이동하거나 단일 열을 카운트(column)로 이동합니다.
DB 벤더도 확인해 볼 가치가 있습니다.예전에 Informix를 사용했을 때 COUNT(*)에 대한 최적화 기능이 있었는데, 이는 단일 또는 여러 개의 컬럼을 세는 것에 비해 쿼리 계획 실행 비용이 1이었다.
SELECT COUNT (1) FROM group_relations를 시도하면 컬럼에서 정보를 취득하지 않기 때문에 조금 빨라집니다.
이전에는 COUNT(*)보다 COUNT(1)가 빨랐지만, 최신 DBMS는 열을 알고 싶지 않을 정도로 스마트하기 때문에 더 이상 그렇지 않습니다.
MySQL에서 들은 조언은 일반적으로 이러한 트릭을 기반으로 쿼리를 최적화하려고 하면 장기적으로 문제가 될 수 있다는 것입니다.MySQL의 역사를 보면 최적화 도구의 작동 방식에 의존하는 고성능 기술이 다음 릴리스에서 병목현상이 되는 사례가 있습니다.
모든 행 수를 계산하려면 COUNT(*)를 사용합니다.null이 아닌 열을 카운트하려면 COUNT(col) WHERE col IS NOT NULL을 사용합니다. 인덱스를 적절하게 지정하고 최적화에 맡깁니다.자체 쿼리 수준의 최적화를 시도하면 내장 옵티마이저의 효과가 저하될 수 있습니다.
단, 옵티마이저가 더 쉽게 속도를 높일 수 있도록 쿼리에서 수행할 수 있는 작업이 있지만 COUNT는 그 중 하나라고 생각하지 않습니다.
편집: 하지만 위의 답변에 있는 통계는 흥미롭습니다.이 경우 옵티마이저에 실제로 어떤 것이 작용하고 있는지는 잘 모르겠습니다.일반적으로 쿼리 수준의 최적화를 말하는 것입니다.
일반적으로 다음과 같은 질문을 하는 것은 좋지 않은 생각입니다.
SELECT * FROM `group_relations`
그러나 카운트만 원하는 경우 테이블을 변경할 수 있지만 결과는 같기 때문에 이 쿼리를 사용해야 합니까?
SELECT COUNT(*) FROM `group_relations`
당신의 질문이 시사하는 바와 같이, 그 이유는SELECT *
테이블을 변경하면 코드 변경이 필요할 수 있습니다.그것은 에 해당되지 않는다COUNT(*)
특별한 행동을 원하는 경우는 거의 없습니다.SELECT COUNT('group_id')
를 사용하면 일반적으로 레코드 수를 알 수 있습니다.그렇구나COUNT(*)
용도가 있으니까 쓰세요.
언급URL : https://stackoverflow.com/questions/457263/should-i-count-or-not
'programing' 카테고리의 다른 글
WAMP/MySQL 오류가 올바른 언어로 표시되지 않음 (0) | 2022.11.20 |
---|---|
600851475143의 "Integer number too large" 오류 메시지 (0) | 2022.11.20 |
실제로 Python 3.3에서 "유효율" 구문의 주요 용도는 무엇입니까? (0) | 2022.11.20 |
휴지 상태 오류: org.hibernate.NonUniqueObjectException: 동일한 식별자 값을 가진 다른 개체가 세션에 이미 연결되어 있습니다. (0) | 2022.11.20 |
MySQL에서 열과 테이블 이름은 대소문자를 구분합니까? (0) | 2022.11.20 |