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.