日付のセットを返すクエリをループし、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;
現実の世界では、内部プロシージャはデータウェアハウス自動化ツールによって生成されるため、変更したくありません。ループを含む呼び出しプロシージャで、やりたいことが何でもできます。
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
;
...
等。
関連: