Можете да групирате по-голямата част от разходите в една основна заявка в CTE
и използвайте повторно резултата няколко пъти.
Това връща един ред с три колони именуван след всеки type
(както е поискано в коментар
):
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
тип, изразът е труден за индексиране, тъй като резултатът зависи от текущата настройка на часовата зона на сесията - което също може да доведе до различни резултати, когато се изпълнява в различни часови зони.
Сравнете:
- Извадете две заявки от една и съща таблица
- Изваждане на часовете от сега( ) функция
- Пълно игнориране на часовите зони в Rails и PostgreSQL
Просто трябва да посочите часовата зона, която трябва да определя вашата дата. Вземам моята часова зона във Виена като пример:
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
Но първият вариант не зависи от текущата настройка на часовата зона.
Подробно обяснение във връзките по-горе.
Сега заявката може да използва вашия индекс и трябва да бъде много по-бърза, ако има много различни дни в таблицата ви.