2016-06-17 30 views
0

我有兩個歷史表。一個是父母,第二個是細節。在這種情況下,它們是追蹤另一個表中變化的歷史表。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包裹的多條語句來做到這一點。我想確保我以最有效的方式做到這一點。我不認爲有辦法擺脫動態的EXECUTE,但希望有人比我更聰明,可以推動我朝着正確的方向前進。

感謝您的任何幫助。

回答

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聲明,但我真的不認爲這是可能的。