어느 것이 가장 빠릅니까? `table`에서 SELECT SQL_CALC_FOUND_ROWS 또는 SELECT COUNT (*)


176

일반적으로 페이징에 사용되는 SQL 쿼리에 의해 리턴되는 행 수를 제한하는 경우 총 레코드 수를 판별하는 두 가지 방법이 있습니다.

방법 1

SQL_CALC_FOUND_ROWSoriginal에 옵션을 포함 SELECT시키고 다음을 실행하여 총 행 수를 가져옵니다 SELECT FOUND_ROWS().

SELECT SQL_CALC_FOUND_ROWS * FROM table WHERE id > 100 LIMIT 10;
SELECT FOUND_ROWS();  

방법 2

쿼리를 정상적으로 실행 한 다음 다음을 실행하여 총 행 수를 얻습니다. SELECT COUNT(*)

SELECT * FROM table WHERE id > 100 LIMIT 10;
SELECT COUNT(*) FROM table WHERE id > 100;  

어떤 방법이 가장 좋고 빠릅니까?

답변:


120

때에 따라 다르지. 이 주제에 대한 MySQL 성능 블로그 게시물을 참조하십시오 : http://www.mysqlperformanceblog.com/2007/08/28/to-sql_calc_found_rows-or-not-to-sql_calc_found_rows/

간단히 요약하면 Peter는 색인과 기타 요인에 따라 달라집니다. 게시물에 대한 많은 의견은 SQL_CALC_FOUND_ROWS가 두 쿼리를 실행하는 것보다 거의 항상 느리며 때로는 최대 10 배까지 느리다고 말합니다.


27
나는 이것을 확인할 수 있습니다-방금 168,000 행 데이터베이스에서 4 개의 조인으로 쿼리를 업데이트했습니다. 를 사용하여 처음 100 개의 행만 선택하면 SQL_CALC_FOUND_ROWS20 초가 걸렸습니다. 별도의 COUNT(*)쿼리를 사용하는 데 5 초 미만이 걸렸습니다 (카운트 + 결과 쿼리 모두).
Sam Dufel

9
매우 흥미로운 발견. 때문에 MySQL의 설명서가 명시 적으로 알 수 SQL_CALC_FOUND_ROWS빠를 것이다, 나는 그것이 실제로 (있는 경우) 어떤 상황에서 궁금 입니다 빨리!
svidgen

12
오래된 주제이지만 여전히 흥미로운 사람들을 위해! 10 검사에서 INNODB에 대한 검사를 완료했습니다. 9.2 (1 쿼리)에 대해 26 (2query)임을 알 수 있습니다. SELECT SQL_CALC_FOUND_ROWS tblA. *, tblB.id AS 'b_id', tblB.city AS 'b_city', tblC.id AS 'C_ID'tblC.type AS 'c_type'tblD.id AS 'D_ID'tblD.extype AS 'd_extype'tblY.id AS 'y_id'FROM tblY.ydt AS의 y_ydt tblA, tblB, tblC, tblD, tblY WHERE tblA.b = tblC.id 및 tblA.c = tblB.id 및 tblA.d = tblD.id 및 tblA.y = tblY.id
Al Po

4
방금이 실험을 실행했으며 SQLC_CALC_FOUND_ROWS가 두 개의 쿼리보다 훨씬 빠릅니다. 이제 내 기본 테이블은 65k에 불과하고 수백 개의 조인이지만 SQLC_CALC_FOUND_ROWS의 유무에 관계없이 기본 쿼리는 0.18 초가 걸리지 만 COUNT ( id)를 사용하여 두 번째 쿼리를 실행하면 0.25 만 걸립니다 .
transilvlad

1
가능한 성능 문제 외에도 FOUND_ROWS()MySQL 8.0.17에서 더 이상 사용되지 않는 것을 고려하십시오 . @ madhur-bhaiya의 답변도 참조하십시오.
arueckauer

19

"최상의"접근 방식을 선택할 때 속도보다 더 중요한 고려 사항은 코드의 유지 관리 성과 정확성입니다. 그렇다면 단일 쿼리 만 유지하면되기 때문에 SQL_CALC_FOUND_ROWS가 선호됩니다. 단일 쿼리를 사용하면 기본 쿼리와 개수 쿼리 사이에 미묘한 차이가 생길 수 있으므로 COUNT가 부정확 할 수 있습니다.


11
이것은 설정에 따라 다릅니다. 어떤 종류의 ORM 또는 쿼리 빌더를 사용하는 경우 두 쿼리의 기준을 동일하게 사용하고 선택 필드를 카운트로 바꾸고 한계를 삭제하는 것이 매우 쉽습니다. 기준을 두 번 쓰면 안됩니다.
mpen

독점 MySQL 기능을 사용하는 것보다 이해하기 쉬운 두 가지 간단한 표준 SQL 쿼리를 사용하여 코드를 유지하는 것이 좋습니다. 주목할만한 가치는 최신 MySQL 버전에서는 더 이상 사용되지 않습니다.
thomasrutter

15

MySQL은 SQL_CALC_FOUND_ROWS8.0.17 이후 버전에서 기능을 더 이상 사용하지 않습니다 .

따라서 항상을 사용 하여 쿼리를 실행 LIMIT한 다음 추가 행이 있는지 여부를 결정 COUNT(*)하지 않고 두 번째 쿼리를 실행하는 것이 좋습니다LIMIT .

에서 문서 :

SQL_CALC_FOUND_ROWS 쿼리 수정 자와 함께 제공되는 FOUND_ROWS () 함수는 MySQL 8.0.17부터 더 이상 사용되지 않으며 향후 MySQL 버전에서 제거 될 예정입니다.

COUNT (*)는 특정 최적화 대상입니다. SQL_CALC_FOUND_ROWS로 인해 일부 최적화가 비활성화됩니다.

대신 다음 쿼리를 사용하십시오.

SELECT * FROM tbl_name WHERE id > 100 LIMIT 10;
SELECT COUNT(*) WHERE id > 100;

또한 MySQL WL # 12615SQL_CALC_FOUND_ROWS 에서 설명한 것처럼 일반적으로 더 많은 문제가있는 것으로 관찰되었습니다 .

SQL_CALC_FOUND_ROWS에는 여러 가지 문제가 있습니다. 우선, 느립니다. COUNT ( )가 전체 결과 세트를 검색 할 때 수행 할 수없는 최적화 (예 : 파일 정렬)를 사용할 수 있으므로 LIMIT을 사용하여 쿼리를 실행 한 다음 동일한 쿼리에 대해 별도의 SELECT COUNT ( )를 사용하는 것이 더 저렴합니다. COUNT (*)에 대해서는 건너 뛸 수 있지만 CALC_FOUND_ROWS에서는 올바른 결과를 보장하기 위해 일부 파일 정렬 최적화를 비활성화해야합니다)

더 중요한 것은 여러 상황에서 의미가 매우 불분명하다는 것입니다. 특히 쿼리에 여러 쿼리 블록이있는 경우 (예 : UNION) 유효한 쿼리를 생성하는 것과 동시에 "있을 것"행 수를 계산할 수있는 방법이 없습니다. 반복자 실행자가 이러한 종류의 쿼리를 향해 진행함에 따라 동일한 의미를 유지하는 것은 실제로 어렵습니다. 또한 쿼리에 여러 LIMIT가있는 경우 (예 : 파생 테이블의 경우) SQL_CALC_FOUND_ROWS 중 어떤 것이 참조되어야하는지 명확하지는 않습니다. 따라서 이러한 사소한 쿼리는 반복 실행기에서 이전과 비교했을 때 반드시 다른 의미를 갖습니다.

마지막으로 SQL_CALC_FOUND_ROWS가 유용한 것처럼 보이는 대부분의 사용 사례는 LIMIT / OFFSET 이외의 다른 메커니즘으로 간단히 해결해야합니다. 예를 들어, 전화 번호부는 레코드 번호가 아닌 문자 (UX 및 색인 사용)로 페이지를 매겨 야합니다. 토론은 우편 번호로 페이지를 매기는 것이 아니라 날짜별로 (인덱스 사용을 허용하는) 순서대로 점점 더 많이 스크롤됩니다. 등등.


이 두 선택을 원자 연산으로 수행하는 방법은 무엇입니까? 누군가가 SELECT COUNT (*) 쿼리 앞에 행을 삽입하면 어떻게됩니까? 감사.
Dom

@Dom MySQL8 +를 사용하는 경우 Window 함수를 사용하여 단일 쿼리에서 두 쿼리를 모두 실행할 수 있습니다. 그러나 인덱스가 올바르게 사용되지 않으므로 최적의 솔루션이 아닙니다. 또 다른 옵션은 이러한 두 개의 쿼리를 포위하는 것입니다 LOCK TABLES <tablename>UNLOCK TABLES. 세 번째 옵션 및 (최상의 IMHO)는 페이지 매김을 다시 생각하는 것입니다. 읽어보십시오 : mariadb.com/kb/en/library/pagination-optimization
Madhur Bhaiya


8

IMHO, 2 개의 쿼리가 필요한 이유

SELECT * FROM count_test WHERE b = 666 ORDER BY c LIMIT 5;
SELECT count(*) FROM count_test WHERE b = 666;

SQL_CALC_FOUND_ROWS를 사용하는 것보다 빠릅니다.

SELECT SQL_CALC_FOUND_ROWS * FROM count_test WHERE b = 555 ORDER BY c LIMIT 5;

특별한 경우로보아야합니다.

실제로 ORDER + LIMIT와 동등한 내재 된 항목의 선택 성과 비교 한 WHERE 절의 선택성에 따라 달라집니다.

Arvids가 의견 ( http://www.mysqlperformanceblog.com/2007/08/28/to-sql_calc_found_rows-or-not-to-sql_calc_found_rows/#comment-1174394 ) 에서 언급했듯이 EXPLAIN이 사용하는지 여부는 템포 레이 테이블은 SCFR이 더 빠른지 아닌지를 알기에 좋은 기반이되어야합니다.

그러나 ( http://www.mysqlperformanceblog.com/2007/08/28/to-sql_calc_found_rows-or-not-to-sql_calc_found_rows/#comment-8166482 )를 추가 했으므로 결과는 실제로 사례에 따라 다릅니다. 특정 페이지 매김 자에 대해 다음과 같은 결론을 얻을 수 있습니다. 다음 페이지에 대해서는 SCFR을 사용하십시오.”!


6

불필요한 SQL을 제거하면 COUNT(*)보다 빠릅니다 SQL_CALC_FOUND_ROWS. 예:

SELECT Person.Id, Person.Name, Job.Description, Card.Number
FROM Person
JOIN Job ON Job.Id = Person.Job_Id
LEFT JOIN Card ON Card.Person_Id = Person.Id
WHERE Job.Name = 'WEB Developer'
ORDER BY Person.Name

그런 다음 불필요한 부분없이 계산하십시오.

SELECT COUNT(*)
FROM Person
JOIN Job ON Job.Id = Person.Job_Id
WHERE Job.Name = 'WEB Developer'

3

벤치마킹 할 다른 옵션이 있습니다.

1.) 윈도우 함수는 실제 크기를 직접 반환합니다 (MariaDB에서 테스트).

SELECT 
  `mytable`.*,
  COUNT(*) OVER() AS `total_count`
FROM `mytable`
ORDER BY `mycol`
LIMIT 10, 20

2.) 상자를 생각할 때 사용자가 대부분 테이블 의 정확한 크기 를 알 필요가없는 경우 대략적인 것이 충분합니다.

SELECT `TABLE_ROWS` AS `rows_approx`
FROM `INFORMATION_SCHEMA`.`TABLES`
WHERE `TABLE_SCHEMA` = DATABASE()
  AND `TABLE_TYPE` = "BASE TABLE"
  AND `TABLE_NAME` = ?
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.