Postgresql
使用 jsonb 參數中的鍵更新動態列名
我想為 Postgres 12 編寫一個通用過程,根據
jsonb
參數中提供的數據更新表的列。當然,它可以在應用程序邏輯中完成,但我試圖將盡可能多的程式碼下推到數據庫層。這是我天真地希望可能起作用的那種事情:
CREATE PROCEDURE record_event( foo_arg integer, name_arg text, data_arg jsonb, occurred_at_arg timestamptz ) AS $$ DECLARE column_name text; BEGIN -- This part is incidental: INSERT INTO foo_event(foo, name, data, occurred_at) VALUES(foo_arg, name_arg, data_arg, occurred_at_arg); -- This is the part I'm struggling with: FOR column_name IN (SELECT * FROM jsonb_object_keys(data_arg)) LOOP PREPARE update_query(text, text, integer) AS UPDATE foo SET $1 = $2 WHERE id = $3; EXECUTE update_query(column_name, data_arg->>column_name, foo_arg); END LOOP; END $$ LANGUAGE plpgsql;
但是 pg 不喜歡
$1
那個位置:psql:src/db/migrations/foo/up.sql:43: ERROR: syntax error at or near "$1" LINE 15: UPDATE foo SET $1 = $2 WHERE id = $3;
我還注意到有一個
json_populate_record()
似乎在這裡應該有用的功能,但我真的不明白如何從文件中將它應用於我的情況。我正在嘗試做的事情是否有意義並且可能?
您的案例過於動態
json_populate_record()
:它採用了您在致電時不知道(也沒有)的記錄類型。
PROCEDURE
呼叫UPDATE
每個 JSON 鍵概念證明,以顯示您的嘗試中沒有奏效的內容。
CREATE PROCEDURE record_event ( foo_arg int, name_arg text, data_arg jsonb, occurred_at_arg timestamptz ) AS $proc$ DECLARE column_name text; BEGIN INSERT INTO foo_event (foo , name , data , occurred_at) VALUES(foo_arg, name_arg, data_arg, occurred_at_arg); -- works, but inefficiently: FOR column_name IN SELECT * FROM jsonb_object_keys(data_arg) LOOP EXECUTE format('UPDATE foo SET %I = $1 WHERE id = $2', column_name) USING data_arg->>column_name, foo_arg; END LOOP; END $proc$ LANGUAGE plpgsql;
稱呼:
CALL record_event(1, 'name_arg', '{"col1":"val1","CoL2":"val2"}', now());
列名不能是動態的,所以格式化查詢(
format()
為方便起見)並使用EXECUTE
. 但是該子句更好地提供了值。USING
注意格式說明符
%I
,但參數$1
和$2
引用USING
子句提供的值(不是函式參數!)。但我不會使用它。多個
UPDATE
命令效率很低。而是使用 …功能單一
UPDATE
應該效率更高。
CREATE OR REPLACE FUNCTION record_event( foo_arg int, name_arg text, data_arg jsonb, occurred_at_arg timestamptz ) RETURNS void AS $func$ DECLARE _sql text; BEGIN INSERT INTO foo_event (foo , name , data , occurred_at) VALUES (foo_arg, name_arg, data_arg, occurred_at_arg); SELECT INTO _sql 'UPDATE foo SET ' || string_agg(format('%I = %L', key, value), ', ') || ' WHERE id = ' || foo_arg FROM jsonb_each_text(data_arg); IF _sql IS NOT NULL THEN -- RAISE NOTICE '%', _sql; -- uncomment instead of EXECUTE to debug EXECUTE _sql; END IF; END $func$ LANGUAGE plpgsql;
稱呼:
SELECT record_event(1, 'name_arg', '{"col1":"val1","CoL2":"val2"}', now());
我們可以
USING
用另一個子句傳遞值。但為了方便起見,只需連接整個命令。使用 a
FUNCTION
因為沒有什麼需要 aPROCEDURE
(自 Postgres 11 以來的新功能)。不過,也可以作為程序工作。與往常一樣,正確引用標識符和值以防禦可能的 SQL 注入。我只敢
foo_arg
直接連接,因為一個integer
值在這方面是安全的,並且作為未引用的數字文字工作。請注意,列名被視為區分大小寫。
有關的: