Gewijzigde tabeldefinitie
Als u echt wilt dat die kolommen NOT NULL
zijn en je hebt echt de string 'default'
nodig als standaard voor engine_slug
, zou ik adviseren om kolomstandaarden in te voeren:
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-statement zou zijn:
ALTER TABLE watson_searchentry ALTER COLUMN engine_slug DEFAULT 'default';
enz.
Dan hoef je die waarden niet elke keer handmatig in te voeren.
Ook:object_id text NOT NULL, object_id_int INTEGER
? Dat is raar. Ik denk dat je je redenen hebt ...
Ik ga akkoord met uw bijgewerkte eis:
Natuurlijk moet je moeten voeg een UNIEK toe beperking om uw vereisten af te dwingen:
ALTER TABLE watson_searchentry
ADD CONSTRAINT ws_uni UNIQUE (content_type_id, object_id_int)
De bijbehorende index wordt gebruikt. Door deze vraag om te beginnen.
Trouwens, ik gebruik bijna nooit varchar(n)
in Postgres. Gewoon text
. Hier is een reden.
Query met gegevensmodificerende CTE's
Dit kan worden herschreven als een enkele SQL-query met gemeenschappelijke tabelexpressies die gegevens wijzigen, ook wel "beschrijfbare" CTE's genoemd. Vereist Postgres 9.1 of hoger.
Bovendien verwijdert deze zoekopdracht alleen wat moet worden verwijderd en wordt bijgewerkt wat kan worden bijgewerkt.
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;
-
De subquery op
django_content_type
retourneert altijd een enkele waarde? Anders, deCROSS JOIN
kan problemen veroorzaken. -
De eerste CTE
sel
verzamelt de in te voegen rijen. Let op hoe ik overeenkomende kolomnamen kies om dingen te vereenvoudigen. -
In de CTE
del
Ik vermijd het verwijderen van rijen die kunnen worden bijgewerkt. -
In de CTE
up
die rijen worden in plaats daarvan bijgewerkt. -
Dienovereenkomstig vermijd ik het invoegen van rijen die niet eerder zijn verwijderd in de laatste
INSERT
.
Kan eenvoudig worden verpakt in een SQL- of PL/pgSQL-functie voor herhaald gebruik.
Niet veilig voor intensief gelijktijdig gebruik. Veel beter dan de functie die je had, maar nog steeds niet 100% robuust tegen gelijktijdig schrijven. Maar dat is geen probleem volgens uw bijgewerkte informatie.
Het vervangen van de UPDATEs door DELETE en INSERT kan al dan niet een stuk duurder zijn. Intern resulteert elke UPDATE sowieso in een nieuwe rijversie, vanwege de MVCC model .
Snelheid eerst
Als u niet echt geïnteresseerd bent in het behouden van oude rijen, kan uw eenvoudigere aanpak sneller zijn:verwijder alles en voeg nieuwe rijen in. Bovendien bespaart het inpakken in een plpgsql-functie wat planningsoverhead. Uw functie eigenlijk, met een paar kleine vereenvoudigingen en met inachtneming van de hierboven toegevoegde standaardwaarden:
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;
Ik zie zelfs af van het gebruik van concat_ws()
:Het is veilig tegen NULL
waarden en vereenvoudigt code, maar een beetje langzamer dan eenvoudige aaneenschakeling.
Ook:
Het zou sneller zijn om de logica in deze functie op te nemen - als dit de enige keer is dat de trigger nodig is. Anders is het waarschijnlijk niet de moeite waard.