【问题标题】:How to write a function that returns text or integer values?如何编写返回文本或整数值的函数?
【发布时间】:2013-07-09 09:24:16
【问题描述】:

我使用的是 PostgreSQL 9.2.4。

postgres=# 选择版本();

                           version
-------------------------------------------------------------
 PostgreSQL 9.2.4, compiled by Visual C++ build 1600, 64-bit
(1 row)

sqlfiddle link

我的查询安全地执行插入。我需要的是我的函数应该返回除了 void 数据类型之外的东西。像 text("inserted into table") 或 integer(0-false,1-true) 之类的东西,对我验证它是否被插入很有用?

我需要一个函数的语法,它在插入完成时返回 integertext。用于验证目的。有没有办法解决这个问题?

【问题讨论】:

  • returns integer 而不是 returns void?
  • 您是否尝试过RETURNING 子句用于INSERT 查询?详情here.
  • Postgres 文档中关于 PL/pgSQL 函数的哪些部分不清楚或没有帮助?
  • 您使用的是什么客户端驱动程序? Java + PgJDBC? C# + nPgSQL? Python + psycopg2?什么? 您需要在客户端执行此操作
  • java + pgJDBC 是我的驱动

标签: postgresql types plpgsql postgresql-9.2


【解决方案1】:

你可能需要什么

您很可能需要一个函数返回text,另一个函数返回integer,或者一个返回boolean 的函数表示成功。所有这些都是微不足道的,我会将您推荐给 excellent manual on CREATE FUNCTIONcode examples in similar questions on SO

你实际问了什么

如何编写返回文本或整数值的函数?

...从某种意义上说,我们有 one 返回类型是textinteger。不像建议的那样微不足道,但也不是不可能的。关键词是:polymorphic types

建立在这张简单的桌子上:

CREATE TABLE tbl(
  tbl_id int,
  txt    text,
  nr     int
);

此函数根据输入类型返回整数或文本(或任何其他类型,如果您允许的话)。

CREATE FUNCTION f_insert_data(_id int, _data anyelement, OUT _result anyelement)
  RETURNS anyelement AS
$func$
BEGIN

CASE pg_typeof(_data) 
WHEN 'text'::regtype THEN
    INSERT INTO tbl(tbl_id, txt) VALUES(_id, _data)
    RETURNING txt
    INTO _result;

WHEN 'integer'::regtype THEN
    INSERT INTO tbl(tbl_id, nr) VALUES(_id, _data)
    RETURNING nr
    INTO _result;

ELSE
    RAISE EXCEPTION 'Unexpected data type: %', pg_typeof(_data)::text;
END CASE;

END
$func$
LANGUAGE plpgsql;

呼叫:

SELECT f_insert_data(1, 'foo'::text);  -- explicit cast needed.
SELECT f_insert_data(1, 7);

简单案例

一个函数返回TRUE/FALSE表示是否插入了一行,只有一个可变类型的输入参数:

CREATE FUNCTION f_insert_data2(_id int, _data anyelement)
  RETURNS boolean AS
$func$
BEGIN

CASE pg_typeof(_data)
WHEN 'text'::regtype THEN
   INSERT INTO tbl(tbl_id, txt) VALUES(_id, _data);

WHEN 'integer'::regtype THEN
   INSERT INTO tbl(tbl_id, nr) VALUES(_id, _data);

ELSE
   RAISE EXCEPTION 'Unexpected data type: >>%<<', pg_typeof(_data)::text;
END CASE;

IF FOUND THEN RETURN TRUE;
ELSE RETURN FALSE;
END IF;

END
$func$
LANGUAGE plpgsql;

在大多数情况下,输入类型可以替换为text 参数,可以转换为任何其他类型。

【讨论】:

  • 只有我一个人,还是这背后的整个想法真的很奇怪?仅在执行独立插入时使用受影响的行数应该没问题,否则有GET DIAGNOSTICSFOUNDINSERT ... RETURNING
  • @CraigRinger:一定只有你。 :p 说真的,我认为 OP 不需要任何这些,正如已经轻轻暗示的那样。这是用于特殊用途的高级东西。
  • 是的。我添加了一个示例,说明如何使用 Python 以理智的方式进行操作,因为未指定 OP 使用的工具/语言。我怀疑他们在一个地方,虽然没有真正理解,但喝了“所有东西的存储过程”kool-aid...
【解决方案2】:

听起来你是在通过制造更大的问题来解决问题。

你根本不需要这个函数。通过检查每个 DML 查询返回的受影响行数,在客户端执行此操作,或使用 INSERT ... RETURNING

你没有提到你的客户端语言,所以这里是如何在 Python 中使用psycopg2 来做到这一点。相同的方法适用于具有语法变化的其他语言。

#!/usr/bin/env python
import psycopg2

# Connect to the db
conn = psycopg2.connect("dbname=regress")
curs = conn.cursor()

# Set up the table to use
curs.execute("""
DROP TABLE IF EXISTS so17587735;

CREATE TABLE so17587735 (
    id serial primary key,
    blah text not null
);
""");

# Approach 1: Do the insert and check the rowcount:
curs.execute("""
INSERT INTO so17587735(blah) VALUES ('whatever');
""");
if curs.rowcount != 1:
    raise Exception("Argh, insert affected zero rows, wtf?")
print("Inserted {0} rows as expected".format(curs.rowcount))

# Approach 2: Use RETURNING
curs.execute("""
INSERT INTO so17587735(blah) VALUES ('bored') RETURNING id;
""");
returned_rows = curs.fetchall();
if len(returned_rows) != 1:
    raise Exception("Got unexpected row count {0} from INSERT".format(len(returned_rows)))

print("Inserted row id is {0}".format(returned_rows[0][0]))

在 PL/PgSQL 调用INSERT 的情况下,您可以使用the GET DIAGNOSTICS commandFOUND 变量或RETURN QUERY EXECUTE INSERT ... RETURNING ...。使用GET DIAGNOSTICS

CREATE OR REPLACE FUNCTION blah() RETURNS void AS $$
DECLARE
    inserted_rows integer;
BEGIN
    INSERT INTO some_table VALUES ('whatever');
    GET DIAGNOSTICS inserted_rows = ROW_COUNT;
    IF inserted_rows <> 1 THEN
        RAISE EXCEPTION 'Failed to insert rows; expected 1 row, got %', inserted_rows;
    END IF;
END;
$$ LANGUAGE plpgsql VOLATILE;

或者如果您必须返回值并且由于某种原因必须使用 PL/PgSQL:

CREATE OR REPLACE FUNCTION blah() RETURNS SETOF integer AS $$
BEGIN
    RETURN QUERY EXECUTE INSERT INTO some_table VALUES ('whatever') RETURNING id;
END;
$$ LANGUAGE plpgsql VOLATILE;

(假设密钥是id

与以下内容相同:

CREATE OR REPLACE FUNCTION blah() RETURNS SETOF integer AS $$
INSERT INTO some_table VALUES ('whatever') RETURNING id;
$$ LANGUAGE sql;

或者只是

INSERT INTO some_table VALUES ('whatever') RETURNING id;

换句话说:为什么将它包装在一个函数中?这没有意义。只需检查客户端的行数,使用RETURNING 或使用客户端驱动程序的受影响行数INSERT

【讨论】:

  • 实际上的情况是我有一个使用函数的后端代码,因为我保留了它的 SQL FIDDLE!当我将它与java前端连接时,我需要函数的响应,即返回值,以便我可以确保插入数据库已经完成..所以我想澄清是否有任何返回值的机会到被调用的函数。或者有没有其他方法可以解决这个问题? @craig 雷格
  • @user2561626 当然可以。检查过程中的GET DIAGNOSTICS,如果没有报告行,则检查RAISE EXCEPTION
  • @user2561626 答案已重写。
【解决方案3】:

一个函数只能返回一种类型。在您的情况下,您可以创建一个包含两个字段(一个整数和一个文本)的复合类型,然后返回它。

【讨论】:

  • 好的,部分。多态返回类型需要一个多态参数,这里不是这种情况。
猜你喜欢
  • 2020-06-03
  • 2015-03-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-25
  • 1970-01-01
  • 1970-01-01
  • 2019-04-24
相关资源
最近更新 更多