我有两个历史表。一个是父母,第二个是细节。在这种情况下,它们是跟踪另一个表中更改的历史记录表。
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 :(得分: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
语句的内容,但我真的不认为这是可能的。