変更されたテーブル定義
これらの列を本当にNOT NULL
にする必要がある場合 本当に必要なのは文字列'default'
engine_slug
のデフォルトとして 、列のデフォルトを導入することをお勧めします:
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ステートメントは次のようになります:
ALTER TABLE watson_searchentry ALTER COLUMN engine_slug DEFAULT 'default';
など
そうすれば、それらの値を毎回手動で挿入する必要はありません。
また、object_id text NOT NULL, object_id_int INTEGER
?それは変です。理由があると思います...
更新された要件に対応します:
もちろん、あなたはしなければならない UNIQUEを追加します 要件を強制するための制約:
ALTER TABLE watson_searchentry
ADD CONSTRAINT ws_uni UNIQUE (content_type_id, object_id_int)
付随するインデックスが使用されます。初心者のためのこのクエリによって。
ところで、私はvarchar(n)
をほとんど使用しません Postgresで。 text
。 1つの理由があります。
データ変更CTEを使用したクエリ
これは、「書き込み可能な」CTEとも呼ばれるデータ変更共通テーブル式を使用した単一のSQLクエリとして書き直すことができます。 Postgres 9.1以降が必要です。
さらに、このクエリは削除する必要があるものだけを削除し、更新できるものを更新します。
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;
-
django_content_type
のサブクエリ 常に単一の値を返しますか?それ以外の場合、CROSS JOIN
トラブルの原因になります。 -
最初のCTE
sel
挿入する行を収集します。 一致する列名を選択する方法に注意してください 物事を単純化するため。 -
CTEの
del
更新可能な行を削除することは避けています。 -
CTEで
up
それらの行は代わりに更新されます。 -
したがって、以前に削除されなかった行を最後の
INSERT
に挿入することは避けます。 。
繰り返し使用するためにSQLまたはPL/pgSQL関数に簡単にラップできます。
大量の同時使用には安全ではありません。あなたが持っていた機能よりもはるかに優れていますが、それでも同時書き込みに対して100%堅牢ではありません。しかし、更新された情報によると、それは問題ではありません。
UPDATEをDELETEおよびINSERTに置き換えると、コストが大幅に高くなる場合とそうでない場合があります。 MVCCにより、内部的にすべてのUPDATEで新しい行バージョンが生成されます。モデル 。
スピードファースト
古い行を保持することにあまり関心がない場合は、より単純なアプローチの方が速い場合があります。すべてを削除して、新しい行を挿入します。また、plpgsql関数にラップすることで、計画のオーバーヘッドを少し節約できます。あなたの関数は基本的に、いくつかのマイナーな簡略化と上記で追加されたデフォルトの遵守を伴います:
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;
concat_ws()
:NULL
に対して安全です 値を設定してコードを単純化しますが、単純な連結よりも少し遅くなります。
また:
トリガーが必要なのがこれだけの場合は、ロジックをこの関数に組み込む方が高速です。そうでなければ、大騒ぎする価値はおそらくないでしょう。