重构PL / pgSQL函数以返回各种SELECT查询的输出

问题描述 投票:29回答:3

我写了一个函数,输出一个以文本形式组成的PostgreSQL SELECT查询。现在我不想再输出文本,但实际上对数据库运行生成的SELECT语句并返回结果 - 就像查询本身一样。

What I have so far:

CREATE OR REPLACE FUNCTION data_of(integer)
  RETURNS text AS
$BODY$
DECLARE
   sensors varchar(100);   -- holds list of column names
   type    varchar(100);   -- holds name of table
   result  text;           -- holds SQL query
       -- declare more variables

BEGIN
      -- do some crazy stuff

      result := 'SELECT\r\nDatahora,' || sensors ||
      '\r\n\r\nFROM\r\n' || type ||
      '\r\n\r\nWHERE\r\id=' || $1 ||'\r\n\r\nORDER BY Datahora;';

      RETURN result;
END;
$BODY$
LANGUAGE 'plpgsql' VOLATILE;
ALTER FUNCTION data_of(integer) OWNER TO postgres;

sensors保存表type的列名列表。这些是在功能过程中声明和填写的。最终,他们拥有如下价值观:

  • qazxsw poi:qazxsw poi 除了qazxsw poi(qazxsw poi),所有列都是sensors类型。
  • 'column1, column2, column3'Datahora 可以是四个表之一的名称。每个列都有不同的列,除了常见的列timestamp

double precision

变量type将保存此处显示的所有列,用于'myTable'中的相应表。例如:如果DatahoraDefinition of the underlying tables.那么sensors将是type

变量用于构建存储在type中的pcdmet语句。喜欢:

sensors

现在,我的函数将此语句作为'datahora,dirvento,precipitacao,pressaoatm,radsolacum,tempar,umidrel,velvento'返回。我复制粘贴并在pgAdmin或psql中执行它。我想自动执行此操作,自动运行查询并返回结果。我怎样才能做到这一点?

sql database postgresql plpgsql dynamic-sql
3个回答
68
投票

动态SQL和SELECT类型

(我最后保存了最好的,继续阅读!) 您想要执行动态SQL。原则上,在result的帮助下,这在plpgsql中很简单。你不需要游标 - 实际上,大多数时候你没有明确的游标会更好。 SELECT Datahora, column1, column2, column3 FROM myTable WHERE id=20 ORDER BY Datahora;

您遇到的问题:您想要返回尚未定义类型的记录。函数需要使用text子句(或使用RETURNEXECUTE参数)声明返回类型。在您的情况下,您将不得不回退到匿名记录,因为返回列的数量,名称和类型各不相同。喜欢:

Find examples on SO with a search

但是,这不是特别有用。这样,您必须在每次调用函数时提供列定义列表。喜欢:

RETURNS

但是,如果你事先不知道这些列,你甚至会怎么做呢? 您可以采用结构较少的文档数据类型,如OUTINOUTCREATE FUNCTION data_of(integer) RETURNS SETOF record AS ... SELECT * FROM data_of(17) AS foo (colum_name1 integer , colum_name2 text , colum_name3 real);

  • json

但是出于这个问题的目的,让我们假设你想尽可能多地返回单独的,正确输入和命名的列。

固定返回类型的简单解决方案

jsonb似乎是给定的,我将假设数据类型hstore并且总是有两个具有不同名称和数据类型的列。

名称我们将放弃支持返回类型中的通用名称。 类型我们也将放弃,并将所有类型转换为xml,因为每种数据类型都可以转换为How to store a data table (or List<KeyValuePair<int,Object>>, or Dictionary) in database?

datahora

How does this work?

  • 变量timestamptext可以是输入参数。
  • 请注意text条款。
  • 注意使用CREATE OR REPLACE FUNCTION data_of(_id integer) RETURNS TABLE (datahora timestamp, col2 text, col3 text) AS $func$ DECLARE _sensors text := 'col1::text, col2::text'; -- cast each col to text _type text := 'foo'; BEGIN RETURN QUERY EXECUTE ' SELECT datahora, ' || _sensors || ' FROM ' || quote_ident(_type) || ' WHERE id = $1 ORDER BY datahora' USING _id; END $func$ LANGUAGE plpgsql; 。这是从动态查询返回行的更优雅的方法之一。
  • 我使用函数参数的名称,只是为了使_sensors_type子句更容易混淆。 SQL字符串中的RETURNS TABLE不引用函数参数,而是引用RETURN QUERY EXECUTE子句传递的值。 (在这个简单的例子中,两者都恰好是各自范围内的USING。)
  • 请注意RETURN QUERY EXECUTE的示例值:每个列都转换为类型$1
  • 这种代码非常容易受到USING的攻击。我用$1来防止它。将变量_sensors中的几个列名汇集在一起​​可以防止使用text(通常是一个坏主意!)。确保没有其他方式存在任何不良内容,例如通过SQL injection单独运行列名称。想到一个quote_ident()参数......

Simpler with PostgreSQL 9.1+

使用9.1或更高版本,您可以使用_sensors进一步简化:

quote_ident()

同样,单个列名称可以正确转义,并且将是干净的方式。

可变数量的列共享相同的类型

在您的问题更新后,它看起来像您的返回类型

  • 可变数量的列
  • 但所有相同类型的列quote_ident()(别名VARIADIC

因为我们必须定义函数的format()类型,所以我在这种情况下使用RETURN QUERY EXECUTE format(' SELECT datahora, %s -- identifier passed as unescaped string FROM %I -- assuming the name is provided by user WHERE id = $1 ORDER BY datahora' ,_sensors, _type) USING _id; 类型,它可以包含可变数量的值。另外,我返回一个包含列名的数组,因此您也可以解析结果中的名称:

double precision

Various complete table types

如果你实际上试图返回表的所有列(例如float8中的一个,那么使用这个简单,非常强大的RETURN解决方案:

ARRAY

呼叫:

CREATE OR REPLACE FUNCTION data_of(_id integer)
  RETURNS TABLE (datahora timestamp, names text[], values float8[] ) AS
$func$
DECLARE
   _sensors text := 'col1, col2, col3';  -- plain list of column names
   _type    text := 'foo';
BEGIN
   RETURN QUERY EXECUTE format('
      SELECT datahora
           , string_to_array($1)  -- AS names
           , ARRAY[%s]            -- AS values
      FROM   %s
      WHERE  id = $2
      ORDER  BY datahora'
    , _sensors, _type)
   USING  _sensors, _id;
END
$func$  LANGUAGE plpgsql;

用任何其他表名替换调用中的tables at the linked page

How does this work?

  • polymorphic type是伪数据类型,多态类型,任何非数组数据类型的占位符。函数中出现的所有CREATE OR REPLACE FUNCTION data_of(_tbl_type anyelement, _id int) RETURNS SETOF anyelement AS $func$ BEGIN RETURN QUERY EXECUTE format(' SELECT * FROM %s -- pg_typeof returns regtype, quoted automatically WHERE id = $1 ORDER BY datahora' , pg_typeof(_tbl_type)) USING _id; END $func$ LANGUAGE plpgsql; 都会计算为运行时提供的相同类型。通过提供定义类型的值作为函数的参数,我们隐式定义了返回类型。
  • PostgreSQL自动为每个创建的表定义行类型(复合数据类型),因此每个表都有一个定义良好的类型。这包括临时表,便于临时使用。
  • 任何类型都可以是SELECT * FROM data_of(NULL::pcdmet, 17); 。所以我们交出一个pcdmet值,转换为表格类型。
  • 现在函数返回一个定义良好的行类型,我们可以使用anyelement来分解行并获取各个列。
  • anyelement将表的名称返回为NULL。当自动转换为NULL时,标识符会自动双引号并在需要时进行模式限定。因此,SQL注入是不可能的。这甚至可以处理模式限定的表名SELECT * FROM data_of(...)

3
投票

你可能想要返回一个pg_typeof(_tbl_type)。尝试这样的事情(我还没试过):

object identifier type regtype

1
投票

我很遗憾地说,但你的问题很不清楚。但是下面你会找到一个自包含的例子,如何创建和使用一个返回游标变量的函数。希望能帮助到你 !

text
© www.soinside.com 2019 - 2024. All rights reserved.