Vous pouvez regrouper la plupart des coûts dans une seule requête principale dans un CTE
et réutiliser le résultat plusieurs fois.
Cela renvoie une ligne unique avec trois colonnes nommé d'après chaque type
(comme demandé dans le commentaire
):
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;
Devrait fonctionner pour Postgres 9.3. Dans Postgres 9.4, vous pouvez utiliser le nouvel agrégat FILTER
clause :
count(*) FILTER (WHERE key = 'disable_student_nav' AND value = 'False') AS spn
, count(*) FILTER (WHERE key = 'pacing' AND value = 'teacher') AS tp
Détails pour les deux variantes de syntaxe :
La condition marquée problem?
peut être un gros problème de performances, selon le type de données de cai.end_time
. D'une part, ce n'est pas sargable
. Et si c'est un timestamptz
type, l'expression est difficile à indexer, car le résultat dépend du paramètre de fuseau horaire actuel de la session - ce qui peut également conduire à des résultats différents lorsqu'il est exécuté dans différents fuseaux horaires.
Comparez :
- Soustraire deux requêtes de la même table
- Soustraire les heures de maintenant( ) fonction
- Ignorer complètement les fuseaux horaires dans Rails et PostgreSQL
Il vous suffit de nommer le fuseau horaire censé définir votre date. En prenant mon fuseau horaire à Vienne comme exemple :
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'
Vous pouvez fournir un simple timestamptz
valeurs aussi. Vous pouvez même :
WHERE cai.end_time >= '2015-09-12'::date
AND cai.end_time < '2015-09-12'::date + 1
Mais la première variante ne dépend pas du paramètre de fuseau horaire actuel.
Explication détaillée dans les liens ci-dessus.
Désormais, la requête peut utiliser votre index et devrait être beaucoup plus rapide s'il y a plusieurs jours différents dans votre table.