PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Optimieren Sie den INSERT / UPDATE / DELETE-Vorgang

Geänderte Tabellendefinition

Wenn Sie wirklich brauchen, dass diese Spalten NOT NULL sind und Sie brauchen wirklich den String 'default' als Standard für engine_slug , würde ich raten, Spaltenvorgaben einzuführen:

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

Die DDL-Anweisung wäre:

ALTER TABLE watson_searchentry ALTER COLUMN  engine_slug DEFAULT 'default';

usw.

Dann müssen Sie diese Werte nicht jedes Mal manuell eingeben.

Außerdem:object_id text NOT NULL, object_id_int INTEGER ? Das ist seltsam. Ich denke, Sie haben Ihre Gründe ...

Ich gehe mit Ihrer aktualisierten Anforderung:

Natürlich müssen Sie fügen Sie ein UNIQUE hinzu Einschränkung, um Ihre Anforderungen durchzusetzen:

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

Der begleitende Index wird verwendet. Durch diese Abfrage für den Anfang.

Übrigens verwende ich fast nie varchar(n) in Postgres. Nur text . Hier ist ein Grund.

Abfrage mit datenmodifizierenden CTEs

Dies könnte als einzelne SQL-Abfrage mit datenmodifizierenden allgemeinen Tabellenausdrücken, auch als „schreibbare“ CTEs bezeichnet, umgeschrieben werden. Erfordert Postgres 9.1 oder höher.
Außerdem löscht diese Abfrage nur, was gelöscht werden muss, und aktualisiert, was aktualisiert werden kann.

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;
  • Die Unterabfrage auf django_content_type gibt immer einen einzelnen Wert zurück? Andernfalls der CROSS JOIN kann Probleme verursachen.

  • Der erste CTE sel sammelt die einzufügenden Zeilen. Beachten Sie, wie ich übereinstimmende Spaltennamen auswähle Dinge zu vereinfachen.

  • Im CTE del Ich vermeide es, Zeilen zu löschen, die aktualisiert werden können.

  • Im CTE up diese Zeilen werden stattdessen aktualisiert.

  • Dementsprechend vermeide ich es, im abschließenden INSERT Zeilen einzufügen, die vorher nicht gelöscht wurden .

Kann zur wiederholten Verwendung einfach in eine SQL- oder PL/pgSQL-Funktion eingebunden werden.

Nicht sicher für intensive gleichzeitige Nutzung. Viel besser als die Funktion, die Sie hatten, aber immer noch nicht 100% robust gegen gleichzeitige Schreibvorgänge. Aber das ist laut Ihren aktualisierten Informationen kein Problem.

Das Ersetzen der UPDATEs durch DELETE und INSERT kann viel teurer sein oder auch nicht. Intern führt jedes UPDATE aufgrund des MVCC ohnehin zu einer neuen Zeilenversion Modell .

Geschwindigkeit zuerst

Wenn es Ihnen nicht wirklich wichtig ist, alte Zeilen beizubehalten, ist Ihr einfacherer Ansatz möglicherweise schneller:Alles löschen und neue Zeilen einfügen. Außerdem spart das Wrapping in eine plpgsql-Funktion etwas Planungsaufwand. Ihre Funktion im Grunde, mit ein paar kleinen Vereinfachungen und unter Beachtung der oben hinzugefügten Standardwerte:

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;

Ich verzichte sogar auf die Verwendung von concat_ws() :Es ist sicher gegen NULL Werte und vereinfacht den Code, ist aber etwas langsamer als einfache Verkettung.

Auch:

Es wäre schneller, die Logik in diese Funktion zu integrieren - wenn dies das einzige Mal ist, dass der Trigger benötigt wird. Sonst ist es wahrscheinlich den Aufwand nicht wert.