wal*_*.ar 32 sql database postgresql dynamic-sql plpgsql
我写了一个函数,输出一个SELECT以文本形式组成的PostgreSQL 查询.现在我不想再输出文本,但实际上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;
Run Code Online (Sandbox Code Playgroud)
sensors保存表的列名列表type.这些是在功能过程中声明和填写的.最终,他们拥有如下价值观:
sensors:'column1, column2, column3'
除Datahora(timestamp)所有列都是类型double precision.
type:'myTable'
可以是四个表之一的名称.除了公共列之外,每个列都有不同的列Datahora.
该变量sensors将保存此处显示的相应表中的所有列type.例如:如果type是pcdmet再sensors会'datahora,dirvento,precipitacao,pressaoatm,radsolacum,tempar,umidrel,velvento'
变量用于构建SELECT存储在其中的语句result.喜欢:
SELECT Datahora, column1, column2, column3
FROM myTable
WHERE id=20
ORDER BY Datahora;
Run Code Online (Sandbox Code Playgroud)
现在,我的函数返回此语句为text.我复制粘贴并在pgAdmin或psql中执行它.我想自动执行此操作,自动运行查询并返回结果.我怎样才能做到这一点?
Erw*_*ter 74
RETURN类型(我最后保存了最好的,继续阅读!)
你想要执行动态SQL.原则上,在plpgsql的帮助下,这很简单EXECUTE.你不需要游标 - 实际上,大多数时候你没有明确的游标会更好.
通过搜索在SO上查找示例.
您遇到的问题:您想要返回尚未定义类型的记录.函数需要使用RETURNS子句(或带OUT或INOUT参数)声明返回类型.在您的情况下,您将不得不回退到匿名记录,因为返回列的数量,名称和类型各不相同.喜欢:
CREATE FUNCTION data_of(integer)
RETURNS SETOF record AS ...
Run Code Online (Sandbox Code Playgroud)
但是,这不是特别有用.这样,您必须在每次调用函数时提供列定义列表.喜欢:
SELECT * FROM data_of(17)
AS foo (colum_name1 integer
, colum_name2 text
, colum_name3 real);
Run Code Online (Sandbox Code Playgroud)
但是,如果你事先不知道这些列,你甚至会怎么做呢?
你可以采取一个不太结构化文档数据类型,例如json,jsonb,hstore或xml:
但是出于这个问题的目的,让我们假设你想尽可能多地返回单独的,正确输入和命名的列.
该列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;
Run Code Online (Sandbox Code Playgroud)
变量_sensors和_type可能的输入参数来代替.
请注意该RETURNS TABLE条款.
注意使用RETURN QUERY EXECUTE.这是从动态查询返回行的更优雅的方法之一.
我使用函数参数的名称,只是为了使USING条款RETURN QUERY EXECUTE更容易混淆.$1在SQL-string中没有引用函数参数,而是引用随该USING子句传递的值.($1在这个简单的例子中,两者恰好都在各自的范围内.)
请注意以下示例值_sensors:将每个列强制转换为类型text.
这种代码非常容易受到SQL注入的攻击.我quote_ident()用来防止它.将变量中的几个列名集中在一起_sensors可以防止使用quote_ident()(并且通常是一个坏主意!).确保没有其他方式存在任何不良内容,例如通过单独运行列名称quote_ident().VARIADIC想到一个参数......
使用9.1或更高版本,您可以使用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;
Run Code Online (Sandbox Code Playgroud)
同样,单个列名称可以正确转义,并且将是干净的方式.
在您的问题更新后,它看起来像您的返回类型
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;
Run Code Online (Sandbox Code Playgroud)
如果您实际上是在尝试返回表的所有列(例如链接页面上的一个表,那么请使用这个简单,非常强大的多态类型解决方案:
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;
Run Code Online (Sandbox Code Playgroud)
呼叫:
SELECT * FROM data_of(NULL::pcdmet, 17);
Run Code Online (Sandbox Code Playgroud)
pcdmet使用任何其他表名替换呼叫.
anyelement是一种伪数据类型,多态类型,任何非数组数据类型的占位符.anyelement函数中出现的所有内容都计算为运行时提供的相同类型.通过提供定义类型的值作为函数的参数,我们隐式定义了返回类型.
PostgreSQL自动为每个创建的表定义行类型(复合数据类型),因此每个表都有一个定义良好的类型.这包括临时表,便于临时使用.
任何类型都可以NULL.所以我们交出一个NULL值,转换为表类型.
现在函数返回一个明确定义的行类型,我们可以SELECT * FROM data_of(...)用来分解行并获取各个列.
pg_typeof(_tbl_type)返回表的名称作为对象标识符类型regtype.自动转换为text标识符时,标识符会自动双引号并在需要时进行模式限定.因此,SQL注入是不可能的.这甚至可以处理模式修饰表的名字在那里quote_ident()会失败.