PostgreSQL:将函数存储在列中作为值

发布于 2024-12-14 07:13:30 字数 314 浏览 1 评论 0原文

函数可以作为匿名函数直接存储在列中作为其值吗?

假设我希望这个函数存储在列中。 示例(伪代码):

Table my_table: pk (int), my_function (func)

func ( x ) { return x * 100 }

稍后将其用作:

select 
    t.my_function(some_input) AS output
from 
    my_table as t 
where t.pk = 1999

每个 pk 的函数可能有所不同。

Can functions be stored as anonymous functions directly in column as its value?

Let's say I want this function be stored in column.
Example (pseudocode):

Table my_table: pk (int), my_function (func)

func ( x ) { return x * 100 }

And later use it as:

select 
    t.my_function(some_input) AS output
from 
    my_table as t 
where t.pk = 1999

Function may vary for each pk.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

半夏半凉 2024-12-21 07:13:30

你的标题要求的不是你的例子。

  1. 必须先创建函数,然后才能调用它。 (标题)
  2. 必须评估表达式。为此,您需要一个元功能。 (示例)

以下是针对这两种情况的解决方案:

1. 动态计算表达式

您必须考虑到结果类型可能会有所不同。我为此使用多态类型。

CREATE OR REPLACE FUNCTION f1(int)
  RETURNS int
  LANGUAGE sql IMMUTABLE AS
'SELECT $1 * 100;';

CREATE OR REPLACE FUNCTION f2(text)
  RETURNS text
  LANGUAGE sql IMMUTABLE AS
$SELECT $1 || '_foo';$;

CREATE TABLE my_expr (
  expr    text PRIMARY KEY
, def     text
, rettype regtype
);

INSERT INTO my_expr VALUES
  ('x', 'f1(3)'      , 'int')
, ('y', $f2('bar')$, 'text')
, ('z', 'now()'      , 'timestamptz')
;

CREATE OR REPLACE FUNCTION f_eval(text, _type anyelement = 'NULL'::text, OUT _result anyelement)
  LANGUAGE plpgsql AS
$func$
BEGIN
   EXECUTE
   'SELECT ' || (SELECT def FROM my_expr WHERE expr = $1)
   INTO _result;
END
$func$;

相关:

调用:

SQL 是严格类型化的,相同的结果列只能有一个数据类型。对于可能具有异构数据类型的多行,您可能会选择 text 类型,因为每种数据类型都可以与 text 进行转换:

SELECT *, f_eval(expr) AS result  -- default to type text
FROM   my_expr;

或者返回 multplce 列,例如:

SELECT *
     , CASE WHEN rettype = 'text'::regtype        THEN f_eval(expr) END                    AS text_result  -- default to type text
     , CASE WHEN rettype = 'int'::regtype         THEN f_eval(expr, NULL::int) END         AS int_result
     , CASE WHEN rettype = 'timestamptz'::regtype THEN f_eval(expr, NULL::timestamptz) END AS tstz_result
  -- , more?
FROM   my_expr;

db>>小提琴此处

2. 动态创建和使用函数

可以动态创建函数然后使用它们。但是,您无法使用普通 SQL 来做到这一点。您将必须使用另一个 函数 来执行此操作,或者至少使用一个匿名代码块(DO 语句),在 PostgreSQL 9.0 中引入。

它可以这样工作:

CREATE TABLE my_func (func text PRIMARY KEY, def text);

INSERT INTO my_func VALUES
  ('f'
 , $CREATE OR REPLACE FUNCTION f(int)
  RETURNS int
  LANGUAGE sql IMMUTABLE AS
'SELECT $1 * 100;'$);

CREATE OR REPLACE FUNCTION f_create_func(text)
  RETURNS void
  LANGUAGE plpgsql AS
$func$
BEGIN
   EXECUTE (SELECT def FROM my_func WHERE func = $1);
END
$func$;

调用:

SELECT f_create_func('f');
SELECT f(3);

db<>fiddle 此处

之后您可能想删除该函数。

在大多数情况下,您应该只创建函数并完成它。如果您遇到多个版本或权限的问题,请使用单独的架构。

有关我在这里使用的功能的更多信息,请参阅我在 dba.stackexchange.com 上的相关答案

Your title asks something else than your example.

  1. A function has to be created before you can call it. (title)
  2. An expression has to be evaluated. You would need a meta-function for that. (example)

Here are solutions for both:

1. Evaluate expressions dynamically

You have to take into account that the resulting type can vary. I use polymorphic types for that.

CREATE OR REPLACE FUNCTION f1(int)
  RETURNS int
  LANGUAGE sql IMMUTABLE AS
'SELECT $1 * 100;';

CREATE OR REPLACE FUNCTION f2(text)
  RETURNS text
  LANGUAGE sql IMMUTABLE AS
$SELECT $1 || '_foo';$;

CREATE TABLE my_expr (
  expr    text PRIMARY KEY
, def     text
, rettype regtype
);

INSERT INTO my_expr VALUES
  ('x', 'f1(3)'      , 'int')
, ('y', $f2('bar')$, 'text')
, ('z', 'now()'      , 'timestamptz')
;

CREATE OR REPLACE FUNCTION f_eval(text, _type anyelement = 'NULL'::text, OUT _result anyelement)
  LANGUAGE plpgsql AS
$func$
BEGIN
   EXECUTE
   'SELECT ' || (SELECT def FROM my_expr WHERE expr = $1)
   INTO _result;
END
$func$;

Related:

Call:

SQL is strictly typed, the same result column can only have one data type. For multiple rows with possibly heterogeneous data types, you might settle for type text, as every data type can be cast to and from text:

SELECT *, f_eval(expr) AS result  -- default to type text
FROM   my_expr;

Or return multplce columns like:

SELECT *
     , CASE WHEN rettype = 'text'::regtype        THEN f_eval(expr) END                    AS text_result  -- default to type text
     , CASE WHEN rettype = 'int'::regtype         THEN f_eval(expr, NULL::int) END         AS int_result
     , CASE WHEN rettype = 'timestamptz'::regtype THEN f_eval(expr, NULL::timestamptz) END AS tstz_result
  -- , more?
FROM   my_expr;

db<>fiddle here

2. Create and use functions dynamically

It is possible to create functions dynamically and then use them. You cannot do that with plain SQL, however. You will have to use another function to do that or at least an anonymous code block (DO statement), introduced in PostgreSQL 9.0.

It can work like this:

CREATE TABLE my_func (func text PRIMARY KEY, def text);

INSERT INTO my_func VALUES
  ('f'
 , $CREATE OR REPLACE FUNCTION f(int)
  RETURNS int
  LANGUAGE sql IMMUTABLE AS
'SELECT $1 * 100;'$);

CREATE OR REPLACE FUNCTION f_create_func(text)
  RETURNS void
  LANGUAGE plpgsql AS
$func$
BEGIN
   EXECUTE (SELECT def FROM my_func WHERE func = $1);
END
$func$;

Call:

SELECT f_create_func('f');
SELECT f(3);

db<>fiddle here

You may want to drop the function afterwards.

In most cases you should just create the functions instead and be done with it. Use separate schemas if you have problems with multiple versions or privileges.

For more information on the features I used here, see my related answer on dba.stackexchange.com.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文