Định nghĩa bảng đã sửa đổi
Nếu bạn thực sự cần các cột đó phải NOT NULL
và bạn thực sự cần chuỗi 'default'
làm mặc định cho engine_slug
, Tôi khuyên bạn nên giới thiệu các giá trị mặc định của cột:
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
...
Câu lệnh DDL sẽ là:
ALTER TABLE watson_searchentry ALTER COLUMN engine_slug DEFAULT 'default';
Vv.
Sau đó, bạn không phải chèn các giá trị đó theo cách thủ công mỗi lần.
Ngoài ra:object_id text NOT NULL, object_id_int INTEGER
? Thật ki quặc. Tôi đoán bạn có lý do của mình ...
Tôi sẽ xử lý yêu cầu cập nhật của bạn:
Tất nhiên, bạn phải thêm một DUY NHẤT ràng buộc để thực thi các yêu cầu của bạn:
ALTER TABLE watson_searchentry
ADD CONSTRAINT ws_uni UNIQUE (content_type_id, object_id_int)
Chỉ mục đi kèm sẽ được sử dụng. Bằng truy vấn này cho người mới bắt đầu.
BTW, tôi hầu như không bao giờ sử dụng varchar(n)
trong Postgres. Chỉ cần text
. Đây là một lý do.
Truy vấn với CTE sửa đổi dữ liệu
Điều này có thể được viết lại dưới dạng một truy vấn SQL duy nhất với các biểu thức bảng phổ biến sửa đổi dữ liệu, còn được gọi là CTE "có thể ghi". Yêu cầu Postgres 9.1 trở lên.
Ngoài ra, truy vấn này chỉ xóa những gì phải xóa và cập nhật những gì có thể cập nhật.
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;
-
Truy vấn con trên
django_content_type
luôn trả về một giá trị duy nhất? Nếu không,CROSS JOIN
có thể gây ra sự cố. -
CTE
sel
đầu tiên tập hợp các hàng được chèn. Lưu ý cách tôi chọn tên cột phù hợp để đơn giản hóa mọi thứ. -
Trong CTE
del
Tôi tránh xóa các hàng có thể được cập nhật. -
Trong CTE
up
những hàng đó được cập nhật thay thế. -
Do đó, tôi tránh chèn các hàng chưa bị xóa trước đó trong
INSERT
cuối cùng .
Có thể dễ dàng được gói vào một hàm SQL hoặc PL / pgSQL để sử dụng nhiều lần.
Không an toàn khi sử dụng đồng thời nhiều. Tốt hơn nhiều so với chức năng bạn đã có, nhưng vẫn không mạnh mẽ 100% so với chức năng ghi đồng thời. Nhưng đó không phải là vấn đề theo thông tin cập nhật của bạn.
Việc thay thế CẬP NHẬT bằng XÓA và CHÈN có thể đắt hơn hoặc không đắt hơn rất nhiều. Nội bộ mỗi UPDATE luôn dẫn đến một phiên bản hàng mới, do MVCC mô hình .
Tốc độ đầu tiên
Nếu bạn không thực sự quan tâm đến việc bảo tồn các hàng cũ, cách tiếp cận đơn giản hơn của bạn có thể nhanh hơn:Xóa mọi thứ và chèn các hàng mới. Ngoài ra, gói thành một hàm plpgsql giúp tiết kiệm một chút chi phí lập kế hoạch. Về cơ bản, chức năng của bạn, với một vài đơn giản hóa nhỏ và tuân theo các giá trị mặc định được thêm ở trên:
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;
Tôi thậm chí không sử dụng concat_ws()
:Nó an toàn chống lại NULL
và đơn giản hóa mã, nhưng chậm hơn một chút so với nối đơn giản.
Ngoài ra:
Sẽ nhanh hơn nếu kết hợp logic vào chức năng này - nếu đây là lần duy nhất cần kích hoạt. Mặt khác, nó có lẽ không đáng để ồn ào.