1

对于某些报告,我需要使用类似这样的方法从考虑中删除极低和极高的值

SELECT ...
FROM
(
  SELECT val, ntile(10) OVER(ORDER BY val) AS tile FROM table
) AS tiled_table
WHERE tile > 1 AND tile < 10

它可以被提取到存储过程中,该过程将采用表名和列名,连接字符串并执行查询,但有时我需要在另一个查询的结果上使用此过程。有没有办法在 PostgreSQL 中做到这一点?

4

3 回答 3

1

要解决此问题,您将需要动态 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

然后,当子查询用括号括起来时,这将起作用。如果它们不在原始查询中,您可以添加括号。

于 2012-10-25T14:56:50.517 回答
1

如果您总是返回同一组列,则可以创建一个传递表和列名的集合返回函数。可以像表格一样使用:

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 ...

如果您的查询每次需要时返回不同数量的列,这将不起作用。

于 2012-10-25T15:04:29.833 回答
0

如果要使用函数来处理查询的结果集,最好的选择是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 不会太可怕。请参阅这个较早的答案

于 2012-10-26T00:10:11.360 回答