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

일반적으로 페이징에 사용되는 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;  

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



답변

때에 따라 다르지. 이 주제에 대한 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 배까지 느리다고 말합니다.


답변

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


답변

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 및 색인 사용)로 페이지를 매겨 야합니다. 토론은 우편 번호로 페이지를 매기는 것이 아니라 날짜별로 (인덱스 사용을 허용하는) 순서대로 점점 더 많이 스크롤됩니다. 등등.


답변

다음 기사에 따르면 https://www.percona.com/blog/2007/08/28/to-sql_calc_found_rows-or-not-to-sql_calc_found_rows/

where 절에 INDEX 가있는 경우 (ID가 색인 인 경우) SQL_CALC_FOUND_ROWS 를 사용하지 않고 2 개의 쿼리를 대신 사용하는 것이 좋지만 where 절에 넣은 것에 대한 색인이없는 경우 (귀하의 경우 id) SQL_CALC_FOUND_ROWS 를 사용하는 것이 더 효율적입니다.


답변

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을 사용하십시오.”!


답변

불필요한 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'


답변

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

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` = ?