sql - 比多个 SELECT 语句更好的方法?

标签 sql postgresql select common-table-expression postgresql-performance

我正在创建一个显示饼图的网络应用程序。为了在单个 HTTP 请求中从 PostgreSQL 9.3 数据库获取图表的所有数据,我组合了多个 SELECT带有 UNION ALL 的语句— 这是一部分:

SELECT 'spf' as type, COUNT(*)
    FROM (SELECT cai.id
          FROM common_activityinstance cai
          JOIN common_activityinstance_settings cais ON cai.id = cais.activityinstance_id
          JOIN common_activitysetting cas ON cas.id = cais.id
          JOIN quizzes_quiz q ON q.id = cai.activity_id
          WHERE cai.end_time::date = '2015-09-12'
          AND q.name != 'Exit Ticket Quiz'
          AND cai.activity_type = 'QZ'
          AND (cas.key = 'disable_student_nav' AND cas.value = 'True'
            OR cas.key = 'pacing' AND cas.value = 'student')
          GROUP BY cai.id
          HAVING COUNT(cai.id) = 2) sub
UNION ALL
SELECT 'spn' as type, COUNT(*)
    FROM common_activityinstance cai
    JOIN common_activityinstance_settings cais ON cai.id = cais.activityinstance_id
    JOIN common_activitysetting cas ON cas.id = cais.id
    WHERE cai.end_time::date = '2015-09-12'
    AND cai.activity_type = 'QZ'
    AND cas.key = 'disable_student_nav'
    AND cas.value = 'False'
UNION ALL
SELECT 'tp' as type, COUNT(*)
    FROM (SELECT cai.id 
          FROM common_activityinstance cai
          JOIN common_activityinstance_settings cais ON cai.id = cais.activityinstance_id
          JOIN common_activitysetting cas ON cas.id = cais.id
          WHERE cai.end_time::date = '2015-09-12'
          AND cai.activity_type = 'QZ'
          AND cas.key = 'pacing' AND cas.value = 'teacher') sub;

这会产生一个很好的小响应以发送回客户端:

 type |  count 
------+---------
 spf  |  100153
 spn  |   96402
 tp   |   84211

我想知道是否可以提高我的查询效率。每个 SELECT 语句主要使用相同的 JOIN 操作。有没有办法不为每个新的 SELECT 重复 JOIN?
我实际上更喜欢单行 3 列。

或者,总的来说,是否有一些与我正在做的完全不同但更好的方法?

最佳答案

您可以将大部分成本捆绑在 CTE 中的单个主查询中并多次重复使用结果。
这将返回以每个 type ( as requested in the comment) 命名的单行三列:

WITH cte AS (
   SELECT cai.id, cai.activity_id, cas.key, cas.value
   FROM   common_activityinstance cai
   JOIN   common_activityinstance_settings s ON s.activityinstance_id = cai.id
   JOIN   common_activitysetting cas ON cas.id = s.id
   WHERE  cai.end_time::date = '2015-09-12'   -- problem?
   AND    cai.activity_type = 'QZ'
   AND   (cas.key = 'disable_student_nav' AND cas.value IN ('True', 'False') OR
          cas.key = 'pacing' AND cas.value IN ('student', 'teacher'))
   )
SELECT *
FROM  (
   SELECT count(*) AS spf
   FROM  (
      SELECT c.id
      FROM   cte c
      JOIN   quizzes_quiz q ON q.id = c.activity_id
      WHERE  q.name <> 'Exit Ticket Quiz'
      AND   (c.key, c.value) IN (('disable_student_nav', 'True')
                               , ('pacing', 'student'))
      GROUP  BY 1
      HAVING count(*) = 2
      ) sub
   ) spf
,  (
   SELECT count(key = 'disable_student_nav' AND value = 'False' OR NULL) AS spn
        , count(key = 'pacing' AND value = 'teacher' OR NULL) AS tp
   FROM   cte
   ) spn_tp;

应该适用于 Postgres 9.3。在 Postgres 9.4 中,您可以使用新的聚合 FILTER 子句:

  count(*) FILTER (WHERE key = 'disable_student_nav' AND value = 'False') AS spn
, count(*) FILTER (WHERE key = 'pacing' AND value = 'teacher') AS tp

两种语法变体的详细信息:

标记为problem? 的条件可能是很大的性能问题,具体取决于cai.end_time 的数据类型。首先,它不是 sargable .而如果是 timestamptz 类型,则表达式很难索引,因为结果取决于 session 的当前时区设置——在不同时区执行时也会导致不同的结果。

比较:

您只需命名应该定义您的日期的时区。以我在维也纳的时区为例:

WHERE  cai.end_time >= '2015-09-12 0:0'::timestamp AT TIME ZONE 'Europe/Vienna' 
AND    cai.end_time <  '2015-09-13 0:0'::timestamp AT TIME ZONE 'Europe/Vienna'

您也可以提供简单的 timestamptz 值。你甚至可以:

WHERE  cai.end_time >= '2015-09-12'::date
AND    cai.end_time <  '2015-09-12'::date + 1

但第一个变体不依赖于当前时区设置。
上面的链接中有详细说明。

现在查询可以使用您的索引,如果您的表中有许多不同的日期,查询应该会快得多。

关于sql - 比多个 SELECT 语句更好的方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32544322/

相关文章:

sql - Postgres 中的库存计算

mysql - 为mysql中的特定列预设结果?

javascript - 遍历具有相同类名的选择下拉列表以查找特定选项的选择

select - Go select 语句解决方法中的优先级

mysql - SQL Insert from Select to same table but with a changed ID - MySQL

mysql 统计一个字段值在另一个表字段中出现的次数

javascript - Prisma findMany 函数不返回关系数据

php - 使用 Propel 获取最后 5 项 sql 结果

sql - C 语言的 DB2 包

c# - 与 PostgreSQL 连接的良好技术