【问题标题】:How can you expand a "condensed" PostgreSQL row into separate columns?如何将“压缩”的 PostgreSQL 行扩展为单独的列?
【发布时间】:2014-06-27 04:34:55
【问题描述】:

我有一个返回表格的函数。

如果你运行SELECT * FROM some_function(12345),结果是:

object_id | name
----------------
    12345 | "B"

如果你运行SELECT some_function(12345),结果是:

some_function
-------------
(12345,"B")

问题是我想要原始表单(以便我可以访问单个列值),但 some_function() 的参数来自表中的列。我可以执行 SELECT some_function(thing_id) FROM things 但这会返回:

some_function
-------------
(12345,"B")
(12346,"C")
(12347,"D")

而我想要返回的是:

object_id | name
----------------
    12345 | "B"
    12346 | "C"
    12347 | "D"

那么如何“取消嵌套”或“扩展”这样一个压缩的行呢?

【问题讨论】:

    标签: sql postgresql


    【解决方案1】:

    9.3及以上:横向查询

    在 PostgreSQL 9.3 或更新版本中使用隐式横向查询:

    SELECT f.* FROM things t, some_function(t.thing_id) f;
    

    对于所有新查询,首选此公式。以上是标准制定

    它也适用于 RETURNS TABLERETURNS SETOF RECORD 的函数以及带有 RETURNS RECORD 的输出参数的函数。

    它是以下的简写:

    SELECT f.*
    FROM things t
    CROSS JOIN LATERAL some_function(t.thing_id) f;
    

    9.3 之前:通配符扩展(小心)

    以前的版本,导致some_function 的多重评估,如果some_function 返回一个集合,工作,不要使用这个

    SELECT (some_function(thing_id)).* FROM things;
    

    以前的版本,避免使用第二层间接对some_function 进行多重评估。仅当您必须支持相当旧的 PostgreSQL 版本时才使用它。

    SELECT (f).*
    FROM (
      SELECT some_function(thing_id) f
      FROM things
    ) sub(f);
    

    演示:

    设置:

    CREATE FUNCTION some_function(i IN integer, x OUT integer, y OUT text, z OUT text) RETURNS record LANGUAGE plpgsql AS $$
    BEGIN
      RAISE NOTICE 'evaluated with %',i;
      x := i;
      y := i::text;
      z := 'dummy';
      RETURN;
    END;
    $$;
    
    create table things(thing_id integer);
    insert into things(thing_id) values (1),(2),(3);
    

    试运行:

    demo=>     SELECT f.* FROM things t, some_function(t.thing_id) f;
    NOTICE:  evaluated with 1
    NOTICE:  evaluated with 2
    NOTICE:  evaluated with 3
     x | y |   z   
    ---+---+-------
     1 | 1 | dummy
     2 | 2 | dummy
     3 | 3 | dummy
    (3 rows)
    
    demo=>     SELECT (some_function(thing_id)).* FROM things;
    NOTICE:  evaluated with 1
    NOTICE:  evaluated with 1
    NOTICE:  evaluated with 1
    NOTICE:  evaluated with 2
    NOTICE:  evaluated with 2
    NOTICE:  evaluated with 2
    NOTICE:  evaluated with 3
    NOTICE:  evaluated with 3
    NOTICE:  evaluated with 3
     x | y |   z   
    ---+---+-------
     1 | 1 | dummy
     2 | 2 | dummy
     3 | 3 | dummy
    (3 rows)
    
    demo=>  SELECT (f).*
        FROM (
          SELECT some_function(thing_id) f
          FROM things
        ) sub(f);
    NOTICE:  evaluated with 1
    NOTICE:  evaluated with 2
    NOTICE:  evaluated with 3
     x | y |   z   
    ---+---+-------
     1 | 1 | dummy
     2 | 2 | dummy
     3 | 3 | dummy
    (3 rows)
    

    【讨论】:

    • f.* 来自哪里?我不知道横向连接可能是隐式的。我认为这不好,甚至不应该被提及 :) 只是为了确定:9.3 不会对 select (f()).* 进行多重评估?
    • @ClodoaldoNeto 修复了缺少的别名。至于多个 eval ... 9.3 does 对由行返回 f(f()).* 中返回的每个 col 乘以评估 f。横向查询不会。
    • 至于 LATERAL 是隐含的 - 它符合规范。 LATERAL 关键字只是那里的噪音——这似乎是合理的,因为函数的参数无法来自其他任何地方。我想将其显示为内联文档是值得的,但是当人们看到没有它的类似查询时会感到困惑。
    • 值得强调,因为我已经被它咬了。警告:调用select (myfunc('param')).* 将多次执行该函数(对于@CraigRinger 上述返回的每一列)
    • @SirajMansour 您实际上可以使用另一层子查询来解决这个问题:您让第一层评估函数并返回行值结果,然后第二层扩展它。原因是* 进行了非常简单的类似宏的扩展,其中(expr).* 扩展为(expr).col1, (expr).col2, ...。但是由于横向查询不会受到这个问题的影响,所以现在不必担心它,完全避免(expr).*
    【解决方案2】:
    SELECT * FROM (SELECT some_function(thing_id) FROM things) x;
    

    子选择SELECT some_function(thing_id) FROM things 为找到的每条记录返回一行。外部选择将行“解压缩”为单独的列。

    【讨论】:

    • 请在您的答案中添加一些解释,以便 OP 和其他用户可以更好地理解
    猜你喜欢
    • 1970-01-01
    • 2018-11-19
    • 1970-01-01
    • 1970-01-01
    • 2017-07-06
    • 1970-01-01
    • 1970-01-01
    • 2019-12-03
    • 1970-01-01
    相关资源
    最近更新 更多