한계가 적용되기 전에 결과 수를 얻는 가장 좋은 방법
-
03-07-2019 - |
문제
DB에서 나오는 데이터를 통해 페이징 할 때는 페이지 점프 컨트롤을 렌더링하는 페이지 수를 알아야합니다.
현재 나는 한 번 쿼리를 두 번 실행하여 count()
총 결과를 결정하고 제한이 적용된 두 번째 시간은 현재 페이지에 필요한 결과 만 되 찾을 수 있습니다.
이것은 비효율적 인 것 같습니다. 이전에 몇 개의 결과가 반환되었는지 결정하는 더 좋은 방법이 있습니까? LIMIT
적용 되었습니까?
PHP와 Postgres를 사용하고 있습니다.
해결책
순수한 SQL
2008 년부터 상황이 바뀌 었습니다 창 함수 한 쿼리를 전체 카운트와 제한된 결과를 얻으려면. (소개 2009 년 PostgreSQL 8.4).
SELECT foo
, count(*) OVER() AS full_count
FROM bar
WHERE <some condition>
ORDER BY <some col>
LIMIT <pagesize>
OFFSET <offset>
이것은 총 수가없는 것보다 훨씬 비쌀 수 있습니다. 모든 행을 계산해야하며, 일치하는 인덱스에서 상단 행 만 가져갈 수있는 바로 가기는 더 이상 도움이되지 않을 수 있습니다.
작은 테이블이나 그다지 중요하지 않습니다 full_count
<= OFFSET
+ LIMIT
. 실질적으로 더 큰 문제 full_count
.
코너 케이스: 언제 OFFSET
적어도 기본 쿼리의 행 수만큼이나 좋습니다. 줄 없음 반환됩니다. 그래서 당신은 또한 아니요 full_count
. 가능한 대안 :
고려하다 사건의 연속:
WHERE
절 (및JOIN
조건이지만 여기에는 없음)베이스 테이블에서 자격있는 행을 필터링합니다.(
GROUP BY
그리고 집계 기능은 여기에 갈 것입니다.)윈도우 함수는 모든 적격 행을 고려하여 적용됩니다 (
OVER
조항 및 함수의 프레임 사양). 단순한count(*) OVER()
모든 행을 기반으로합니다.ORDER BY
(
DISTINCT
또는DISTINCT ON
여기에 갈 것입니다.)LIMIT
/OFFSET
반환 할 행을 선택하기 위해 확립 된 순서에 따라 적용됩니다.
LIMIT
/ OFFSET
테이블에서 점점 더 많은 행으로 인해 점점 비효율적입니다. 더 나은 성능이 필요한 경우 대체 접근 방식을 고려하십시오.
최종 수를 얻기위한 대안
영향을받는 행의 수를 얻는 데 완전히 다른 접근법이 있습니다 (~ 아니다 이전의 전체 수 OFFSET
& LIMIT
적용). Postgres는 마지막 SQL 명령의 영향을받는 행 수를 내부 부기를 가지고 있습니다. 일부 클라이언트는 PSQL과 같이 해당 정보에 액세스하거나 행을 카운트 할 수 있습니다.
예를 들어, 영향을받는 행의 수를 검색 할 수 있습니다. PLPGSQL SQL 명령을 실행 한 직후 :
GET DIAGNOSTICS integer_var = ROW_COUNT;
또는 사용할 수 있습니다 pg_num_rows
안에 PHP. 또는 다른 고객의 유사한 기능.
관련된:
다른 팁
내가 설명하는대로 내 블로그에서, MySQL에는 부르는 기능이 있습니다 sql_calc_found_rows. 이렇게하면 쿼리를 두 번 수행 할 필요가 없지만 한계 조항이 일찍 중지 할 수 있었음에도 불구하고 여전히 쿼리를 수행해야합니다.
내가 아는 한, postgresql에 대한 유사한 기능은 없습니다. 페이지 매김을 할 때 조심해야 할 한 가지는 (한계가 IMHO를 사용하는 가장 일반적인 것) : "오프셋 1000 리미트 10"을 수행하는 것은 DB가 가져와야한다는 것을 의미합니다. 적어도 1010 행은 10 만 제공하더라도 10을 제공하더라도 이전 행 (이 경우 1000 번째)에 대해 주문한 행의 값을 기억하고 다음과 같이 쿼리를 다시 작성하는 것입니다. . 여기서 order_row> value_of_1000_th 리미트 10 ". 장점은 "Order_row"가 아마도 인덱싱된다는 것입니다 (그렇지 않은 경우 문제가 발생합니다). 단점은 페이지보기 사이에 새로운 요소가 추가되면 동기화가 약간 떨어질 수 있다는 점입니다 (그러나 다시 방문자는 관찰 할 수 없으며 큰 성능 이득이 될 수 있습니다).
매번 Count () 쿼리를 실행하지 않음으로써 성능 페널티를 완화 할 수 있습니다. 쿼리가 다시 실행되기 5 분 전에 페이지 수를 캐시합니다. 많은 수의 인서트를 보지 않는 한, 그것은 잘 작동해야합니다.
Postgres는 이미 일정량의 캐싱을 수행하기 때문에 이러한 유형의 방법은 비효율적이지 않습니다. 실행 시간이 두 배가되는 것은 아닙니다. 우리는 DB 층에 타이머를 내장하여 증거를 보았습니다.
페이징 목적으로 알아야 할 때 전체 쿼리를 한 번 실행하고 서버 측 캐시로 디스크에 데이터를 작성한 다음 페이징 메커니즘을 통해이를 공급하는 것이 좋습니다.
사용자에게 데이터를 제공할지 여부를 결정하기 위해 카운트 쿼리를 실행하는 경우 (예 :> X 레코드가있는 경우 오류를 돌려 주면) Count 접근 방식을 고수해야합니다.