我写了一个函数,输出一个以文本形式组成的PostgreSQL SELECT
查询。现在我不想再输出文本,但实际上对数据库运行生成的SELECT
语句并返回结果 - 就像查询本身一样。
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
的列名列表。这些是在功能过程中声明和填写的。最终,他们拥有如下价值观:
sensors
类型。'column1, column2, column3'
:Datahora
可以是四个表之一的名称。每个列都有不同的列,除了常见的列timestamp
。double precision
变量type
将保存此处显示的所有列,用于'myTable'
中的相应表。例如:如果Datahora
是Definition of the underlying tables.那么sensors
将是type
变量用于构建存储在type
中的pcdmet
语句。喜欢:
sensors
现在,我的函数将此语句作为'datahora,dirvento,precipitacao,pressaoatm,radsolacum,tempar,umidrel,velvento'
返回。我复制粘贴并在pgAdmin或psql中执行它。我想自动执行此操作,自动运行查询并返回结果。我怎样才能做到这一点?
SELECT
类型(我最后保存了最好的,继续阅读!)
您想要执行动态SQL。原则上,在result
的帮助下,这在plpgsql中很简单。你不需要游标 - 实际上,大多数时候你没有明确的游标会更好。
SELECT Datahora, column1, column2, column3
FROM myTable
WHERE id=20
ORDER BY Datahora;
。
您遇到的问题:您想要返回尚未定义类型的记录。函数需要使用text
子句(或使用RETURN
或EXECUTE
参数)声明返回类型。在您的情况下,您将不得不回退到匿名记录,因为返回列的数量,名称和类型各不相同。喜欢:
Find examples on SO with a search
但是,这不是特别有用。这样,您必须在每次调用函数时提供列定义列表。喜欢:
RETURNS
但是,如果你事先不知道这些列,你甚至会怎么做呢?
您可以采用结构较少的文档数据类型,如OUT
,INOUT
,CREATE 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
timestamp
和text
可以是输入参数。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()
参数......使用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
如果你实际上试图返回表的所有列(例如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。
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;
都会计算为运行时提供的相同类型。通过提供定义类型的值作为函数的参数,我们隐式定义了返回类型。SELECT * FROM data_of(NULL::pcdmet, 17);
。所以我们交出一个pcdmet
值,转换为表格类型。anyelement
来分解行并获取各个列。anyelement
将表的名称返回为NULL
。当自动转换为NULL
时,标识符会自动双引号并在需要时进行模式限定。因此,SQL注入是不可能的。这甚至可以处理模式限定的表名SELECT * FROM data_of(...)
。你可能想要返回一个pg_typeof(_tbl_type)
。尝试这样的事情(我还没试过):
object identifier type regtype
我很遗憾地说,但你的问题很不清楚。但是下面你会找到一个自包含的例子,如何创建和使用一个返回游标变量的函数。希望能帮助到你 !
text