如何在 PostgreSQL 的 SELECT 语句中将行值用作列?
How to use row values as columns in a SELECT statement in PostgreSQL?
我的 PostgreSQL 9.2.8 数据库中有一个包含这三个 table 的 table 结构:
我正在尝试弄清楚如何 select 一个 orders
行并在同一行上添加 order_points
和 points
中的一些列。
您可以将 points
table 想象成一个人可以购买的所有物品的列表,abbrev
在内部知道它并且花费 amount
.
order_points
table 是购买的每件商品,因此 points.id == order_points.points_id
和 amount
类似于说他们购买了 5 块糖果。它通过 order_points.order_id == orders.id
链接到订单
当我 select 一个订单时,我想为每个存在的 abbrev
以及 order_points
table 中的 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
我不知道如何从 table 查找中动态添加具有名称(abbrev
)的列。
您不能动态添加名称未知的列。查询中列的名称必须是已知的。但是您可以使用一个查询的输出以编程方式定义输出查询中的字段名称。
正如 Adam Silenko 所说,您不能在运行时添加列。您可以做的最好的事情是使用 2 个查询。一个函数将创建一个包含您需要的列的临时 table,另一个用于查询 table 并获取结果。这是解释 here.
将创建临时文件的函数 table:
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_names
和 column_values
来存储名称("sn"、"mw"、“cf”和这些名称的值 select分别下单。
我们使用这 2 个数组生成 select 语句(在当前代码中,我只从订单 table 中获取 ID 和名称,但您可以轻松更改它)。我们将 select 语句存储到 final_select
变量中。最后我们将生成的 select 语句添加到 create_table_statement
并创建并填充临时 table.
现在,如上面 link 中所述,因为我们需要 2 个查询来访问数据,所以我们必须在单个事务中执行两个查询(为了避免名称冲突,如果我们多次调用该函数)。
BEGIN;
SELECT * FROM get_order(1);
SELECT * FROM temp_result_table;
COMMIT; --The temporary table will be dropped on commit
我的 PostgreSQL 9.2.8 数据库中有一个包含这三个 table 的 table 结构:
我正在尝试弄清楚如何 select 一个 orders
行并在同一行上添加 order_points
和 points
中的一些列。
您可以将 points
table 想象成一个人可以购买的所有物品的列表,abbrev
在内部知道它并且花费 amount
.
order_points
table 是购买的每件商品,因此 points.id == order_points.points_id
和 amount
类似于说他们购买了 5 块糖果。它通过 order_points.order_id == orders.id
当我 select 一个订单时,我想为每个存在的 abbrev
以及 order_points
table 中的 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
我不知道如何从 table 查找中动态添加具有名称(abbrev
)的列。
您不能动态添加名称未知的列。查询中列的名称必须是已知的。但是您可以使用一个查询的输出以编程方式定义输出查询中的字段名称。
正如 Adam Silenko 所说,您不能在运行时添加列。您可以做的最好的事情是使用 2 个查询。一个函数将创建一个包含您需要的列的临时 table,另一个用于查询 table 并获取结果。这是解释 here.
将创建临时文件的函数 table:
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_names
和 column_values
来存储名称("sn"、"mw"、“cf”和这些名称的值 select分别下单。
我们使用这 2 个数组生成 select 语句(在当前代码中,我只从订单 table 中获取 ID 和名称,但您可以轻松更改它)。我们将 select 语句存储到 final_select
变量中。最后我们将生成的 select 语句添加到 create_table_statement
并创建并填充临时 table.
现在,如上面 link 中所述,因为我们需要 2 个查询来访问数据,所以我们必须在单个事务中执行两个查询(为了避免名称冲突,如果我们多次调用该函数)。
BEGIN;
SELECT * FROM get_order(1);
SELECT * FROM temp_result_table;
COMMIT; --The temporary table will be dropped on commit