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

Optimalizujte operaci INSERT / UPDATE / DELETE

Upravená definice tabulky

Pokud opravdu potřebujete, aby tyto sloupce byly NOT NULL a opravdu potřebujete řetězec 'default' jako výchozí pro engine_slug , doporučil bych zavést výchozí hodnoty sloupců:

COLUMN           |          TYPE           |      Modifiers
-----------------+-------------------------+---------------------
 id              | INTEGER                 | NOT NULL DEFAULT ... 
 engine_slug     | CHARACTER VARYING(200)  | NOT NULL DEFAULT 'default'
 content_type_id | INTEGER                 | NOT NULL
 object_id       | text                    | NOT NULL
 object_id_int   | INTEGER                 |
 title           | CHARACTER VARYING(1000) | NOT NULL
 description     | text                    | NOT NULL DEFAULT ''
 content         | text                    | NOT NULL
 url             | CHARACTER VARYING(1000) | NOT NULL DEFAULT ''
 meta_encoded    | text                    | NOT NULL DEFAULT '{}'
 search_tsv      | tsvector                | NOT NULL
 ...

Příkaz DDL by byl:

ALTER TABLE watson_searchentry ALTER COLUMN  engine_slug DEFAULT 'default';

atd.

Pak nemusíte tyto hodnoty pokaždé vkládat ručně.

Také:object_id text NOT NULL, object_id_int INTEGER ? To je zvláštní. Myslím, že máte své důvody ...

Vezmu váš aktualizovaný požadavek:

Samozřejmě, že musíte přidat UNIQUE omezení pro prosazení vašich požadavků:

ALTER TABLE watson_searchentry
ADD CONSTRAINT ws_uni UNIQUE (content_type_id, object_id_int)

Použije se doprovodný index. Pro začátek tímto dotazem.

BTW, téměř nikdy nepoužívám varchar(n) v Postgresu. Stačí text . To je jeden důvod.

Dotaz pomocí CTE upravujících data

To by se dalo přepsat jako jeden SQL dotaz s běžnými tabulkovými výrazy upravujícími data, nazývané také „zapisovatelné“ CTE. Vyžaduje Postgres 9.1 nebo novější.
Tento dotaz navíc smaže pouze to, co je třeba odstranit, a aktualizuje to, co aktualizovat lze.

WITH  ctyp AS (
   SELECT id AS content_type_id
   FROM   django_content_type
   WHERE  app_label = 'web'
   AND    model = 'member'
   )
, sel AS (
   SELECT ctyp.content_type_id
         ,m.id       AS object_id_int
         ,m.id::text AS object_id       -- explicit cast!
         ,m.name     AS title
         ,concat_ws(' ', u.email,m.normalized_name,c.name) AS content
         -- other columns have column default now.
   FROM   web_user    u
   JOIN   web_member  m  ON m.user_id = u.id
   JOIN   web_country c  ON c.id = m.country_id
   CROSS  JOIN ctyp
   WHERE  u.is_active
   )
, del AS (     -- only if you want to del all other entries of same type
   DELETE FROM watson_searchentry w
   USING  ctyp
   WHERE  w.content_type_id = ctyp.content_type_id
   AND    NOT EXISTS (
      SELECT 1
      FROM   sel
      WHERE  sel.object_id_int = w.object_id_int
      )
   )
, up AS (      -- update existing rows
   UPDATE watson_searchentry 
   SET    object_id = s.object_id
         ,title     = s.title
         ,content   = s.content
   FROM   sel s
   WHERE  w.content_type_id = s.content_type_id
   AND    w.object_id_int   = s.object_id_int
   )
               -- insert new rows
INSERT  INTO watson_searchentry (
        content_type_id, object_id_int, object_id, title, content)
SELECT  sel.*  -- safe to use, because col list is defined accordingly above
FROM    sel
LEFT    JOIN watson_searchentry w1 USING (content_type_id, object_id_int)
WHERE   w1.content_type_id IS NULL;
  • Dílčí dotaz na django_content_type vždy vrací jednu hodnotu? V opačném případě CROSS JOIN může způsobit potíže.

  • První CTE sel shromažďuje řádky, které mají být vloženy. Všimněte si, jak vybírám odpovídající názvy sloupců abychom věci zjednodušili.

  • V CTE del Vyhýbám se mazání řádků, které lze aktualizovat.

  • V CTE up tyto řádky jsou místo toho aktualizovány.

  • V souladu s tím se vyhýbám vkládání řádků, které nebyly předtím smazány, do závěrečného INSERT .

Lze jej snadno zabalit do funkce SQL nebo PL/pgSQL pro opakované použití.

Není bezpečný pro těžké souběžné použití. Mnohem lepší než funkce, kterou jste měli, ale stále ne 100% robustní proti souběžným zápisům. Ale to podle vašich aktualizovaných informací není problém.

Nahrazení UPDATE s DELETE a INSERT může, ale nemusí být mnohem dražší. Interně má každá UPDATE každopádně za následek novou verzi řádku kvůli MVCC model .

Nejdříve rychlost

Pokud se opravdu nestaráte o zachování starých řádků, váš jednodušší přístup může být rychlejší:Smažte vše a vložte nové řádky. Zabalení do funkce plpgsql také ušetří trochu režie plánování. Vaše funkce v podstatě s několika drobnými zjednodušeními a dodržením výchozích hodnot přidaných výše:

CREATE OR REPLACE FUNCTION update_member_search_index()
  RETURNS VOID AS
$func$
DECLARE
   _ctype_id int := (
      SELECT id
      FROM   django_content_type
      WHERE  app_label='web'
      AND    model = 'member'
      );  -- you can assign at declaration time. saves another statement
BEGIN
   DELETE FROM watson_searchentry
   WHERE content_type_id = _ctype_id;

   INSERT INTO watson_searchentry
         (content_type_id, object_id, object_id_int, title, content)
   SELECT _ctype_id, m.id, m.id::int,m.name
         ,u.email || ' ' || m.normalized_name || ' ' || c.name
   FROM   web_member  m
   JOIN   web_user    u USING (user_id)
   JOIN   web_country c ON c.id = m.country_id
   WHERE  u.is_active;
END
$func$ LANGUAGE plpgsql;

Dokonce se zdržuji používání concat_ws() :Je bezpečný proti NULL hodnot a zjednodušuje kód, ale o něco pomaleji než jednoduché zřetězení.

Také:

Bylo by rychlejší začlenit logiku do této funkce - pokud je to jediný okamžik, kdy je potřeba spoušť. Jinak to asi nestojí za ten povyk.




  1. Opravte problém se zamítnutím přístupu k obnovení SQL databáze

  2. mysql dvousloupcový primární klíč s automatickým přírůstkem

  3. Oracle:Vyberte z datového typu záznamu

  4. Porovnání výkonu virtuálních počítačů Windows Azure, část 1