【问题标题】:How to extract the common behavior of removing extremes?如何提取消除极端的常见行为?
【发布时间】:2012-10-26 08:15:40
【问题描述】:

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

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 postgresql


    【解决方案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
    

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

    【讨论】:

    • 虽然有缺点——如果你做一个这样的函数,你将无法在查询中使用准备好的语句,你需要在客户端引用你的查询参数,这很容易搞砸了。
    • @Tometzky 是的。这是一个潜在的巨大的 SQL 注入安全漏洞,要求客户端对标识符引用等非常偏执。
    【解决方案2】:

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

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

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

    【讨论】:

    • 这(字符串连接)正是我想要避免的。
    • @synapse: 如果你想指定列名和表名作为参数,我看不出动态 SQL 有什么办法(除非你有 非常 有限的一组选择在这种情况下,您可能会使用简单的if 级联和“硬编码”语句。但鉴于您的问题的性质,为什么要避免使用动态 SQL?
    • @synapse 如果您需要动态 SQL - 并且如果您需要动态列或表名 - 那么您几乎被字符串连接所困扰。我建议不要使用||quote_ident(...),而是使用format%I。有关示例,请参见 stackoverflow.com/a/12995424/398670
    【解决方案3】:

    如果您想使用函数来处理查询的结果集,最好的选择是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 一个游标并将其传递给函数。您不能只使用普通的函数调用语法。它的效率也很低,需要在函数中使用LOOPing。我认为这不会有太大帮助。

    实现该功能时,使用EXECUTE format(...) USING ... 保持动态SQL 不会太可怕。见this earlier answer

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-03-11
      • 1970-01-01
      • 1970-01-01
      • 2010-09-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多