sql >> Database >  >> RDS >> PostgreSQL

Optimaliseer de INSERT / UPDATE / DELETE-bewerking

Gewijzigde tabeldefinitie

Als u echt wilt dat die kolommen NOT NULL zijn en je hebt echt de string 'default' nodig als standaard voor engine_slug , zou ik adviseren om kolomstandaarden in te voeren:

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-statement zou zijn:

ALTER TABLE watson_searchentry ALTER COLUMN  engine_slug DEFAULT 'default';

enz.

Dan hoef je die waarden niet elke keer handmatig in te voeren.

Ook:object_id text NOT NULL, object_id_int INTEGER ? Dat is raar. Ik denk dat je je redenen hebt ...

Ik ga akkoord met uw bijgewerkte eis:

Natuurlijk moet je moeten voeg een UNIEK toe beperking om uw vereisten af ​​te dwingen:

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

De bijbehorende index wordt gebruikt. Door deze vraag om te beginnen.

Trouwens, ik gebruik bijna nooit varchar(n) in Postgres. Gewoon text . Hier is een reden.

Query met gegevensmodificerende CTE's

Dit kan worden herschreven als een enkele SQL-query met gemeenschappelijke tabelexpressies die gegevens wijzigen, ook wel "beschrijfbare" CTE's genoemd. Vereist Postgres 9.1 of hoger.
Bovendien verwijdert deze zoekopdracht alleen wat moet worden verwijderd en wordt bijgewerkt wat kan worden bijgewerkt.

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;
  • De subquery op django_content_type retourneert altijd een enkele waarde? Anders, de CROSS JOIN kan problemen veroorzaken.

  • De eerste CTE sel verzamelt de in te voegen rijen. Let op hoe ik overeenkomende kolomnamen kies om dingen te vereenvoudigen.

  • In de CTE del Ik vermijd het verwijderen van rijen die kunnen worden bijgewerkt.

  • In de CTE up die rijen worden in plaats daarvan bijgewerkt.

  • Dienovereenkomstig vermijd ik het invoegen van rijen die niet eerder zijn verwijderd in de laatste INSERT .

Kan eenvoudig worden verpakt in een SQL- of PL/pgSQL-functie voor herhaald gebruik.

Niet veilig voor intensief gelijktijdig gebruik. Veel beter dan de functie die je had, maar nog steeds niet 100% robuust tegen gelijktijdig schrijven. Maar dat is geen probleem volgens uw bijgewerkte informatie.

Het vervangen van de UPDATEs door DELETE en INSERT kan al dan niet een stuk duurder zijn. Intern resulteert elke UPDATE sowieso in een nieuwe rijversie, vanwege de MVCC model .

Snelheid eerst

Als u niet echt geïnteresseerd bent in het behouden van oude rijen, kan uw eenvoudigere aanpak sneller zijn:verwijder alles en voeg nieuwe rijen in. Bovendien bespaart het inpakken in een plpgsql-functie wat planningsoverhead. Uw functie eigenlijk, met een paar kleine vereenvoudigingen en met inachtneming van de hierboven toegevoegde standaardwaarden:

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;

Ik zie zelfs af van het gebruik van concat_ws() :Het is veilig tegen NULL waarden en vereenvoudigt code, maar een beetje langzamer dan eenvoudige aaneenschakeling.

Ook:

Het zou sneller zijn om de logica in deze functie op te nemen - als dit de enige keer is dat de trigger nodig is. Anders is het waarschijnlijk niet de moeite waard.




  1. ALTER-tabel - AUTOINCREMENT toevoegen in MySQL

  2. CodeIgniter:hoe de WHERE-clausule en OR-clausule te gebruiken?

  3. Exclusief MySQL-slot

  4. Hoe de ORDER BY-clausule op te lossen staat niet in de SELECT-lijst veroorzaakt MySQL 5.7 met SELECT DISTINCT en ORDER BY