web-dev-qa-db-ja.com

Greenplumプロシージャがビューの変更を認識しないのはなぜですか?

日付のセットを返すクエリをループし、WHERE句の日付を使用してビューのセットを再作成し、これらのビューを使用する他の一連のプロシージャを呼び出すプロシージャがあります。したがって、ループに3つの日付が含まれている場合、ループは各日付でプロシージャを実行し、ビューのWHERE句にコード化されます。ビューが選択するテーブルは、WHERE句の日付でパーティション化されます。ビューは、不要なパーティションをすべて削除するために存在します。

ループの最初の反復では、すべてが正常です。ビューが作成され、プロシージャが呼び出され、データが処理されます。

2回目以降の反復では、プロシージャは、ビューに最初の反復の日付が残っているかのように動作します。ループが終了した後、ビューのWHERE句には最後の日付が含まれますが、プロシージャは明らかに最初の日付を何度も処理していました。

ビューを作成し、そこからカウントを選択し、別の日付で再度作成し、そこからカウントを選択する簡単なテストを作成しました。2つの正しいカウントを取得するので、プロシージャ内でビューを明確に再定義して使用します。可能です。

ループで機能しない理由はありますか?

これがデモスクリプトです。最初の実行では2つのレコードを処理し、2番目の実行では4つのレコードを処理する必要がありますが、どちらもプロセス2を実行します。

create schema dv;

create schema rpt;

drop table dv.batch_data;
create table dv.batch_data
(
  std_date date
, dv_load_dt timestamp with time zone
)
WITH (APPENDONLY=false, OIDS=FALSE)
DISTRIBUTED BY (std_date)
;

insert into dv.batch_data values( '2016-01-02'::date, current_timestamp );
insert into dv.batch_data values( '2016-01-04'::date, current_timestamp );

drop table dv.table_1;
create table dv.table_1
(
  std_date date
, col_1 text
, col_2 text
)
WITH (APPENDONLY=false, OIDS=FALSE)
DISTRIBUTED BY (std_date)
;

insert into dv.table_1 values( '2016-01-01'::date, 'First',   'Record' );
insert into dv.table_1 values( '2016-01-02'::date, 'Second',  'Record' );
insert into dv.table_1 values( '2016-01-02'::date, 'Third',   'Record' );
insert into dv.table_1 values( '2016-01-03'::date, 'Fourth',  'Record' );
insert into dv.table_1 values( '2016-01-03'::date, 'Fifth',   'Record' );
insert into dv.table_1 values( '2016-01-03'::date, 'Sixth',   'Record' );
insert into dv.table_1 values( '2016-01-04'::date, 'Seventh', 'Record' );
insert into dv.table_1 values( '2016-01-04'::date, 'Eighth',  'Record' );
insert into dv.table_1 values( '2016-01-04'::date, 'Ninth',   'Record' );
insert into dv.table_1 values( '2016-01-04'::date, 'Tenth',   'Record' );

create table rpt.stage_table_a
(
  std_date date
, col text
)
WITH (APPENDONLY=false, OIDS=FALSE)
DISTRIBUTED BY (std_date)
;

CREATE OR REPLACE FUNCTION rpt.custom_proc_rpt_loop()
  RETURNS integer AS
$BODY$
DECLARE
  v_count       integer;         

  rec           RECORD;
  v_days        integer;
  v_query       varchar(10000);
  v_return_msg  varchar(256);

BEGIN
  v_count := 0;
  v_days := 0;

  BEGIN

  FOR rec IN
    SELECT DISTINCT std_date
    FROM dv.batch_data
    ORDER BY std_date
  LOOP
    v_days := v_days + 1;

    v_query := 'CREATE OR REPLACE VIEW rpt.view_batch_table_1 AS ' ||
               'SELECT std_date ' ||
                    ', col_1 ' ||
                    ', col_2 ' ||
               'FROM dv.table_1 ' || 
               'WHERE std_date = ''' || rec.std_date || '''';

    RAISE NOTICE '%',v_query;
    EXECUTE v_query;

    v_count := v_count + 1;

    PERFORM rpt.update_stage_table_a();

    ANALYZE rpt.stage_table_a;

  END LOOP;

  END;

  v_return_msg := 'custom_proc_rpt_loop completed. '
    || CAST(v_days AS VARCHAR(64)) || ' loops performed. ';

  RAISE NOTICE '%',v_return_msg;

  RETURN 1;

END;
$BODY$
  LANGUAGE plpgsql VOLATILE;


CREATE OR REPLACE FUNCTION rpt.update_stage_table_a()
  RETURNS integer AS
$BODY$
DECLARE
  v_count             integer;         
  v_return_msg  varchar(256);

  BEGIN

  TRUNCATE rpt.stage_table_a;

  INSERT INTO rpt.stage_table_a
  (
    std_date
  , col
  )
  SELECT DISTINCT 
    std_date
  , COALESCE(col_1,'') || CASE WHEN length(col_1) > 1 AND length(col_2) > 1 THEN ' ' ELSE '' || COALESCE(col_1,'') END
  FROM rpt.view_batch_table_1
  ;

  GET DIAGNOSTICS v_count = row_count;

  v_return_msg := 'stage_table_a completed. '
    || CAST(v_count AS VARCHAR(64)) || ' records processed. ';

  RAISE NOTICE '%',v_return_msg;

  RETURN 1;

END;
$BODY$
  LANGUAGE plpgsql VOLATILE;

select rpt.custom_proc_rpt_loop();

select count(*) from rpt.view_batch_table_1;

現実の世界では、内部プロシージャはデータウェアハウス自動化ツールによって生成されるため、変更したくありません。ループを含む呼び出しプロシージャで、やりたいことが何でもできます。

2
PhilHibbs

Greenplumを使用していませんが、PostgreSQLの遺産から推測すると、おそらくPL/pgSQLがSQLステートメントを処理する方法が原因です:プリペアドステートメントのように。それらは計画されており、最初の使用時にキャッシュされます。これは、とりわけ、ビューがその基になるテーブルに解決されることを意味します。

同じ計画が後続の反復で再利用されるため、通常、再計画の時間を節約できます。しかし、それはまた、ビューを再作成する試みを失敗させます。

これを修正するにはさまざまな方法があります。簡単な解決策の1つは、だまされたSQLステートメントを動的SQLにも変換して、毎回再計画を強制することです。そう:

...

EXECUTE '
   SELECT * 
    FROM rpt.update_stage_table_a
    ( p_sequence        
    , p_job_name        
    , p_task_name       
    , p_job_id          
    , p_task_id          
    , p_return_msg      
    , p_status
    )'
INTO v_parameters
   , v_return_msg
   , v_status
;

...

等。

関連:

2