从PostgreSQL中的联接表中过滤数据

时间:2018-12-03 18:53:47

标签: sql postgresql

在我的数据库中,我具有以下架构:

CREATE TABLE survey_results (
    id integer NOT NULL
);

CREATE TABLE slide_results (
    id integer NOT NULL,
    survey_result_id integer,
    tags character varying[] DEFAULT '{}'::character varying[],
    content character varying,
    created_at timestamp with time zone NOT NULL
);

INSERT INTO survey_results (id)
  VALUES (1);

INSERT INTO slide_results (id, survey_result_id, tags, content, created_at)
  VALUES (1, 1, '{food}', 'Food slide', now());

INSERT INTO slide_results (id, survey_result_id, tags, content, created_at)
  VALUES (2, 1, '{motivation}', 'Motivation slide', now());

现在,我想要一个SQL查询,该查询将返回调查结果ID和具有指定标签的幻灯片结果的内容。我写了这样的东西:

select distinct on(sr.id)
  sr.id,
  slr.content AS food,
  slr2.content AS motivation
  from survey_results sr

  LEFT JOIN slide_results slr ON slr.survey_result_id = sr.id AND slr.id IN (
    SELECT id as id
    FROM slide_results
    WHERE 'food' = ANY(tags)
    ORDER BY created_at desc
  )

  LEFT JOIN slide_results slr2 ON slr2.survey_result_id = sr.id AND slr2.id IN (
    SELECT id as id
    FROM slide_results
    WHERE 'motivation' = ANY(tags)
    ORDER BY created_at desc
  )
  group by slr.content, slr2.content, sr.id

返回:

| id  | food       | motivation       |
| --- | ---------- | ---------------- |
| 1   | Food slide | Motivation slide |

此查询工作正常,但我想知道是否有更好的方法?

编辑:

我忘了添加链接做db-fiddle:

https://www.db-fiddle.com/f/gP761psywgmovfdTT7DjP4/0

2 个答案:

答案 0 :(得分:1)

我会这样写查询:

SELECT DISTINCT ON (sr.id)
       sr.id,
       slr.content AS food,
       slr2.content AS motivation
FROM survey_results AS sr
   LEFT JOIN (SELECT survey_result_id, content, created_at
              FROM slide_results
              WHERE '{food}' <@ tags) AS slr
      ON slr.survey_result_id = sr.id
   LEFT JOIN (SELECT survey_result_id, content, created_at
              FROM slide_results
              WHERE '{motivation}' <@ tags) AS slr2
      ON slr2.survey_result_id = sr.id
ORDER BY sr.id, slr.created_at DESC, slr2.created_at DESC;

ORDER BY必须在外部查询中才能生效。

使用<@而不是=ANY可以在slide_results.tags上使用GIN索引。

FROM列表中使用子选择可以避免不必要的联接和效率低的IN子查询。

答案 1 :(得分:1)

我不能保证这会比您拥有的更好,但是它似乎更具可扩展性。在没有看到完整的数据集和所需结果的情况下,很难知道这是否会适得其反:

select
  sl.survey_result_id,
  array_to_string (array_agg (distinct sl.content) filter
      (where 'food' = any (sl.tags)), ',') as food,
  array_to_string (array_agg (distinct sl.content) filter
      (where 'motivation' = any (sl.tags)), ',') as motivation
from
  survey_results s
  join slide_results sl on s.id = sl.survey_result_id
group by survey_result_id