1

두 개의 테이블에있는 연산자 AB을 사용하는 간단한 2 단계 SQL 쿼리가 있습니다. 하위 선택을 사용하여 외래 키로 저장된 테이블 A의 ID 수를 검색합니다. B, 테이블 B에 대한 (가능하면 복잡한) u 리 (및 기타 조인 된 테이블)를 사용합니다. 그런 다음 첫 번째 x ID를 A으로 간단하게 반환하고 싶습니다. 포스트 그레스가 제한되기 전에 설정 전체 결과에 GROUP BY/DISTINCT 작업을 수행 보인다 매우 느린증분 DISTINCT/GROUP BY 연산

SELECT sq.id 
FROM (
    SELECT a_id AS id, created_at 
    FROM B 
    WHERE ... 
    ORDER BY created_at DESC 
    ) sq 
GROUP BY sq.id 
ORDER BY max(sq.created_at) DESC 
LIMIT 10; 

을 :이 같은 쿼리를 사용했습니다. 하위 쿼리 (예 : 100)가 LIMIT 인 경우 성능은 좋아질 뿐이지 만 (결과적으로 sq의 결과 행에 적어도 10 개의 별개의 값이 있음을 보장하지 않습니다. 포스트 그레스는 대신에 (기존) 인덱스를 사용 B에 순차 검색을 수행 할 것으로 보인다 마찬가지로

쿼리

SELECT a_id AS id 
FROM B 
WHERE ... 
GROUP BY id 
ORDER BY max(created_at) DESC 
LIMIT 10 

는 매우 느립니다. GROUP BY 절을 제거하면 색인이 잘 사용됩니다.

테이블 B의 데이터는 대부분 a_id을 포함하지 않으므로 GROUP BY이 없어도 반환되는 ID의 대부분이 다릅니다. 그룹화에서 추구하는 목표는 결과 집합에 항상 A의 주어진 수의 항목이 포함되도록하는 것입니다.

"증분 DISTINCT/GROUP BY"을 수행 할 수있는 방법이 있습니까? 내 순진한 생각으로 Postgres가 결과 행을 생성하고 숫자가 LIMIT에 도달 할 때까지 점진적으로 그룹화하는 것으로 충분합니다. 대부분의 경우 대부분의 경우 a_id 값이 다르기 때문에 거의 순간적이어야합니다. 데이터를 쿼리하는 데 여러 가지 방법을 시도했지만 지금까지는 안정적으로 작동하는 것을 찾지 못했습니다.

포스트그레스 버전 9.6이며, 데이터 스키마는 다음과 같이 당신이 완전한 ORDER BY 절에 인덱스가있는 경우 플래너는 전체 테이블을 정렬하지 않도록 할 수있는 기회를 가지고

       Table "public.a" 
Column |  Type  |     Modifiers      
--------+-------------------+------------------------------------------------ 
id  | bigint   | not null default nextval('a_id_seq'::regclass) 
bar | character varying | 
Indexes: 
    "a_pkey" PRIMARY KEY, btree (id) 
    "ix_a_bar" btree (bar) 
Referenced by: 
    TABLE "b" CONSTRAINT "b_a_id_fkey" FOREIGN KEY (a_id) REFERENCES a(id) 

             Table "public.b" 
    Column |   Type    |     Modifiers      
------------+-----------------------------+-------------------------------------------------- 
id   | bigint      | not null default nextval('b_id_seq'::regclass) 
foo  | character varying   | 
a_id  | bigint      | not null 
created_at | timestamp without time zone | 
Indexes: 
    "b_pkey" PRIMARY KEY, btree (id) 
    "ix_b_created_at" btree (created_at) 
    "ix_b_foo" btree (foo) 
Foreign-key constraints: 
    "b_a_id_fkey" FOREIGN KEY (a_id) REFERENCES a(id) 
+1

약식 스키마와 샘플 데이터를 포함하십시오. 또한 이러한 세부 사항이 중요하므로 전체 쿼리를 표시하십시오. –

+1

'distinct'는 *** 함수가 아닙니다 ***. 'distinct (id) '를 쓰는 것은 말이되지 않습니다. –

+1

[postgresql-performance] (http://stackoverflow.com/tags/postgresql-performance/info)를 읽고 ** [edit] ** 귀하의 질문과 누락 된 정보를 제공하십시오. –

답변

1

이 문제는 언뜻보기에 보일 수있는 것보다 훨씬 복잡합니다.

하면 ...

  • 당신의 기준은 많은 당신처럼 (테이블 Ba_id 중복이없는 매우 선택하지
  • (훨씬 더 자격을 별개의 a_id 10 이상)이다 진술 한)

그 때 아주 빠른 방법이있다.

비트를 단순화하기 위해 created_at도 정의되어 있으며 NOT NULL으로 정의되어 있거나 더 많은 작업을 수행해야한다고 가정합니다.

WITH RECURSIVE top10 AS (
    (-- extra parentheses required 
    SELECT a_id, ARRAY[a_id] AS id_arr, created_at 
    FROM b 
    WHERE ... -- your other filter conditions here 
    ORDER BY created_at DESC, a_id DESC -- both NOT NULL 
    LIMIT 1 
    ) 
    UNION ALL -- UNION ALL, not UNION, since we exclude dupes a priori 
    (
    SELECT b.a_id, id_arr || b.a_id, b.created_at 
    FROM top10 t 
    JOIN b ON (b.created_at, b.a_id) 
      < (t.created_at, t.a_id) -- comparing ROW values 
      AND b.a_id <> ALL (t.id_arr) 
    WHERE ... -- repeat conditions 
    ORDER BY created_at DESC, a_id DESC 
    LIMIT 1 
    ) 
    ) 
SELECT a_id 
FROM top10 
LIMIT 10; 

이상적 (created_at DESC, a_id DESC) (또는 (created_at, a_id))에서 인덱스에 의해 지원된다.

다른 WHERE 조건에 따라 다른 (부분?) 색인이 더 잘 작동 할 수 있습니다.

작은 결과 집합에 특히 효율적입니다. 그렇지 않으면 다양한 세부 사항에 따라 다른 솔루션이 더 빠를 수도 있습니다. (더 많은 설명과 함께)

관련 :

+0

위대한, 나는 그것을 조사 할 것이다! 행을 생성하기 위해 재귀 적 CTE를 사용하는 방법에 대해서도 생각했지만 간단한 방법이 있는지 궁금해했습니다. – ThePhysicist

+1

@ ThePhysicist : 첫 번째 쿼리에는 버그가 있었지만 작업 버전으로 교체했습니다. –

1

유일한 방법은 .

그러면 정확한 순서를 얻기 위해 인덱스 스캔을 선택할 수 있으며 처음 10 개의 결과 행을 신속하게 찾을 수 있습니다.