sql >> Databáze >  >> RDS >> PostgreSQL

Lepší způsob než více příkazů SELECT?

Většinu nákladů můžete spojit do jednoho hlavního dotazu v CTE a výsledek několikrát znovu použít.
Vrátí to jeden řádek se třemi sloupci pojmenované podle každého type (dle požadavku v komentáři ):

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;

Mělo by fungovat pro Postgres 9.3. V Postgres 9.4 můžete použít nový agregát FILTER klauzule:

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

Podrobnosti pro obě varianty syntaxe:

Stav označený jako problem? může být velký problém s výkonem v závislosti na typu dat cai.end_time . Za prvé, není to sargable . A pokud je to timestamptz typu, výraz je těžké indexovat, protože výsledek závisí na aktuálním nastavení časového pásma relace – což může také vést k různým výsledkům při spuštění v různých časových pásmech.

Porovnejte:

Stačí pojmenovat časové pásmo, které má definovat vaše datum. Vezmu si jako příklad moje časové pásmo ve Vídni:

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'

Můžete poskytnout jednoduchý timestamptz hodnoty také. Můžete dokonce jen:

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

Ale první varianta nezávisí na aktuálním nastavení časového pásma.
Podrobné vysvětlení v odkazech výše.

Nyní může dotaz používat váš index a měl by být mnohem rychlejší, pokud je v tabulce mnoho různých dnů.



  1. PHP Jak vrátit datetime(6) z Mysql?

  2. Jak přetypovat proměnné v T-SQL pro hromadné vkládání?

  3. MySql - Aktualizujte tabulku pomocí vybraného příkazu ze stejné tabulky

  4. JSONb data:skutečná data interně?