ind*_*ago 7 postgresql plpgsql postgresql-9.1 upsert
我在 PostgreSQL 9.1 中有一个名为fun_test
. 它有一个复合类型作为输入参数,我在调用它时不断收到转换错误。
CREATE OR REPLACE FUNCTION netcen.fun_test(myobj netcen.testobj)
RETURNS boolean AS
$BODY$
DECLARE
tmp_code smallint;
cur_member refcursor;
BEGIN
-- Check if the member exists first
OPEN cur_member FOR
EXECUTE 'SELECT testkey FROM netcen.test WHERE testkey=' || myobj.testkey ;
FETCH cur_member INTO tmp_code;
CLOSE cur_member;
CASE tmp_code
WHEN COALESCE(tmp_code,0)=0 THEN
-- Record not found INSERT a new record
-- will skip user defined validation for now
insert into netcen.test values(myobj.testkey,
myobj.tes,
myobj.testname);
ELSE
-- Record found UPDATE the record
update netcen.test set
test=myobj.test,
testname=myobj.testname WHERE testkey=myobj.testkey;
END CASE;
END;$BODY$
LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)
下面是类型 testobj
CREATE TYPE netcen.testobj AS
(testkey smallint,
tes text,
testname text);
Run Code Online (Sandbox Code Playgroud)
当我调用函数时:
SELECT netcen.fun_test('(3,khaendra@me.com,khaendra)':: netcen.testobj);
Run Code Online (Sandbox Code Playgroud)
.. 我收到以下错误消息:
ERROR: operator does not exist: smallint = boolean
LINE 1: SELECT "__Case__Variable_8__" IN (COALESCE(tmp_code,0)=0)
^
HINT: No operator matches the given name and argument type(s).
You might need to add explicit type casts.
QUERY: SELECT "__Case__Variable_8__" IN (COALESCE(tmp_code,0)=0)
CONTEXT: PL/pgSQL function "fun_test" line 11 at CASE
Run Code Online (Sandbox Code Playgroud)
我应该在哪里投?
表的定义netcen.test
:
CREATE TABLE netcen.test (
testkey smallint NOT NULL DEFAULT 0,
tes netcen.dom_email_validation,
testname text,
CONSTRAINT key PRIMARY KEY (testkey)
)
Run Code Online (Sandbox Code Playgroud)
@ Erwin,感谢您的链接。我阅读并修改了我的函数,请仔细阅读并告诉我它是否可以与多个客户端同时调用相同的函数一起工作?
CREATE OR REPLACE FUNCTION netcen.fun_test_modified(myobj netcen.test)
RETURNS boolean AS
$BODY$
DECLARE
myoutput boolean :=false;
BEGIN
update netcen.test set
tes=myobj.tes,
testname=myobj.testname WHERE testkey=myobj.testkey;
IF FOUND THEN
myoutput:= TRUE;
RETURN myoutput;
END IF;
BEGIN
INSERT INTO netcen.test values(myobj.testkey,
myobj.tes,
myobj.testname);
myoutput:= TRUE;
EXCEPTION WHEN OTHERS THEN
update netcen.test set
tes=myobj.tes,
testname=myobj.testname WHERE testkey=myobj.testkey;
myoutput:= TRUE;
END;
RETURN myoutput;
END;
$BODY$ LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)
我已经取消了类型测试,只使用了表格test
!我不知道这可以工作!
该错误发生在这里:
CASE tmp_code
WHEN COALESCE(tmp_code,0)=0 THEN
Run Code Online (Sandbox Code Playgroud)
像这样工作:
CASE WHEN COALESCE(tmp_code,0)=0 THEN
Run Code Online (Sandbox Code Playgroud)
您以不兼容的方式混合了两种不同的PL/pgSQLCASE
语法变体(“简单”与“搜索”)。
还有一个错误:
update netcen.test set
test=myobj.test,
testname=myobj.testname WHERE testkey=myobj.testkey;
Run Code Online (Sandbox Code Playgroud)
你的意思是:
UPDATE test
SET tes = myobj.tes
, testname = myobj.testname
WHERE testkey = myobj.testkey;
Run Code Online (Sandbox Code Playgroud)
也没有必要CREATE TYPE netcen.testobj ...
。您可以使用表名netcen.test
作为类型名。
在 Postgres 9.5 或更高版本中,使用新的 UPSERT ( INSERT ... ON CONFLICT DO UPDATE
)。喜欢:
对于旧版本,您可以模拟UPSERT
. plpgsql中的简单形式,没有并发:
CREATE OR REPLACE FUNCTION fun_test(myobj testobj)
RETURNS boolean
LANGUAGE plpgsql AS
$func$
BEGIN
UPDATE test
SET tes = myobj.tes
, testname = myobj.testname
WHERE testkey = myobj.testkey;
IF FOUND THEN
RETURN FALSE;
ELSE
INSERT INTO test SELECT (myobj).*;
RETURN TRUE;
END IF;
END
$func$;
Run Code Online (Sandbox Code Playgroud)
可能只是带有数据修改 CTE 的普通 SQL :
WITH my_row(testkey, tes, testname) AS (
SELECT 1::smallint, 'khaendra@me.net', 'khaendra'
)
, u AS (
UPDATE test t
SET tes = m.tes
, testname = m.testname
FROM my_row m
WHERE t.testkey = m.testkey
RETURNING t.testkey
)
INSERT INTO test (testkey, tes, testname)
SELECT * FROM my_row
WHERE NOT EXISTS (SELECT FROM u);
Run Code Online (Sandbox Code Playgroud)
使用这种形式(单个组合语句),可能出现竞争条件的时间窗口非常小。如果并发仍然是一个问题(重并发写入负载),那么......
如果函数返回,则该行已被插入或更新。唯一的另一种方式是一种EXCEPTION
不同的方式。返回的值true
只是噪音。(返回true
forINSERT
和false
for可能更有趣UPDATE
。)所以我简化了:
CREATE OR REPLACE FUNCTION netcen.fun_test_modified(myobj netcen.test)
RETURNS boolean
LANGUAGE plpgsql AS
$func$
BEGIN
UPDATE netcen.test
SET tes = myobj.tes
, testname = myobj.testname
WHERE testkey = myobj.testkey;
IF FOUND THEN
RETURN true;
END IF;
BEGIN
INSERT INTO netcen.test
SELECT (myobj).*; -- simpler form, parenthesis needed.
EXCEPTION WHEN unique_violation THEN -- cleaner
UPDATE netcen.test
SET tes = myobj.tes
, testname = myobj.testname
WHERE testkey = myobj.testkey;
END;
RETURN true;
END
$func$;
Run Code Online (Sandbox Code Playgroud)
SO的相关答案:
虽然 klin 在他关于如何修复您当前功能的回答中在技术上是正确的,但让我质疑您的整个方法。您尝试实现的目标称为“UPSERT”,使用 PostgreSQL 9.1(提供可写 CTE),您可以通过一种非常简单的方法来实现这一目标。为了清楚起见,我省略了函数定义,但您可以轻松地将其包装在查询语言函数中(即以 结尾LANGUAGE sql
):
WITH upd AS (
UPDATE netcen.test
SET (test, testname) = ($1.test, $1.testname)
WHERE testkey = $1.testkey
RETURNING *
)
INSERT INTO netcen.test (
testkey,
test,
testname
)
VALUES (
$1.testkey,
$1.tes,
$1.testname
)
WHERE NOT EXISTS (
SELECT 1
FROM upd
);
Run Code Online (Sandbox Code Playgroud)
这样您就可以避免使用昂贵的游标和更昂贵的EXCEPTION
子句。
请注意,这与 Erwin 在其答案中提供的几个解决方案之一基本相同(由于历史原因,两个问题与所有答案合并为一个)。所以关于并发的警告也适用于这一点。
归档时间: |
|
查看次数: |
895 次 |
最近记录: |