1

수십억 개의 행을 가진 Postgres 테이블이 있으며 기계 학습 응용 프로그램의 경우 열차 및 테스트 세트로 나누어야합니다.포스트 그램의 연속적인 행 부분 집합을 효율적으로 표시하십시오.

나는 테스트 행이 자신의 id 열을 기준으로 대부분이 연속되고 싶어요, 그래서 무작위로 1,000 연속 행 각각의 여러 덩어리를 선택하고 테스트 행로 표시하고 싶습니다.

UPDATE table SET test=true WHERE id BETWEEN 100000 AND 101000; 

매우 효율적이며, 사용자가 예상하는대로 인덱스 스캔을 사용 : 내가 그렇게 임의 1,000 연속 행을 선택하여 id 컬럼에 인덱스가 빠르고입니다. 불행하게도 최대한 빨리 초기 id가 무작위로 생성 할 수 있도록로 즉

WITH off AS (SELECT ROUND(random()*maxId)) 
    UPDATE table SET test=true 
    WHERE id BETWEEN (SELECT * FROM off LIMIT 1) 
       AND (SELECT * FROM off LIMIT 1)+1000; 

쿼리 플래너 지금은 전체 테이블 스캔 (매우 느리게)를 수행하기로 결정합니다.

물론이 작업을 한 번만 수행하면 수동으로 임의의 행을 생성 할 수 있습니다. 문제가 없습니다. 그러나 결국에는 아래처럼 테스트와 열차로 자동으로 나누는 함수가 필요합니다 :

CREATE OR REPLACE FUNCTION test_train_divide(chunkSize integer, proportion real) 
RETURNS BOOLEAN 
AS $$ 
DECLARE 
maxId INTEGER := (SELECT MAX(id) FROM table); 
BEGIN 
FOR i IN 1 .. round(maxId*proportion/chunkSize) LOOP 
    RAISE NOTICE 'Update call %', i; 
    WITH off AS (SELECT ROUND(random()*maxId)) 
    UPDATE table SET test=true 
    WHERE id BETWEEN (SELECT * FROM off LIMIT 1) 
       AND (SELECT * FROM off LIMIT 1)+chunkSize; 
END LOOP; 
return true; 
END; 
$$ LANGUAGE plpgsql; 

SELECT test_train_divide(1000,0.01); 

이 작동하지만 아주 천천히! 어떤 포인터? 여기에 업데이트

tbl "public.tbl” 
    Column | Type | Modifiers 
-----------+---------+----------- 
subid  | integer | 
id  | bigint | 
wordid | integer | 
capid  | integer | 
test  | boolean | 
Indexes: 
    “tbl_id_idx" btree (id) 

그리고 여기가 (인덱스를 사용하는) 두 개의 서로 다른 쿼리 계획, 하나 개의 좋은 일 나쁜있는 스키마입니다 :

will=# EXPLAIN UPDATE tbl SET test=true WHERE id BETWEEN 1000000 AND 1001000; 

              QUERY PLAN            
--------------------------------------------------------------------------------------------------- 
Update on tbl (cost=0.57..790.45 rows=1079 width=38) 
    -> Index Scan using tbl_id_idx on tbl (cost=0.57..790.45 rows=1079 width=38) 
     Index Cond: ((id >= 1000000) AND (id <= 1001000)) 
(3 rows) 


will=# EXPLAIN WITH start AS (SELECT round(random()*max(id)) FROM tbl) UPDATE tbl c SET test=true WHERE c.id BETWEEN (SELECT * FROM start LIMIT 1) AND (SELECT * FROM start LIMIT 1)+1000; 



                  QUERY PLAN                
--------------------------------------------------------------------------------------------------------------------------------------- 
Update on tbl c (cost=0.65..14932243.97 rows=1459961 width=38) 
    CTE start 
    -> Result (cost=0.59..0.61 rows=1 width=0) 
      InitPlan 1 (returns $0) 
      -> Limit (cost=0.57..0.59 rows=1 width=8) 
        -> Index Only Scan Backward using tbl_id_idx on tbl (cost=0.57..5846291.90 rows=288468819 width=8) 
         Index Cond: (id IS NOT NULL) 
    InitPlan 3 (returns $2) 
    -> Limit (cost=0.00..0.02 rows=1 width=8) 
      -> CTE Scan on start (cost=0.00..0.02 rows=1 width=8) 
    InitPlan 4 (returns $3) 
    -> Limit (cost=0.00..0.02 rows=1 width=8) 
      -> CTE Scan on start start_1 (cost=0.00..0.02 rows=1 width=8) 
    -> Seq Scan on tbl c (cost=0.00..14932243.32 rows=1459961 width=38) 
     Filter: (((id)::double precision >= $2) AND ((id)::double precision <= ($3 + 1000::double precision))) 
(15 rows) 

Time: 2.649 ms 

+0

비슷한 질문 성능 질문 –

+1

"효율적으로 임의의 행을 선택,"우리는 포스트 그레스의 버전이 필요합니다. 그리고 기본 테이블 정의는 항상 유용하다 ('CREATE TABLE' 문 또는 psql의'\ d tbl'). –

+0

좋아요. Postgres 9.3에 대한 정보를 더 제공해 줬습니다. 이제 테이블 정의가 – wxs

답변

2

max_id을 초기화 한 후 max(id) - 1000 1000 행을위한 공간을 남기려면 색인을 사용해야합니다.

UPDATE table 
SET test = true 
FROM (SELECT (random() * max_id)::bigint AS lower_bound) t 
WHERE id BETWEEN t.lower_bound AND t.lower_bound + 999; 
  • CTE 및 하위 쿼리가있는 복잡한 구조가 필요하지 않습니다. 단일 하위 쿼리를 사용하십시오.

  • numeric (또는 dp)의 원래 계산 결과는 bigint 열의 색인과 잘 맞지 않을 수 있습니다. 출연 : bigint. (9.3 절에서 문제가되지 않아야합니다.)

  • BETWEEN은 상한과 하한을 포함합니다. 엄밀히 말하면, 상한은 lower + 999이어야합니다.

  • random()은 (per documentation) a random value in the range 0.0 <= x < 1.0을 반환합니다. 완전히 공정, 당신의 lower_bound 정말 이런 식으로 계산해야 할 ( 없다고 가정) :

    trunc(random() * max_id)::bigint + 1 
    

당신이 진정 난수가 필요한 경우를 (또는 id이 차이가있는 경우)이 고려 관련 대답 :

자문 잠금 또는 다른 접근 방법이 유용 할 수 있습니다. 이 관련, 나중에 답을 비교 : 이전에 요청

+0

이라는 질문에 있습니다. 적어도 쿼리 플래너가가는 한. 다른 문제도 잘 해결됩니다. 많은 부분이 변경 되었기 때문에 어느 부분이 쿼리 계획을 수정했는지 분명하지 않습니다. CTE가 부족하다고 생각하십니까? – wxs

+1

@wxs :'BETWEEN'의 두 가지 서브 쿼리라고 생각합니다. 'SELECT *'... –

+1

으로 시작하는 여러 가지 방법으로 혼란 스럽습니다. trunc (random() * max_id-1000) : : bigint + 1'로 설정하면 end가 in-range가됩니다. – Jasen