developer tip

이중 쿼리없이 MySQL 페이지 매김?

optionbox 2020. 8. 12. 08:09
반응형

이중 쿼리없이 MySQL 페이지 매김?


MySQL 쿼리에서 결과 수를 가져 오는 동시에 결과를 제한하는 방법이 있는지 궁금합니다.

페이지 매김이 작동하는 방식 (내가 이해하는대로), 먼저 다음과 같은 작업을 수행합니다.

query = SELECT COUNT(*) FROM `table` WHERE `some_condition`

num_rows (query)를 얻은 후 결과 수를 얻었습니다. 하지만 실제로 결과를 제한하려면 다음과 같은 두 번째 쿼리를 수행해야합니다.

query2 = SELECT COUNT(*) FROM `table` WHERE `some_condition` LIMIT 0, 10

내 질문 : 어쨌든 주어진 총 결과 수를 검색하고 단일 쿼리에서 반환되는 결과를 제한하는 방법이 있습니까? 또는이를 수행하는 더 효율적인 방법. 감사!


아니요, 페이지 매기기를 원하는 응용 프로그램의 수입니다. 쿼리를 두 번 수행하지만 신뢰할 수 있고 방탄입니다. 그러나 몇 초 동안 카운트를 캐시하면 많은 도움이됩니다.

다른 방법은 SQL_CALC_FOUND_ROWS을 사용한 다음을 호출하는 것 SELECT FOUND_ROWS()입니다. FOUND_ROWS()나중에 호출 해야한다는 사실과는 별개로, 이것에 문제가 있습니다 . MySQL 에는 이것이 ORDER BY두 쿼리의 순진한 접근 방식보다 큰 테이블에서 훨씬 느리게 만드는 쿼리 에 영향을 미치는 간지럼 버그 가 있습니다 .


나는 거의 두 가지 쿼리를하지 않습니다.

필요한 것보다 하나 더 많은 행을 반환하고 페이지에 10 개만 표시하고 더 많은 행이 표시되면 "다음"버튼을 표시합니다.

SELECT x, y, z FROM `table` WHERE `some_condition` LIMIT 0, 11
// iterate through and display 10 rows.

// if there were 11 rows, display a "Next" button.

검색어는 가장 관련성이 높은 순서로 먼저 반환되어야합니다. 대부분의 사람들은 412 개 중 236 페이지에 관심이 없을 가능성이 높습니다.

Google 검색을했는데 결과가 첫 페이지에 없으면 9 페이지가 아닌 2 페이지로 이동합니다.


이중 쿼리를 피하는 또 다른 방법은 먼저 LIMIT 절을 사용하여 현재 페이지에 대한 모든 행을 가져온 다음 최대 행 수가 검색된 경우에만 두 번째 COUNT (*) 쿼리를 수행하는 것입니다.

많은 응용 프로그램에서 가장 가능성이 높은 결과는 모든 결과가 한 페이지에 맞는 것이며 페이지 매김을 수행해야하는 것은 표준이 아니라 예외입니다. 이러한 경우 첫 번째 쿼리는 최대 결과 수를 검색하지 않습니다.

예를 들어, stackoverflow 질문에 대한 답변은 두 번째 페이지에 거의 넘치지 않습니다. 답변에 대한 의견은 모두를 표시하는 데 필요한 5 개 정도를 넘지 않습니다.

따라서 이러한 응용 프로그램에서는 먼저 LIMIT로 쿼리를 수행 한 다음 해당 제한에 도달하지 않는 한 두 번째 COUNT (*) 쿼리를 수행 할 필요없이 정확히 몇 개의 행이 있는지 알 수 있습니다. 대부분의 상황을 다룹니다.


대부분의 경우 직관적이지 않은 것처럼 보이지만 두 개의 개별 쿼리로 수행하는 것이 하나에서 수행하는 것보다 훨씬 빠르고 리소스 집약적이지 않습니다.

SQL_CALC_FOUND_ROWS를 사용하면 큰 테이블의 경우 두 개의 쿼리를 실행하는 것보다 훨씬 느리고 쿼리가 훨씬 느려집니다. 첫 번째 쿼리에는 COUNT (*)가 있고 두 번째 쿼리에는 LIMIT가 있습니다. 그 이유는 SQL_CALC_FOUND_ROWS로 인해 이전이 아닌 행 페치 한 LIMIT 절이 적용되므로 제한을 적용하기 전에 가능한 모든 결과에 대해 전체 행을 페치하기 때문입니다. 실제로 데이터를 가져 오기 때문에 인덱스로는 만족할 수 없습니다.

두 쿼리 접근 방식을 사용하면 첫 번째 쿼리는 COUNT (*) 만 가져오고 실제로는 실제 데이터를 가져 오지 않습니다. 일반적으로 인덱스를 사용할 수 있고 실제 행 데이터를 가져올 필요가 없기 때문에 훨씬 더 빨리 충족 될 수 있습니다. 보는 모든 행. 그런 다음 두 번째 쿼리는 첫 번째 $ offset + $ limit 행만보고 반환하면됩니다.

MySQL 성능 블로그의이 게시물은 이에 대해 자세히 설명합니다.

http://www.mysqlperformanceblog.com/2007/08/28/to-sql_calc_found_rows-or-not-to-sql_calc_found_rows/

페이지 매김을 최적화에 대한 자세한 내용은 확인 이 게시물이 게시물을 .


query = SELECT col, col2, (SELECT COUNT(*) FROM `table`) AS total FROM `table` WHERE `some_condition` LIMIT 0, 10

내 대답이 늦을 수 있지만 두 번째 쿼리 (제한 있음)를 건너 뛰고 백엔드 스크립트를 통해 정보를 필터링 할 수 있습니다. 예를 들어 PHP에서는 다음과 같이 할 수 있습니다.

if($queryResult > 0) {
   $counter = 0;
   foreach($queryResult AS $result) {
       if($counter >= $startAt AND $counter < $numOfRows) {
            //do what you want here
       }
   $counter++;
   }
}

But of course, when you have thousands of records to consider, it becomes inefficient very fast. Pre-calculated count maybe a good idea to look into.

Here's a good read on the subject: http://www.percona.com/ppc2009/PPC2009_mysql_pagination.pdf


You can reuse most of the query in a subquery and set it to an identifier. For example a movie query that finds movies containing the letter 's' ordering by runtime would look like this on my site.

SELECT Movie.*, (
    SELECT Count(1) FROM Movie
        INNER JOIN MovieGenre 
        ON MovieGenre.MovieId = Movie.Id AND MovieGenre.GenreId = 11
    WHERE Title LIKE '%s%'
) AS Count FROM Movie 
    INNER JOIN MovieGenre 
    ON MovieGenre.MovieId = Movie.Id AND MovieGenre.GenreId = 11
WHERE Title LIKE '%s%' LIMIT 8;

Do note that I'm not a database expert, and am hoping someone will be able to optimize that a bit better. As it stands running it straight from the SQL command line interface they both take ~0.02 seconds on my laptop.


SELECT * 
FROM table 
WHERE some_condition 
ORDER BY RAND()
LIMIT 0, 10

참고URL : https://stackoverflow.com/questions/818567/mysql-pagination-without-double-querying

반응형