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

Optimalizace více spojení

Při optimalizaci dotazů je vždy třeba zvážit 2 věci:

  • Jaké indexy lze použít (možná budete muset indexy vytvořit)
  • Jak je dotaz napsán (možná budete muset dotaz změnit, aby optimalizátor dotazů mohl najít vhodné indexy a aby se data zbytečně nečetla)

Několik postřehů:

  • Než se k datům připojíte, provádíte manipulace s datem. Jako obecné pravidlo to zabrání optimalizátoru dotazů v použití indexu, i když existuje. Měli byste se pokusit napsat své výrazy tak, aby indexované sloupce existovaly beze změny na jedné straně výrazu.

  • Vaše poddotazy jsou filtrovány na stejné časové období jako generate_series . Toto je duplikace a omezuje možnost optimalizátora zvolit nejúčinnější optimalizaci. Mám podezření, že to mohlo být zapsáno za účelem zlepšení výkonu, protože optimalizátor nedokázal použít index ve sloupci data (body_time )?

  • POZNÁMKA :Ve skutečnosti bychom velmi rádi použili index na Body.body_time

  • ORDER BY v rámci poddotazů je přinejlepším redundantní. V nejhorším případě by to mohlo přinutit optimalizátor dotazů seřadit sadu výsledků před připojením; a to není nutně dobré pro plán dotazů. Pro konečné zobrazení raději použijte objednávku přímo na konci.

  • Použití LEFT JOIN ve vašich poddotazech je nevhodné. Za předpokladu, že pro NULL používáte konvence ANSI chování (a měli byste být), jakékoli vnější se připojí k envelope vrátí envelope_command=NULL a ty by následně byly vyloučeny podmínkou envelope_command=? .

  • Dílčí dotazy o a i jsou téměř totožné kromě envelope_command hodnota. To přinutí optimser skenovat stejné podkladové tabulky dvakrát. Můžete použít kontingenční tabulku technika pro jedno připojení k datům a rozdělení hodnot do 2 sloupců.

Vyzkoušejte následující postup, který využívá techniku ​​pivotování:

SELECT  p.period,
        /*The pivot technique in action...*/
        SUM(
        CASE WHEN envelope_command = 1 THEN body_size
        ELSE 0
        END) AS Outbound,
        SUM(
        CASE WHEN envelope_command = 2 THEN body_size
        ELSE 0
        END) AS Inbound
FROM    (
        SELECT  date '2009-10-01' + s.day AS period
        FROM    generate_series(0, date '2009-10-31' - date '2009-10-01') AS s(day)
        ) AS p 
        /*The left JOIN is justified to ensure ALL generated dates are returned
          Also: it joins to a subquery, else the JOIN to envelope _could_ exclude some generated dates*/
        LEFT OUTER JOIN (
        SELECT  b.body_size,
                b.body_time,
                e.envelope_command
        FROM    body AS b 
                INNER JOIN envelope e 
                  ON e.message_id = b.message_id 
        WHERE   envelope_command IN (1, 2)
        ) d
          /*The expressions below allow the optimser to use an index on body_time if 
            the statistics indicate it would be beneficial*/
          ON d.body_time >= p.period
         AND d.body_time < p.period + INTERVAL '1 DAY'
GROUP BY p.Period
ORDER BY p.Period

UPRAVIT :Přidán filtr navržený Tomem H.



  1. Odstraňování problémů Oracle - zablokovaný proces

  2. Chyba .NET a MySQL - Volání SSPI selhalo ... přijatá zpráva byla neočekávaná nebo špatně naformátovaná A vyrovnávací paměti dodané funkci byly příliš malé

  3. SQL dotaz s CASE a seskupit podle

  4. DML a zpracování výjimek - Oracle