对于某些报告,我需要使用类似这样的方法从考虑中删除极低和极高的值
SELECT ...
FROM
(
SELECT val, ntile(10) OVER(ORDER BY val) AS tile FROM table
) AS tiled_table
WHERE tile > 1 AND tile < 10
它可以被提取到存储过程中,该过程将采用表名和列名,连接字符串并执行查询,但有时我需要在另一个查询的结果上使用此过程。有没有办法在 PostgreSQL 中做到这一点?
对于某些报告,我需要使用类似这样的方法从考虑中删除极低和极高的值
SELECT ...
FROM
(
SELECT val, ntile(10) OVER(ORDER BY val) AS tile FROM table
) AS tiled_table
WHERE tile > 1 AND tile < 10
它可以被提取到存储过程中,该过程将采用表名和列名,连接字符串并执行查询,但有时我需要在另一个查询的结果上使用此过程。有没有办法在 PostgreSQL 中做到这一点?
要解决此问题,您将需要动态 SQL。通常,查询中不能有动态表名。有关动态 SQL 的信息,请参阅http://www.postgresql.org/docs/9.2/static/ecpg-dynamic.html 。
如果将查询构造为:
FROM
(
SELECT val, ntile(10) OVER(ORDER BY val) AS tile FROM <subquery> t
) AS tiled_table
然后,当子查询用括号括起来时,这将起作用。如果它们不在原始查询中,您可以添加括号。
如果您总是返回同一组列,则可以创建一个传递表和列名的集合返回函数。可以像表格一样使用:
create or replace function get_values(tablename text, columnname text)
returns table (id integer, foobar text)
as
$$
BEGIN
RETURN QUERY EXECUTE 'select id, '||columname||' as foobar from '||tablename;
END;
$$
language plpgsql;
然后,每当您需要这些值时,您可以使用:
select v.*,
t.foobar
from get_values('table_1', 'some_column') v
join table_2 t on ...
如果您的查询每次需要时返回不同数量的列,这将不起作用。
如果要使用函数来处理查询的结果集,最好的选择是SELECT ... INTO TEMPORARY TABLE
使用临时表名调用该函数。
考虑到生成的行集是多么微不足道,在 PostgreSQL 函数中使用行集非常困难。我知道的唯一方法是使用 refcursor、处理临时表或实现聚合或窗口函数。后两个选项不允许您控制返回的行数,因此它们不适合您的目的。
函数不能引用调用函数的 CTE 中的公用表表达式别名,因此不能使用 CTE 创建虚拟表并将表名传递给函数。显示它不起作用的示例:
CREATE OR REPLACE FUNCTION dynsql(tname text, colname text) RETURNS SETOF RECORD AS
$$
BEGIN
RETURN QUERY EXECUTE format('SELECT %I FROM %I', colname, tname);
END;
$$ LANGUAGE plpgsql;
WITH dummy(col) AS (VALUES (1),(2),(3))
SELECT * FROM dynsql('dummy','col') t(id integer);
结果:
ERROR: relation "dummy" does not exist
...因为表达式中的别名是WITH
表达式的局部WITH
变量。(能够从函数中引用它会很好,但这也会产生各种令人兴奋的名称冲突问题和SECURITY DEFINER
函数安全问题。)
虽然您可以编写一个使用 refcursor 的 PL/PgSQL 函数,但这需要您DECLARE
使用带有查询的游标并将其传递给函数。您不能只使用普通的函数调用语法。它也非常低效,并且需要LOOP
在函数中使用。我认为这不会有太大帮助。
在实现该功能时,使用EXECUTE format(...) USING ...
以保持动态 SQL 不会太可怕。请参阅这个较早的答案。