使PostgreSQL查询更干燥

时间:2018-01-18 14:33:04

标签: sql postgresql

我有以下sql查询:

Create table that connects Candidate with Exam
** CandidateExam **
(FK) candidate_id: int
(FK) exam_id: int

查询工作非常好,但有很多重复。我在想怎么能让它干得更干?这是您可以用于实验的sql小提琴:

http://sqlfiddle.com/#!17/746c9/5

1 个答案:

答案 0 :(得分:2)

使其更具可读性的一种方法是提取scores#>>'{medic,categories,food_insecurity}

SELECT
  date(survey_results.created_at),
  json_build_object(
    'high', COUNT(*) FILTER (WHERE (sub.food in('high'))),
    'medium', COUNT(*) FILTER (WHERE (sub.food in('medium'))),
    'low', COUNT(*) FILTER (WHERE (sub.food in('low')))
  ) as food_insecurity,
  json_build_object(
    'high', COUNT(*) FILTER (WHERE (sub.motivation in('high'))),
    'medium', COUNT(*) FILTER (WHERE (sub.motivation in('medium'))),
    'low', COUNT(*) FILTER (WHERE (sub.motivation in('low')))
  ) as motivation                               
  FROM survey_results
 JOIN LATERAL (SELECT scores#>>'{medic,categories,motivation}',
                      scores#>>'{medic,categories,food_insecurity}'
              ) sub(motivation, food)   ON true
 GROUP BY date(survey_results.created_at);

<强> Rextester Demo

另一个步骤可能是tablefunc或某种旋转,以避免将high“中\低”声称为3次。

修改

使用json_object_agg的版本:

WITH cte AS (
    SELECT *
    FROM survey_results
    JOIN LATERAL (SELECT scores#>>'{medic,categories,motivation}',
                         scores#>>'{medic,categories,food_insecurity}'
                 ) s1(motivation, food) ON true
)
SELECT  cte.created_at::DATE
       ,MIN(s3.motivation)::json AS motivation
       ,MIN(s3.food_insecurity)::json AS food_insecurity
FROM cte
LEFT JOIN LATERAL (SELECT JSON_OBJECT_AGG(lvl, cnt_motivation)::text
                         ,JSON_OBJECT_AGG(lvl, cnt_food)::text
                   FROM(SELECT sub2.lvl
                            ,COUNT(*) FILTER (WHERE (sub2.lvl = sub.motivation))
                            ,COUNT(*) FILTER (WHERE (sub2.lvl = sub.food))
                        FROM cte sub
                        CROSS JOIN (VALUES ('high'),
                                         ('medium'), ('low')) AS sub2(lvl)
                        GROUP BY sub2.lvl
                       ) s2(lvl,cnt_motivation, cnt_food)
                  ) s3(motivation,food_insecurity)
          ON true
GROUP BY cte.created_at::DATE;

<强> Rextester Demo 2