PostgreSQL
 sql >> Baza danych >  >> RDS >> PostgreSQL

Optymalizacja operacji INSERT/UPDATE/DELETE

Zmodyfikowana definicja tabeli

Jeśli naprawdę potrzebujesz tych kolumn, aby były NOT NULL i naprawdę potrzebujesz ciągu 'default' jako domyślne dla engine_slug , radziłbym wprowadzić wartości domyślne kolumn:

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
 ...

Oświadczenie DDL to:

ALTER TABLE watson_searchentry ALTER COLUMN  engine_slug DEFAULT 'default';

Itd.

Wtedy nie musisz za każdym razem wstawiać tych wartości ręcznie.

Także:object_id text NOT NULL, object_id_int INTEGER ? To dziwne. Chyba masz swoje powody...

Pójdę z Twoimi zaktualizowanymi wymaganiami:

Oczywiście musisz dodaj UNIKALNE ograniczenie w celu wymuszenia Twoich wymagań:

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

Zostanie użyty towarzyszący indeks. Przez to zapytanie na początek.

BTW, prawie nigdy nie używam varchar(n) w Postgresie. Po prostu text . Oto jeden powód.

Zapytanie z CTE modyfikacji danych

Można to przepisać jako pojedyncze zapytanie SQL ze wspólnymi wyrażeniami tabelowymi modyfikującymi dane, zwanymi także „zapisywalnymi” CTE. Wymaga Postgresa 9.1 lub nowszego.
Ponadto to zapytanie usuwa tylko to, co należy usunąć, i aktualizuje to, co można zaktualizować.

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;
  • Podzapytanie w django_content_type zawsze zwraca pojedynczą wartość? W przeciwnym razie CROSS JOIN może powodować kłopoty.

  • Pierwsze CTE sel zbiera wiersze do wstawienia. Zwróć uwagę, jak wybieram pasujące nazwy kolumn aby uprościć rzeczy.

  • W CTE del Unikam usuwania wierszy, które można aktualizować.

  • W CTE up zamiast tego te wiersze są aktualizowane.

  • W związku z tym unikam wstawiania wierszy, które nie zostały wcześniej usunięte w końcowym INSERT .

Może być łatwo opakowany w funkcję SQL lub PL/pgSQL do wielokrotnego użytku.

Nie jest bezpieczny w przypadku intensywnego jednoczesnego użytkowania. Dużo lepsza niż funkcja, którą miałeś, ale wciąż nie jest w 100% odporna na współbieżne zapisy. Ale to nie jest problem według zaktualizowanych informacji.

Zastąpienie aktualizacji na DELETE i INSERT może, ale nie musi być dużo droższe. Wewnętrznie każda aktualizacja i tak powoduje powstanie nowej wersji wiersza, ze względu na MVCC model .

Najpierw prędkość

Jeśli naprawdę nie zależy Ci na zachowaniu starych wierszy, prostsze podejście może być szybsze:Usuń wszystko i wstaw nowe wiersze. Ponadto zawinięcie w funkcję plpgsql pozwala zaoszczędzić trochę czasu na planowanie. Zasadniczo twoja funkcja, z kilkoma drobnymi uproszczeniami i przestrzeganiem domyślnych ustawień dodanych powyżej:

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;

Powstrzymuję się nawet od używania concat_ws() :Jest bezpieczny przed NULL wartości i upraszcza kod, ale nieco wolniej niż zwykła konkatenacja.

Również:

Szybciej byłoby włączyć logikę do tej funkcji - jeśli jest to jedyny moment, w którym potrzebny jest wyzwalacz. W przeciwnym razie prawdopodobnie nie jest to warte zamieszania.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Agregacja chmur punktów współrzędnych (x,y) w PostgreSQL

  2. Jak za pomocą jsonb (PostgreSQL) pobrać elementy o określonej wartości zapisane jako tablica?

  3. Usuwanie zduplikowanych wierszy w PostgreSQL z wieloma kolumnami

  4. Limit czasu zapytania w pg-promise

  5. Jak wyświetlić numery wierszy w zapytaniu PostgreSQL?