2011-07-27 2 views
1

이 문제는 잠시 두통을 불러 일으 킵니다. PostgreSQL 8.4 데이터베이스는 4.000.000 개 이상의 레코드를 포함하는 테이블 하나만으로 구성됩니다. 이 테이블은 다음과 같이 구성되어 있습니다 :상자 사이의 거리를 결정하십시오.

CREATE TABLE metadata (
    id serial NOT NULL, 
    "value" text NOT NULL DEFAULT ''::text, 
    segment_box box NOT NULL DEFAULT box(point(((-9223372036854775808)::bigint)::double precision, (1)::double precision), point((9223372036854775807::bigint)::double precision, ((-1))::double precision)), 
    CONSTRAINT metadata_pk PRIMARY KEY (id) 
) 

CREATE INDEX metadata_segment_box_ix 
    ON metadata 
    USING gist 
    (segment_box); 

CREATE INDEX metadata_tag_value_ix 
    ON metadata 
    USING btree 
    (value); 

테이블은 직사각형 박스로 표시되는 세그먼트를 포함합니다. 이 세그먼트는 '값'열을 사용하여 주석을 추가합니다.

데이터베이스에서 수행하고자하는 쿼리의 종류는 특정 창에 포함 된 지정된 값을 가진 모든 세그먼트를 찾으려고 시도합니다. 이를 성공적으로 수행하는 쿼리는 다음과 같습니다.

SELECT * FROM (SELECT * FROM metadata WHERE value='X') a, 
(SELECT * FROM metadata WHERE AND value='Y') b 
WHERE a.segment_box <-> b.segment_box <= 3000 

그러나 아마도 알 수 있듯이이 쿼리는 데이터베이스에서 효율적으로 수행 할 수 없습니다. 하위 쿼리 a와 b의 직교 곱이 실제로 커지고 있습니다. 이러한 쿼리를보다 효율적으로 수행 할 수있는 방법이 있습니까? 어떤 종류의 슬라이딩 윈도우 접근 방식이 트릭을 할 것이라고 상상할 수 있습니다. 다음과 같은 아마 뭔가 :

SELECT *, rank() OVER (
PARTITION BY "value" ORDER BY (segment_box[1])[0], (segment_box[0])[0] 
) FROM metadata WHERE value='X' OR value='Y' 

업데이트 : 포스트 그레스에서 사용자 정의 함수를 만드는이 질문을 게시 한 후 시도 것들의 하나. 내가 시도 :

그러나이 함수는 여러 번 수행해야하는 하위 쿼리 때문에 위에서 설명한 기본 솔루션만큼 느립니다. 이것에 관한 다른 생각?

+1

당신의 질문에'postgis'가 있지만 PostGIS를 사용하고 있지 않습니다. 그렇게했다면 [ST_DWithin] (http://postgis.org/documentation/manual-svn/ST_DWithin.html)과 같은 좋은 버퍼와 같은 기능을 통해 도움이 될 것입니다. –

+0

ST_DWithin 함수를 사용하면 응답에 표시된 함수와 비교할 때 쿼리가 더 빨리 수행된다고 생각합니까? – joost1024

답변

2

나는 일종의 스윕 라인 알고리즘으로 문제를 직접 해결했다. 하나의 쿼리 만 수행됩니다. 커서를 사용하여 쿼리의 결과 집합을 앞뒤로 쓸어 넘깁니다. 다음과 같이 결과 알고리즘은 작동 :

CREATE OR REPLACE FUNCTION within_window(size bigint DEFAULT 0) 
    RETURNS setof metadata AS 
$BODY$DECLARE 
crsr SCROLL CURSOR FOR (SELECT * FROM metadata WHERE value='X' OR value='Y' ORDER BY (segment_box[1])[0], (segment_box[0])[0]); 
rc RECORD; 
rcc RECORD; 
crsr_position int; 
last_crsr int; 
BEGIN 
    OPEN crsr; 
    crsr_position := 0; 
    LOOP FETCH NEXT FROM crsr INTO rc; 
     IF NOT FOUND THEN 
      EXIT; 
     END IF; 
     last_crsr := crsr_position; 
     LOOP FETCH NEXT FROM crsr INTO rcc; 
      IF NOT FOUND THEN 
       EXIT; 
      ELSEIF 
       rcc.segment_box &< box(rc.segment_box[0], point((((rc.segment_box[1])[0]) + size), (rc.segment_box[1])[1])) AND 
       rcc.segment_box &> box(rc.segment_box[0], point((((rc.segment_box[1])[0]) + size), (rc.segment_box[1])[1])) 
      THEN 
       RETURN NEXT rcc; 
      ELSE 
       EXIT; 
      END IF; 
     END LOOP; 
     crsr_position := last_crsr + 1; 
     MOVE ABSOLUTE crsr_position FROM crsr; 
    END LOOP; 
    CLOSE crsr; 
END;$BODY$ 
    LANGUAGE plpgsql; 

쿼리가 476 밀리 대신 (4+ 만 열 데이터베이스에) 6+ 분을 필요로이 기능을 사용!

관련 문제