Você pode agrupar a maior parte do custo em uma única consulta principal em um CTE e reutilize o resultado várias vezes.
Isso retorna uma única linha com três colunas nomeado após cada
type
(conforme solicitado no comentário
):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;
Deve funcionar para o Postgres 9.3. No Postgres 9.4 você pode usar o novo agregado
FILTER
cláusula: count(*) FILTER (WHERE key = 'disable_student_nav' AND value = 'False') AS spn
, count(*) FILTER (WHERE key = 'pacing' AND value = 'teacher') AS tp
Detalhes para ambas as variantes de sintaxe:
A condição marcada como
problem?
pode ser um grande problema de desempenho, dependendo do tipo de dados de cai.end_time
. Por um lado, não é sargable
. E se for um timestamptz
tipo, a expressão é difícil de indexar, porque o resultado depende da configuração de fuso horário atual da sessão - o que também pode levar a resultados diferentes quando executado em fusos horários diferentes. Comparar:
- Sustre duas consultas da mesma tabela
- Subtrair horas do agora( ) função
- Ignorando completamente os fusos horários em Rails e PostgreSQL
Você só precisa nomear o fuso horário que deve definir sua data. Tomando meu fuso horário em Viena como exemplo:
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'
Você pode fornecer
timestamptz
simples valores também. Você poderia até mesmo:WHERE cai.end_time >= '2015-09-12'::date
AND cai.end_time < '2015-09-12'::date + 1
Mas a primeira variante não depende da configuração do fuso horário atual.
Explicação detalhada nos links acima.
Agora a consulta pode usar seu índice e deve ser muito mais rápida se houver muitos dias diferentes em sua tabela.