【问题标题】:How to use row values as columns in a SELECT statement in PostgreSQL?如何在 PostgreSQL 的 SELECT 语句中使用行值作为列?
【发布时间】:2018-10-02 09:34:32
【问题描述】:

我的 PostgreSQL 9.2.8 数据库中有一个包含这三个表的表结构:

我试图弄清楚如何选择orders 行并在同一行上添加来自order_pointspoints 的一些列。

您可以将points 表想象成一个人可以购买的所有物品的列表,abbrev 内部知道它,它的成本是amount

order_points 表是购买的每件商品,因此 points.id == order_points.points_idamount 类似于说他们购买了 5 个糖果棒。它通过order_points.order_id == orders.id链接到一个订单

当我选择一个订单时,我希望每个存在的abbrev 以及order_points 表中的amount 都有一个列。

所以如果points 有这个:

 id | name      | abbrev | amount
 --------------------------------
  1 | Snickers  | sn     | 1.34
  2 | Milky Way | mw     | 1.73
  3 | Coffee    | cf     | 10.12

order_points 有这个:

 id | order_id | points_id | amount
 ----------------------------------
  1 |        1 |         1 |     10
  2 |        1 |         3 |      1

然后当我得到我的行时,我想要订单中的所有列,加上三个额外的列。我不想列出上面订单中显示的每一列,但基本上假设我只想要其中的 4 个,再加上所有 points 的东西,我最终会以一行输出:

 id | created    | due        | name  | sn | mw | cf
 ------------------------------------------------
  1 | 2018-04-21 | 2018-05-01 | Fooey | 10 |  0 |  1

我不知道如何从表查找中动态添加具有名称(abbrev)的列。

【问题讨论】:

标签: sql postgresql postgresql-9.2


【解决方案1】:

您不能动态添加名称未知的列。查询中的列名必须是已知的。但是您可以使用一个查询的输出以编程方式定义输出查询中的字段名称。

【讨论】:

  • 你能再详细说明一下吗?你是说我可以通过代码做到这一点,而不仅仅是 SQL?
  • 完全正确。阅读有关 crostab tablefunc module 的信息。最好的方法是使用crosstab(text source_sql, text category_sql),但是你必须知道字段名称 交叉表函数被声明为返回setof记录,所以输出列的实际名称和类型必须在调用 SELECT 语句,例如: SELECT * FROM crosstab('...', '...') AS ct(row_name text, extra text, cat1 text, cat2 text, cat3 text, cat4 text);
  • 我根本不知道如何使用它来解决这个问题。
  • 我只写怎么做,如果你想要一些代码,请提供更多细节......
  • 我不知道如何提供比问题中更多的细节。
【解决方案2】:

正如 Adam Silenko 所说,您不能在运行时添加列。您可以做的最好的事情是使用 2 个查询。一个函数将创建一个包含您需要的列的临时表,另一个函数用于查询表并获取结果。这是解释here

创建临时表的函数:

CREATE OR REPLACE FUNCTION get_order(orderId BIGINT)
  RETURNS VOID AS
  $$
  DECLARE column_names varchar[];
  DECLARE column_values float[];
  DECLARE final_select TEXT := 'SELECT id, name points_columns FROM orders where id=' || orderId;
  DECLARE create_table_statement TEXT := 'CREATE TEMP TABLE temp_result_table ON COMMIT DROP AS select_statement';
  DECLARE columns_values_concatenated TEXT := '';
  BEGIN
    SELECT array_agg(abbrev),  array_agg(CASE WHEN amount IS NULL THEN 0 ELSE amount END)
    into column_names, column_values FROM
     (SELECT abbrev, order_points.amount as amount FROM points LEFT JOIN order_points ON points.id = order_points.points_id and order_id = orderId
     ORDER BY points.id) points_amount;

     FOR i IN 1 .. array_upper(column_names, 1)
       LOOP
       columns_values_concatenated := columns_values_concatenated || ', ' || column_values[i] || ' as ' || column_names[i];
     end loop;
    final_select := replace(final_select, 'points_columns',columns_values_concatenated);
    create_table_statement:= replace(create_table_statement, 'select_statement', final_select);
    EXECUTE create_table_statement;
end;

$$ LANGUAGE Plpgsql;

我们使用 2 个数组 column_namescolumn_values 分别存储所选订单的名称(“sn”、“mw”、“cf)和这些名称的值。

我们使用这 2 个数组来生成 select 语句(在当前代码中,我只从 orders 表中获取 id 和 name,但您可以轻松更改它们)。我们将 select 语句存储到 final_select 变量中。最后我们将生成的 select 语句添加到create_table_statement 中,并创建并填充临时表。

现在,正如上面链接中所解释的,因为我们需要 2 个查询来访问数据,所以我们必须在一个事务中执行这两个查询(如果我们多次调用该函数,以避免名称冲突) .

BEGIN;
SELECT * FROM get_order(1);
SELECT * FROM temp_result_table;
COMMIT; --The temporary table will be dropped on commit

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-09-17
    • 1970-01-01
    • 2016-10-15
    • 1970-01-01
    • 1970-01-01
    • 2014-02-11
    • 1970-01-01
    相关资源
    最近更新 更多