Większość kosztów można umieścić w jednym głównym zapytaniu w CTE
i użyj wyniku kilka razy.
Zwraca pojedynczy wiersz z trzema kolumnami nazwane po każdym type
(zgodnie z żądaniem w komentarzu
):
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;
Powinien działać w Postgresie 9.3. W Postgresie 9.4 możesz użyć nowego agregatu FILTER
klauzula:
count(*) FILTER (WHERE key = 'disable_student_nav' AND value = 'False') AS spn
, count(*) FILTER (WHERE key = 'pacing' AND value = 'teacher') AS tp
Szczegóły obu wariantów składni:
Warunek oznaczony jako problem?
może być dużym problemem z wydajnością, w zależności od typu danych cai.end_time
. Po pierwsze, nie jest sargable
. A jeśli jest to timestamptz
typ, wyrażenie jest trudne do zindeksowania, ponieważ wynik zależy od aktualnego ustawienia strefy czasowej sesji - co może również prowadzić do różnych wyników w przypadku wykonania w różnych strefach czasowych.
Porównaj:
- Odciągnij dwa zapytania z tej samej tabeli
- Odejmij godziny od teraz( ) funkcja
- Całkowite ignorowanie stref czasowych w Railsy i PostgreSQL
Musisz tylko nazwać strefę czasową, która ma określać twoją datę. Na przykładzie mojej strefy czasowej w Wiedniu:
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'
Możesz podać prosty timestamptz
wartości. Możesz nawet po prostu:
WHERE cai.end_time >= '2015-09-12'::date
AND cai.end_time < '2015-09-12'::date + 1
Ale pierwszy wariant nie zależy od aktualnego ustawienia strefy czasowej.
Szczegółowe wyjaśnienie w powyższych linkach.
Teraz zapytanie może korzystać z Twojego indeksu i powinno być znacznie szybsze, jeśli w Twojej tabeli jest wiele różnych dni.