PostgreSQL
 sql >> база данни >  >> RDS >> PostgreSQL

Оптимизирайте операцията INSERT / UPDATE / DELETE

Модифицирана дефиниция на таблица

Ако наистина имате нужда тези колони да бъдат NOT NULL и наистина се нуждаете от низа 'default' по подразбиране за engine_slug , бих посъветвал да се въведат стойности по подразбиране на колоните:

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

DDL изразът би бил:

ALTER TABLE watson_searchentry ALTER COLUMN  engine_slug DEFAULT 'default';

И т.н.

Тогава не е необходимо да вмъквате тези стойности ръчно всеки път.

Също така:object_id text NOT NULL, object_id_int INTEGER ? Това е странно. Предполагам, че имате своите причини...

Ще изпълня актуализираното ви изискване:

Разбира се, четрябвате добавете УНИКАЛНО ограничение за налагане на вашите изисквания:

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

Ще се използва придружаващият индекс. С тази заявка за начало.

Между другото почти никога не използвам varchar(n) в Postgres. Само text . Ето една причина.

Заявка с CTE, модифициращи данни

Това може да бъде пренаписано като единична SQL заявка с модифициращи данни общи таблични изрази, наричани също "записваеми" CTE. Изисква Postgres 9.1 или по-нова версия.
Освен това, тази заявка изтрива само това, което трябва да бъде изтрито, и актуализира това, което може да бъде актуализирано.

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;
  • Подзаявката за django_content_type винаги връща една стойност? В противен случай CROSS JOIN може да причини проблеми.

  • Първият CTE sel събира редовете за вмъкване. Обърнете внимание как избирам съвпадащи имена на колони за опростяване на нещата.

  • В CTE del Избягвам да изтривам редове, които могат да бъдат актуализирани.

  • В CTE up вместо това тези редове се актуализират.

  • Съответно избягвам да вмъквам редове, които не са били изтрити преди това в крайния INSERT .

Може лесно да се обвие в SQL или PL/pgSQL функция за многократна употреба.

Не е сигурно за тежка едновременна употреба. Много по-добра от функцията, която сте имали, но все още не е 100% устойчива срещу едновременни записи. Но това не е проблем според вашата актуализирана информация.

Замяната на актуализациите с DELETE и INSERT може или не може да бъде много по-скъпа. Вътрешно всяка АКТУАЛИЗАЦИЯ така или иначе води до нова версия на ред, поради MVCC модел .

Първо скоростта

Ако наистина не ви е грижа за запазването на стари редове, вашият по-прост подход може да е по-бърз:Изтрийте всичко и вмъкнете нови редове. Освен това обвиването във функция plpgsql спестява малко допълнителни разходи за планиране. Основно вашата функция, с няколко незначителни опростявания и спазване на добавените по-горе настройки по подразбиране:

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;

Дори се въздържам да използвам concat_ws() :Безопасно е срещу NULL стойности и опростява кода, но малко по-бавно от обикновеното конкатениране.

Също така:

Би било по-бързо да се включи логиката в тази функция - ако това е единственият път, когато тригерът е необходим. В противен случай вероятно не си струва шума.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Как ефективно да избера предишната ненулева стойност?

  2. Пребройте кумулативната сума в Postgresql

  3. Защо командата \dt дава - няма намерени отношения?

  4. Инсталирайте PostgreSQL на Ubuntu 18.04

  5. КАСКАДНО ИЗТРИВАНЕ само веднъж