Postgres:这个查询的语法有什么问题?

时间:2018-01-29 05:30:21

标签: sql postgresql

我试图写一个相当简单的PSQL查询来检索一些数据(我意识到它现在不是最有效的查询):

SELECT c.name AS article, c.id AS article_id, t.name AS template, t.id AS template_id, brand_names, COUNT(p.component_id)
FROM publications p
INNER JOIN components c 
(SELECT string_agg(b.name, ', ') AS brand_names 
 FROM brands b
 INNER JOIN brands_components 
 ON b.id = brands_components.brand_id
 WHERE brands_components.component_id = c.id
) brand_query
ON c.id = p.component_id 
INNER JOIN brands_components bc
ON c.id = bc.component_id
AND bc.brand_id IN (16, 23, 24, 35, 37)
INNER JOIN components_templates ct
ON c.id = ct.component_id
INNER JOIN templates t
ON t.id = ct.template_id

这在第4行给出了语法错误。缺少什么?如果我单独运行子查询,它可以正常工作:

  

语法错误在或附近" SELECT"第4行:( SELECT string_agg(b.name,',')AS brand_names ^:SELECT c.name AS article,c.id AS article_id,t.name AS template,t.id AS template_id, brand_nam

子查询旨在检索每个组件的所有品牌名称,并将其显示在一行而不是多行中。他们的联接表是brands_components

可用的小提琴here,所需的结果应该是:

article         article_id   template       template_id   count   brands
--------------------------------------------------------------------------------------------------------------
component one | 1          | template one | 1           | 4     | brand one, brand two, brand three, brand four

3 个答案:

答案 0 :(得分:4)

通过横向连接可以解决您的直接问题:

SELECT c.name AS article, c.id AS article_id, t.name AS template, t.id AS template_id, brand_names, COUNT(p.component_id)
FROM publications p
  JOIN components c ON c.id = p.component_id 
  JOIN brands_components bc ON c.id = bc.component_id AND bc.brand_id IN (1, 2, 3, 4)
  JOIN LATERAL (
    SELECT b.id, string_agg(b.name, ', ') AS brand_names 
    FROM brands b
      JOIN brands_components ON b.id = brands_components.brand_id
    WHERE brands_components.component_id = c.id
    GROUP BY b.id
  ) brand_query ON brand_query.id = bc.brand_id
  JOIN components_templates ct ON c.id = ct.component_id
  JOIN templates t ON t.id = ct.template_id
GROUP BY 1,2,3,4

由于group by不包含brand_names列,因此仍然无法运行。 Postgres不知道brand_names已经是聚合。

但是,如果将聚合移动到外部查询,则不需要派生表:

SELECT c.name AS article, 
       c.id AS article_id, 
       t.name AS template, 
       t.id AS template_id, 
       string_agg(b.name, ',') as brand_names, 
       COUNT(p.component_id)
FROM publications p
  JOIN components c ON c.id = p.component_id 
  JOIN brands_components bc ON c.id = bc.component_id AND bc.brand_id IN (1, 2, 3, 4)
  JOIN brands b on b.id = bc.brand_id
  JOIN components_templates ct ON c.id = ct.component_id
  JOIN templates t ON t.id = ct.template_id
GROUP BY c.name, c.id, t.name, t.id;

答案 1 :(得分:3)

尝试此查询:

SELECT c.name AS article, c.id AS article_id, t.name AS template, t.id AS template_id,MAX(brand_names) AS brand_names, COUNT(p.component_id) AS Counts
FROM publications p
INNER JOIN components c 
ON c.id = p.component_id 
INNER JOIN brands_components bc
ON c.id = bc.component_id
AND bc.brand_id IN (1, 2, 3, 4)
INNER JOIN components_templates ct
ON c.id = ct.component_id
INNER JOIN templates t
ON t.id = ct.template_id
INNER JOIN (SELECT string_agg(b.name, ', ') AS brand_names 
 FROM brands b
 INNER JOIN brands_components bcc
 ON b.id = bcc.brand_id
 INNER JOIN components c ON bcc.component_id = c.id
) brand_query ON brand_names IS NOT NULL
Group by c.name,c.id,t.name,t.id

答案 2 :(得分:1)

我用working获得了一个函数:

CREATE FUNCTION brands(int) RETURNS varchar AS $$
  SELECT string_agg(b.name, ', ') AS brand_names 
  FROM brands b
  INNER JOIN brands_components 
  ON b.id = brands_components.brand_id
  WHERE brands_components.component_id = $1
$$ LANGUAGE SQL;

SELECT c.name, c.id, t.name AS template_name, t.id AS template_id, brands(c.id), COUNT(p.component_id)
FROM publications p
INNER JOIN components c 
ON c.id = p.component_id 
INNER JOIN brands_components bc
ON c.id = bc.component_id
AND bc.brand_id IN (1, 2, 3, 4)
INNER JOIN components_templates ct
ON c.id = ct.component_id
INNER JOIN templates t
ON t.id = ct.template_id
GROUP BY 1, 2, 3, 4

不确定哪个更可取,但它可能是DineshDB的。