PostgreSQL
 sql >> Teknologi Basis Data >  >> RDS >> PostgreSQL

Optimalkan operasi INSERT / UPDATE / DELETE

Definisi tabel yang dimodifikasi

Jika Anda benar-benar membutuhkan kolom tersebut menjadi NOT NULL dan Anda benar-benar membutuhkan string 'default' sebagai default untuk engine_slug , saya akan menyarankan untuk memperkenalkan default kolom:

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

Pernyataan DDL akan menjadi:

ALTER TABLE watson_searchentry ALTER COLUMN  engine_slug DEFAULT 'default';

dll.

Maka Anda tidak perlu memasukkan nilai tersebut secara manual setiap saat.

Juga:object_id text NOT NULL, object_id_int INTEGER ? Itu aneh. Saya kira Anda punya alasan Anda ...

Saya akan mengikuti persyaratan Anda yang diperbarui:

Tentu saja, Anda harus tambahkan UNIK batasan untuk menegakkan persyaratan Anda:

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

Indeks yang menyertainya akan digunakan. Dengan kueri ini sebagai permulaan.

BTW, saya hampir tidak pernah menggunakan varchar(n) di Postgres. Cukup text . Inilah salah satu alasannya.

Kueri dengan CTE pemodifikasi data

Ini dapat ditulis ulang sebagai kueri SQL tunggal dengan ekspresi tabel umum yang mengubah data, juga disebut CTE "yang dapat ditulis". Memerlukan Postgres 9.1 atau yang lebih baru.
Selain itu, kueri ini hanya menghapus apa yang harus dihapus, dan memperbarui apa yang dapat diperbarui.

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;
  • Subkueri pada django_content_type selalu mengembalikan nilai tunggal? Jika tidak, CROSS JOIN dapat menyebabkan masalah.

  • sel CTE pertama mengumpulkan baris yang akan disisipkan. Perhatikan bagaimana saya memilih nama kolom yang cocok untuk menyederhanakan banyak hal.

  • Dalam del CTE Saya menghindari menghapus baris yang dapat diperbarui.

  • Di CTE up baris tersebut malah diperbarui.

  • Oleh karena itu, saya menghindari menyisipkan baris yang tidak dihapus sebelumnya di INSERT terakhir .

Dapat dengan mudah dimasukkan ke dalam fungsi SQL atau PL/pgSQL untuk penggunaan berulang.

Tidak aman untuk penggunaan bersamaan yang berat. Jauh lebih baik daripada fungsi yang Anda miliki, tetapi masih belum 100% kuat terhadap penulisan bersamaan. Tapi itu bukan masalah menurut info terbaru Anda.

Mengganti UPDATE dengan DELETE dan INSERT mungkin jauh lebih mahal atau tidak. Secara internal setiap UPDATE menghasilkan versi baris baru, karena MVCC model .

Kecepatan dulu

Jika Anda tidak terlalu peduli dengan mempertahankan baris lama, pendekatan Anda yang lebih sederhana mungkin lebih cepat:Hapus semuanya dan sisipkan baris baru. Juga, membungkus ke dalam fungsi plpgsql menghemat sedikit overhead perencanaan. Fungsi Anda pada dasarnya, dengan beberapa penyederhanaan kecil dan mengamati default yang ditambahkan di atas:

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;

Saya bahkan menahan diri untuk tidak menggunakan concat_ws() :Aman terhadap NULL nilai dan menyederhanakan kode, tetapi sedikit lebih lambat dari penggabungan sederhana.

Juga:

Akan lebih cepat untuk memasukkan logika ke dalam fungsi ini - jika ini adalah satu-satunya waktu pemicu diperlukan. Jika tidak, mungkin tidak perlu repot.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Postgres Notify tidak berfungsi dengan replikasi logis

  2. Cara memetakan org.postgresql.geometric.PGpoint ke Tipe Hibernasi

  3. Normalisasi unicode di PostgreSQL 13

  4. Solusi Cloud PostgreSQL Terkelola Pembandingan - Bagian Empat:Microsoft Azure

  5. PostgreSQL - INNER GABUNG dua tabel dengan LIMIT