kag*_*ag0 5 postgresql trigger plpgsql json postgresql-9.4
我是一个数据库/postgres 初学者,所以请耐心等待。
如果我有一张桌子,就像这样。
CREATE TABLE testy (
id INTEGER REFERENCES other_table,
name varchar(128) PRIMARY KEY,
json JSONB NOT NULL
);
Run Code Online (Sandbox Code Playgroud)
我在寻找插入或更新,将设置列之前创建一个触发器id,并name与在相同名称字段的值json。
因此,例如,如果testy包含以下内容UPDATE testy SET json = '{"id":2,"name":"jim"}' WHERE id = 1并被调用。
id | name | json
---+------+-----
1 | "jim"| {"id":1,"name":"jim"}
Run Code Online (Sandbox Code Playgroud)
想要的结果是
id | name | json
---+------+-----
2 | "jim"| {"id":2,"name":"jim"}
Run Code Online (Sandbox Code Playgroud)
我希望使其相当通用,因此不需要对列名进行硬编码。如果相应的 json 字段不存在,则将该列设置为 NULL 即可。到目前为止我有
CREATE TABLE testy_index (
id INTEGER PRIMARY KEY
);
INSERT INTO testy_index VALUES (1);
INSERT INTO testy_index VALUES (2);
INSERT INTO testy_index VALUES (3);
CREATE TABLE testy (
id INTEGER REFERENCES testy_index,
json JSONB NOT NULL
);
CREATE UNIQUE INDEX testy_id ON testy((json->>'id'));
CREATE OR REPLACE FUNCTION json_fn() RETURNS TRIGGER AS $testy$
DECLARE
roow RECORD;
BEGIN
FOR roow IN
SELECT column_name FROM information_schema.columns WHERE table_name = 'testy'
LOOP
NEW.roow.column_name = (NEW.json->>roow.column_name);
END LOOP;
END;
$testy$ LANGUAGE plpgsql;
CREATE TRIGGER json_trigger
BEFORE INSERT OR UPDATE ON testy FOR EACH ROW
EXECUTE PROCEDURE json_fn();
Run Code Online (Sandbox Code Playgroud)
这不起作用,因为您不能灵活地使用roow.column_name。我试过玩 EXECUTE 没有成功,尽管我可能只是做得不对。
任何帮助将不胜感激!!
编辑:这样做的动机是可以将外键约束放置在表现为 json 字段的内容上。
编辑:plv8 很棒。使用了@Daniel Vérité 答案的修改版本,以便在 json 中未表示为字段的列将被清空
CREATE OR REPLACE FUNCTION json_fn() RETURNS trigger AS
$$
var obj = JSON.parse(NEW.json);
for(var col in NEW){
if(col == 'json'){
continue;
}
if(col in obj){
NEW[col]=obj[col];
}else{
NEW[col]=null;
}
}
return NEW;
$$
LANGUAGE plv8;
CREATE TRIGGER json_trigger
BEFORE INSERT OR UPDATE ON testy FOR EACH ROW
EXECUTE PROCEDURE json_fn();
Run Code Online (Sandbox Code Playgroud)
实际上,这就是您所需要的:
\nNEW := jsonb_populate_record(NEW, NEW.json);\nRun Code Online (Sandbox Code Playgroud)\n\n\n\n\n
jsonb_populate_record( , ) \xe2\x86\x92baseanyelementfrom_jsonjsonbanyelement将顶级 JSON 对象扩展为具有基本参数复合类型的行。扫描 JSON 对象以查找名称与输出行类型的列名称匹配的字段,并将它们的值插入到输出的这些列中。(不对应于任何输出列名称的字段将被忽略。)在典型使用中,base 的值只是
\nNULL,这意味着与任何对象字段不匹配的任何输出列都将用空值填充。但是,如果\nbase 不是,NULL则它包含的值将用于\n不匹配的列。
作为第一个参数提供的行保留所有未被覆盖的值(json 值中没有匹配的键)在 Postgres 13 之前没有记录。
\n如果“未记录”对您来说太不确定,请使用执行完全相同hstore操作的运算符。#=
NEW := (NEW #= hstore(jsonb_populate_record(NEW, NEW.json)));\nRun Code Online (Sandbox Code Playgroud)\n无论如何,该hstore模块应该安装在大多数系统中。指示:
任一解决方案均可源自:
\n\n需要注意的一件事 - 你写道:
\n\n\n如果相应的 json 字段不存在,则将该列设置为 NULL 就可以了。
\n
这会保留JSON 值中没有匹配键的所有值,这应该更好。
\nCREATE OR REPLACE FUNCTION json_fn()\n RETURNS TRIGGER\n LANGUAGE plpgsql AS\n$func$\nBEGIN\n NEW := jsonb_populate_record(NEW, NEW.json); -- or hstore alternative\n RETURN NEW;\nEND\n$func$;\nRun Code Online (Sandbox Code Playgroud)\n您的设置中的其他所有内容看起来都正确,只需将 PK 添加到testy:
CREATE TABLE testy (\n id int PRIMARY KEY REFERENCES testy_index\n , data jsonb NOT NULL\n);Run Code Online (Sandbox Code Playgroud)\n在 pg 9.4 中进行了测试,它对我来说如广告所示有效。
\nNULL根据评论:
\nCREATE OR REPLACE FUNCTION json_fn()\n RETURNS trigger\n LANGUAGE plpgsql AS\n$func$\nDECLARE\n _j jsonb := NEW.json; -- remember the json value\nBEGIN\n NEW := jsonb_populate_record(NULL::testy, _j);\n NEW.json := _j; -- reassign\n RETURN NEW;\nEND\n$func$;\nRun Code Online (Sandbox Code Playgroud)\n显然,您需要确保列名称或您的jsonb列不会在 JSON 值中显示为键名称。我不会使用作json为列名称,因为它是基本数据类型名称,这可能会令人困惑。
| 归档时间: |
|
| 查看次数: |
5032 次 |
| 最近记录: |