2016-06-17 27 views
0

2つの履歴テーブルがあります。 1つは親で、2つ目は詳細です。この場合、別の表の変更を追跡する履歴表です。pl/pgsql親子テーブルとROWTYPEの配列を持つCTE挿入

CREATE TABLE IF NOT EXISTS history (
    id serial PRIMARY KEY, 
    tablename text, 
    row_id integer, 
    ts timestamp, 
    username text, 
    source text, 
    action varchar(10) 
); 

CREATE TABLE IF NOT EXISTS history_detail (
    id serial PRIMARY KEY, 
    master_id integer NOT NULL references history(id), 
    colname text, 
    oldval text, 
    newval text 
); 

次に、既存の行と新しい行を比較する関数があります。比較はまっすぐ私のように思える。私が苦労している部分は、私の履歴表に差を挿入したいときです。比較の間に私はhistory_detailの配列に違いを格納していますが、当時私はIDまたは親テーブルの行が何であるか分かりません。それが私がハングアップしているところです。

CREATE OR REPLACE FUNCTION update_prescriber(_npi integer, colnames text[]) RETURNS VOID AS $$ 
DECLARE 
    t text[]; 
    p text[]; 
    pos integer := 0; 
    ts text; 
    updstmt text := ''; 
    sstmt text := ''; 
    colname text; 
    _id integer; 
    _tstr text := ''; 
    _dtl history_detail%ROWTYPE; 
    _dtls history_detail[] DEFAULT '{}'; 
BEGIN 
    -- get the master table row id. 
    SELECT id INTO _id FROM master WHERE npi = _npi; 

    -- these select all the rows' column values cast as text. 
    SELECT unnest_table('tempmaster', 'WHERE npi = ''' || _npi || '''') INTO t; 
    SELECT unnest_table('master', 'WHERE npi = ''' || _npi || '''') INTO p; 

    -- go through the arrays and compare values 
    FOREACH ts IN ARRAY t 
    LOOP 
      pos := pos + 1; 
      -- pos + 1 becuse the master table has the ID column 
      IF p[pos + 1] != ts THEN 
        colname := colnames[pos]; 
        updstmt := updstmt || ', ' || colname || '=t.' || colname; 
        sstmt := sstmt || ',' || colname; 
        _dtl.colname := colname; 
        _dtl.oldval := p[pos + 1]; 
        _dtl.newval := ts; 
        _dtls := array_append(dtls, dtl); 
        RAISE NOTICE 'THERE IS a difference at for COLUMN %, old: %, new: %', colname, p[pos + 1], ts; 
      END IF; 

    END LOOP; 

    RAISE NOTICE 'dtls length: %', array_length(dtls,1); 
    RAISE NOTICE 'dtls: %', dtls; 
    RAISE NOTICE 'done comparing: %', updstmt; 
    IF length(updstmt) > 0 THEN 
      WITH hist AS (
        INSERT INTO history 
        (tablename, row_id, ts, username, source, action) 
        VALUES 
        ('master', _id, current_timestamp, 'me', 'source', 'update') 
        RETURNING * 
      ), dtls AS (
        SELECT hist.id_ 
      INSERT INTO history_detail 
-- 
-- this is where I am having trouble 
-- 
      ; 

      _tstr := 'UPDATE master 
        SET ' || substr(updstmt,2) || ' 
        FROM (SELECT ' || substr(sstmt,2) || ' FROM tempmaster WHERE npi = ''' || _npi || ''') AS t 
        WHERE master.id = ' || _id || ';'; 
      EXECUTE _tstr; 
    END IF; 
END; 
$$ LANGUAGE plpgsql; 

理想的な世界で私はこれをすべてステートメントで行うことができます。私は別のBEGIN..ENDの中に包まれた複数のステートメントでそれを行うことができることを知っています。私は可能な限り効率的な方法でそれを行うことを確認したいと思います。私は動的なものを取り除く方法があるとは思わないが、私より賢い人が正しい方向に私を押し込むことができればうれしい。

ありがとうございました。

答えて

0

2つの履歴テーブルに一度に挿入する文を作成することができました。

WITH hist AS (
    INSERT INTO history 
    (tablename, row_id, ts, username, source, action) 
    VALUES 
    ('master', _id, current_timestamp, 'me', 'source', 'update') 
    RETURNING id 
), dtls AS (
    SELECT (my_row).* 
    FROM unnest(_dtls) my_row 
), inserts AS (
    SELECT hist.id AS master_id, 
      dtls.colname AS colname, 
      dtls.newval AS newval, 
      dtls.oldval AS oldval 
    FROM dtls,hist 
) 
INSERT INTO history_detail 
(master_id, colname, newval, oldval) 
SELECT * FROM inserts 
; 

私はまだEXECUTEステートメントではありません何かのように、列の更新を追加したいが、私は本当にそれが可能だとは思いません。

関連する問題