sql >> Databasteknik >  >> RDS >> PostgreSQL

Optimera INSERT / UPDATE / DELETE operation

Ändrad tabelldefinition

Om du verkligen behöver dessa kolumner vara NOT NULL och du behöver verkligen strängen 'default' som standard för engine_slug , Jag skulle råda dig att införa kolumnstandarder:

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-satsen skulle vara:

ALTER TABLE watson_searchentry ALTER COLUMN  engine_slug DEFAULT 'default';

Etc.

Då behöver du inte infoga dessa värden manuellt varje gång.

Även:object_id text NOT NULL, object_id_int INTEGER ? Det är konstigt. Jag antar att du har dina skäl ...

Jag följer ditt uppdaterade krav:

Självklart måste du lägg till en UNIKT begränsning för att upprätthålla dina krav:

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

Det medföljande indexet kommer att användas. Genom denna fråga till att börja med.

BTW, jag använder nästan aldrig varchar(n) i Postgres. Bara text . Här är en anledning.

Fråga med datamodifierande CTE:er

Detta skulle kunna skrivas om som en enda SQL-fråga med datamodifierande vanliga tabelluttryck, även kallade "skrivbara" CTE:er. Kräver Postgres 9.1 eller senare.
Dessutom tar den här frågan bara bort det som måste raderas och uppdaterar det som kan uppdateras.

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;
  • Underfrågan på django_content_type returnerar alltid ett enda värde? Annars används CROSS JOIN kan orsaka problem.

  • Det första CTE sel samlar raderna som ska infogas. Notera hur jag väljer matchande kolumnnamn för att förenkla saker.

  • I CTE del Jag undviker att ta bort rader som kan uppdateras.

  • I CTE up dessa rader uppdateras istället.

  • Följaktligen undviker jag att infoga rader som inte raderades tidigare i den sista INSERT .

Kan enkelt slås in i en SQL- eller PL/pgSQL-funktion för upprepad användning.

Inte säker för tung samtidig användning. Mycket bättre än funktionen du hade, men ändå inte 100% robust mot samtidiga skrivningar. Men det är inget problem enligt din uppdaterade information.

Att ersätta UPPDATERINGAR med DELETE och INSERT kan eller kanske inte vara mycket dyrare. Internt resulterar varje UPPDATERING i en ny radversion ändå, på grund av MVCC modell .

Hastighet först

Om du inte riktigt bryr dig om att bevara gamla rader, kan ditt enklare tillvägagångssätt vara snabbare:Ta bort allt och infoga nya rader. Om du lägger in en plpgsql-funktion sparar du också lite planering. Din funktion i princip, med ett par mindre förenklingar och observation av standardinställningarna som lagts till ovan:

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;

Jag avstår till och med från att använda concat_ws() :Det är säkert mot NULL värden och förenklar kod, men lite långsammare än enkel sammanfogning.

Även:

Det skulle vara snabbare att införliva logiken i den här funktionen - om det är den enda gången som utlösaren behövs. Annars är det nog inte värt krånglet.




  1. Entity Framework wizard kraschar på MySQL

  2. Oupptäckt undantag 'PDOException' med meddelandet 'Det finns ingen aktiv transaktion'?

  3. Hur väljer man ORDER BY kolumn och RAND() båda?

  4. SCD typ 6