PostgreSQL
 sql >> база данни >  >> RDS >> PostgreSQL

Изтрийте родител, ако не е посочен от друго дете

В PostgreSQL 9.1 или по-нова версия можете да направите това с едно изявление, като използвате CTE за модифициране на данни . Това обикновено е по-малко податливо на грешки. Томинимизира времевата рамка между двете ИЗТРИВАНЕ, в които есъстезанието може да доведе до изненадващи резултати при едновременни операции:

WITH del_child AS (
    DELETE FROM child
    WHERE  child_id = 1
    RETURNING parent_id, child_id
    )
DELETE FROM parent p
USING  del_child x
WHERE  p.parent_id = x.parent_id
AND    NOT EXISTS (
   SELECT 1
   FROM   child c
   WHERE  c.parent_id = x.parent_id
   AND    c.child_id <> x.child_id   -- !
   );

SQL Fiddle.

При всички случаи детето се изтрива. Цитирам ръководството:

Операции за модифициране на данни в WITH се изпълняват точно веднъж ивинаги до завършване , независимо от това дали първичната заявка чете продажбата (или изобщо всяка) от техния изход. Забележете, че това е различно от правилото за SELECT в WITH :както е посочено в предишния раздел, изпълнение на SELECT се пренася само доколкото първичната заявка изисква своя изход.

Родителят се изтрива само ако няма други деца.
Обърнете внимание на последното условие. Противно на това, което може да се очаква, това е необходимо, тъй като:

Подизявленията в WITH се изпълняватедновременно един с друг и с основната заявка. Следователно, когато използвате изрази за модифициране на данни в WITH , редът, в който действително се случват посочените актуализации, е непредсказуем. Всички оператори се изпълняват с една и съща моментна снимка (вижте Глава 13), така че те не могат да „виждат“ ефектите на другия върху целевите таблици.

Удебелен акцент моето.
Използвах името на колоната parent_id на мястото на неописателния id .

Елиминиране на състоянието на състезанието

За да премахна възможните условия на състезанието, споменах по-горе напълно , заключете родителския ред първи . Разбира се, всички подобни операции трябва да следват същата процедура, за да работят.

WITH lock_parent AS (
   SELECT p.parent_id, c.child_id
   FROM   child  c
   JOIN   parent p ON p.parent_id = c.parent_id
   WHERE  c.child_id = 12              -- provide child_id here once
   FOR    NO KEY UPDATE                -- locks parent row.
   )
 , del_child AS (
   DELETE FROM child c
   USING  lock_parent l
   WHERE  c.child_id = l.child_id
   )
DELETE FROM parent p
USING  lock_parent l
WHERE  p.parent_id = l.parent_id
AND    NOT EXISTS (
   SELECT 1
   FROM   child c
   WHERE  c.parent_id = l.parent_id
   AND    c.child_id <> l.child_id   -- !
   );

По този начин само един транзакция в даден момент може да заключи един и същ родител. Така че не може да се случи, че множество транзакции изтриват деца на един и същи родител, все още виждат други деца и щадят родителя, докато всички деца са изчезнали след това. (Актуализации на неключови колони все още са разрешени с FOR NO KEY UPDATE .)

Ако такива случаи никога не се случват или можете да живеете с това (едва ли някога) да се случи - първата заявка е по-евтина. В противен случай това е защитеният път.

FOR NO KEY UPDATE беше представен с Postgres 9.4. Подробности в ръководството. В по-старите версии използвайте по-силното заключване FOR UPDATE вместо това.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Компилиране на PL/Proxy с PostgresPlus Advance Server 9.1

  2. Търсене в пълен текст от PostgreSQL 8.3

  3. Обяснена команда за PostgreSQL VALUES

  4. Как да активирате SSL в PostgreSQL

  5. Как да нулирам потребителската парола по подразбиране на postgresql 9.2 (обикновено „postgres“) на mac os x 10.8.2?