从返回复合类型的 PostgreSQL 函数中选择

发布于 2024-10-04 17:17:15 字数 821 浏览 6 评论 0原文

如何在 SELECT 中包含返回复合类型的函数?
我有复合类型:

CREATE TYPE public.dm_nameid AS (
  id   public.dm_int,
  name public.dm_str
);

此外,我有一个返回此类型 fn_GetLinkedProject(integer) 的函数。 我需要做这样的事情:

SELECT 
    p.id, p.data, p.name, 
    pl.id linked_id, pl.name linked_name
FROM tb_projects p
   left join "fn_GetLinkedProject"(p.id) pl

我该怎么做?

我已阅读这篇文章。

我不想要以下方法:

SELECT
 p.id, p.data, p.name, 
    (select pl1.id from "fn_GetLinkedProject"(p.id) pl1 ) linked_id,
    (select pl2.name from "fn_GetLinkedProject"(p.id) pl2 ) linked_name
FROM tb_projects p

How to include a function that returns a composite type in a SELECT?
I have composite type:

CREATE TYPE public.dm_nameid AS (
  id   public.dm_int,
  name public.dm_str
);

Also, I have a function that returns this type fn_GetLinkedProject(integer).
And I need to make something like this:

SELECT 
    p.id, p.data, p.name, 
    pl.id linked_id, pl.name linked_name
FROM tb_projects p
   left join "fn_GetLinkedProject"(p.id) pl

How can I do this?

I have read this article.

I don't want following method:

SELECT
 p.id, p.data, p.name, 
    (select pl1.id from "fn_GetLinkedProject"(p.id) pl1 ) linked_id,
    (select pl2.name from "fn_GetLinkedProject"(p.id) pl2 ) linked_name
FROM tb_projects p

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

匿名的好友 2024-10-11 17:17:15

Postgres 9.3 或更高版本

使用 LATERAL 加入!

SELECT p.id, p.name, p.data, f.*
FROM   tb_projects p
LEFT   JOIN LATERAL fn_getlinkedproject(p.id) f(linked_id, lined_name) ON true;

结果:

 id |  data  |  name  | linked_id | linked_name
----+--------+--------+-----------+-------------
  1 | data_1 | name_1 |         2 | name_2
  2 | data_2 | name_2 |         3 | name_3
  3 | data_3 | name_3 |         1 | name_1

请参阅:

Postgres 9.2 或更旧的

Inferior 有几个原因。附加列别名并不那么简单。而是重命名其他冲突的名称:

SELECT p.id AS p_id, p.data AS p_data, p.name AS p_name
     , (fn_getlinkedproject(p.id)).*
FROM   tb_projects p;

结果:

 p_id | p_data | p_name | id |  name
------+--------+--------+----+--------
    1 | data_1 | name_1 |  2 | name_2
    2 | data_2 | name_2 |  3 | name_3
    3 | data_3 | name_3 |  1 | name_1

重命名结果列,您必须:

SELECT p.id, p.data, p.name
    , (fn_getlinkedproject(p.id)).id   AS linked_id
    , (fn_getlinkedproject(p.id)).name AS linked_name
FROM   tb_projects p;

两个旧解决方案都解析为重复调用函数的相同(差!)查询计划

为了避免这种情况,请使用子查询

SELECT p.id, p.data, p.name
    , (p.x).id   AS linked_id
    , (p.x).name AS linked_name
FROM  (SELECT *, fn_getlinkedproject(id) AS x FROM tb_projects) p;

注意必要的括号的位置。
阅读有关复合类型的手册

演示

CREATE TYPE dm_nameid AS (
  id   int
, name text  -- types simplified for demo
);

CREATE TABLE tb_projects(
  id   int
, data text
, name text
);

INSERT INTO tb_projects VALUES
  (1, 'data_1', 'name_1')
, (2, 'data_2', 'name_2')
, (3, 'data_3', 'name_3')
;

CREATE function fn_getlinkedproject(integer)  -- avoiding CaMeL-case
  RETURNS dm_nameid
  LANGUAGE sql AS
'SELECT id, name FROM tb_projects WHERE id = ($1 % 3) + 1';

fiddle

Postgres 9.3 or newer

Use a LATERAL join!

SELECT p.id, p.name, p.data, f.*
FROM   tb_projects p
LEFT   JOIN LATERAL fn_getlinkedproject(p.id) f(linked_id, lined_name) ON true;

Result:

 id |  data  |  name  | linked_id | linked_name
----+--------+--------+-----------+-------------
  1 | data_1 | name_1 |         2 | name_2
  2 | data_2 | name_2 |         3 | name_3
  3 | data_3 | name_3 |         1 | name_1

See:

Postgres 9.2 or older

Inferior for several reasons. Attaching column aliases is not as simple. Rather rename other conflicting names:

SELECT p.id AS p_id, p.data AS p_data, p.name AS p_name
     , (fn_getlinkedproject(p.id)).*
FROM   tb_projects p;

Result:

 p_id | p_data | p_name | id |  name
------+--------+--------+----+--------
    1 | data_1 | name_1 |  2 | name_2
    2 | data_2 | name_2 |  3 | name_3
    3 | data_3 | name_3 |  1 | name_1

To rename the result columns, you have to:

SELECT p.id, p.data, p.name
    , (fn_getlinkedproject(p.id)).id   AS linked_id
    , (fn_getlinkedproject(p.id)).name AS linked_name
FROM   tb_projects p;

Both old solutions resolve to the same (poor!) query plan of calling the function repeatedly.

To avoid that, use a subquery:

SELECT p.id, p.data, p.name
    , (p.x).id   AS linked_id
    , (p.x).name AS linked_name
FROM  (SELECT *, fn_getlinkedproject(id) AS x FROM tb_projects) p;

Note the placement of essential parentheses.
Read the manual about composite types.

Demo

CREATE TYPE dm_nameid AS (
  id   int
, name text  -- types simplified for demo
);

CREATE TABLE tb_projects(
  id   int
, data text
, name text
);

INSERT INTO tb_projects VALUES
  (1, 'data_1', 'name_1')
, (2, 'data_2', 'name_2')
, (3, 'data_3', 'name_3')
;

CREATE function fn_getlinkedproject(integer)  -- avoiding CaMeL-case
  RETURNS dm_nameid
  LANGUAGE sql AS
'SELECT id, name FROM tb_projects WHERE id = ($1 % 3) + 1';

fiddle

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文