多语言关系中所有3个表的SELECT语句

时间:2018-01-07 21:39:43

标签: postgresql postgresql-performance

我在编写SELECT查询时遇到问题,该查询包含多对多关系中的所有3个表。我有以下表格:

                                                        Table "public.companies"
     Column     |          Type          |                       Modifiers                        | Storage  | Stats target | Description 
----------------+------------------------+--------------------------------------------------------+----------+--------------+-------------
 id             | integer                | not null default nextval('companies_id_seq'::regclass) | plain    |              | 
 name           | character varying(48)  | not null                                               | extended |              | 
 description    | character varying(512) |                                                        | extended |              | 
 tagline        | character varying(64)  |                                                        | extended |              | 
 featured_image | integer                |                                                        | plain    |              | 
Indexes:
    "companies_pkey" PRIMARY KEY, btree (id)
Referenced by:
    TABLE "company_category_associations" CONSTRAINT "company_category_associations_company_id_foreign" FOREIGN KEY (company_id) REFERENCES companies(id) ON DELETE CASCADE


                            Table "public.company_category_associations"
   Column    |  Type   |                                 Modifiers                                  
-------------+---------+----------------------------------------------------------------------------
 id          | integer | not null default nextval('company_category_associations_id_seq'::regclass)
 company_id  | integer | not null
 category_id | integer | not null
Indexes:
    "company_category_associations_pkey" PRIMARY KEY, btree (id)
Foreign-key constraints:
    "company_category_associations_category_id_foreign" FOREIGN KEY (category_id) REFERENCES company_categories(id) ON DELETE RESTRICT
    "company_category_associations_company_id_foreign" FOREIGN KEY (company_id) REFERENCES companies(id) ON DELETE CASCADE


                                  Table "public.company_categories"
   Column    |         Type          |                            Modifiers                            
-------------+-----------------------+-----------------------------------------------------------------
 id          | integer               | not null default nextval('company_categories_id_seq'::regclass)
 name        | character varying(32) | not null
 description | character varying(96) | 
Indexes:
    "company_categories_pkey" PRIMARY KEY, btree (id)
Referenced by:
    TABLE "company_category_associations" CONSTRAINT "company_category_associations_category_id_foreign" FOREIGN KEY (category_id) REFERENCES company_categories(id) ON DELETE RESTRICT

我的companies表格大约有10万行,公司最多可以关联10个类别。当然,我一次不会选择200多家公司。

我设法通过以下查询获得结果:

select 
  c.id as companyid, 
  c.name as companyname, 
  cat.id as categoryid, 
  cat.name as categoryname 
from company_categories cat
  left join company_category_associations catassoc on catassoc.category_id = cat.id
  left join companies c on catassoc.company_id = c.id where c.id is not null;

这个问题来自我需要以JSON格式呈现数据的事实,我希望它看起来像这样:

{
  "companies": [
    {
      "name": "...",
      "description": "...",
      "categories": [
        {
          "id": 12,
          "name": "Technology"
        },
        {
          "id": 14,
          "name": "Computers"
        },
      ]
    },
    /* ... */
  ]
}

基本上我想尽可能少地查询这些数据。

  1. 如何编写SELECT查询以满足我的需求?
  2. 数据库结构是否存在问题,因为它是我的图表?
  3. 谢谢!

    P.S。我正在使用PostgreSQL 9.6.6

2 个答案:

答案 0 :(得分:1)

可以直接从postgresql中获取json。

首先定义要输出的复合类型(json对象)。这是为了获取字段的名称,否则它们将被命名为f1,f2,...

create type cat as (id integer, name varchar);
create type comp as (id integer, name varchar, categories cat[]);

然后选择包含嵌套的猫数组作为json的comps数组

select to_json(array(
  select (
    c.id,
    c.name,
    array(
      select (cc.id, cc.name)::cat
      from company_categories cc 
      join company_category_associations cca on (cca.category_id=cc.id and cca.company_id=c.id)
    ))::comp
  from companies c
)) as companies

dbfiddle

答案 1 :(得分:0)

您可以嵌套两个JSON聚合到达此位置。

第一级为每个公司创建一个JSON值,并将类别存储为数组:

select to_jsonb(c) || jsonb_build_object('categories', jsonb_agg(cc)) comp_json
from companies c
  join company_category_associations cca on cca.company_id = c.id
  join company_categories cc on cc.id = cca.category_id
group by c.id;

每个公司返回一行。现在,我们需要将这些行聚合为一个JSON值:

select jsonb_build_object('companies', jsonb_agg(comp_json))
from (
  select to_jsonb(c) || jsonb_build_object('{categories}', jsonb_agg(cc)) comp_json
  from companies c
    join company_category_associations cca on cca.company_id = c.id
    join company_categories cc on cc.id = cca.category_id
  group by c.id  
) t;

在线示例:http://rextester.com/TRCR26633