PostgreSQL
 sql >> Baza danych >  >> RDS >> PostgreSQL

Usuń rodzica, jeśli nie odwołuje się do niego żadne inne dziecko

W PostgreSQL 9.1 lub nowszym możesz to zrobić za pomocą jednej instrukcji, używając modyfikacji danych CTE . Jest to generalnie mniej podatne na błędy. minimalizuje przedział czasowy między dwoma DELETE, w których warunki wyścigu może prowadzić do zaskakujących wyników przy równoczesnych operacjach:

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   -- !
   );

Skrzypce SQL.

W każdym przypadku dziecko jest usuwane. Cytuję instrukcję:

Instrukcje modyfikujące dane w WITH są wykonywane dokładnie raz izawsze do końca , niezależnie od tego, czy zapytanie podstawowe odczytuje wszystkie (lub w rzeczywistości dowolne) ich dane wyjściowe. Zauważ, że różni się to od reguły SELECT w WITH :jak podano w poprzedniej sekcji, wykonanie SELECT jest przenoszony tylko tak daleko, jak podstawowe zapytanie wymaga jego wyjścia.

Rodzic jest usuwany tylko wtedy, gdy nie ma innych dzieci.
Zwróć uwagę na ostatni warunek. Wbrew pozorom jest to konieczne, ponieważ:

Podzgłoszenia w WITH są wykonywane równocześnie ze sobą i z głównym zapytaniem. Dlatego podczas używania instrukcji modyfikujących dane w WITH , kolejność, w jakiej faktycznie występują określone aktualizacje, jest nieprzewidywalna. Wszystkie instrukcje są wykonywane z tą samą migawką (patrz rozdział 13), więc nie mogą "zobaczyć" innych efektów na tabelach docelowych.

Pogrubiony nacisk na kopalnię.
Użyłem nazwy kolumny parent_id w miejsce nieopisowego id .

Wyeliminuj sytuację wyścigową

Aby wyeliminować możliwe warunki wyścigu, wspomniałem powyżej całkowicie , zablokuj wiersz nadrzędny pierwszy . Oczywiście wszystkie podobne operacje muszą przebiegać według tej samej procedury, aby działały.

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   -- !
   );

W ten sposób tylko jeden Transakcja na raz może zablokować tego samego rodzica. Nie może więc zdarzyć się, że wielokrotne transakcje usuną dzieci tego samego rodzica, nadal zobaczą inne dzieci i oszczędzą rodzica, podczas gdy wszystkie dzieci znikną później. (Aktualizacje w kolumnach bez klucza są nadal dozwolone za pomocą FOR NO KEY UPDATE .)

Jeśli takie przypadki nigdy się nie zdarzają lub można z tym żyć (prawie nigdy) - pierwsze zapytanie jest tańsze. W przeciwnym razie jest to bezpieczna ścieżka.

FOR NO KEY UPDATE został wprowadzony wraz z Postgresem 9.4. Szczegóły w instrukcji. W starszych wersjach użyj mocniejszego zamka FOR UPDATE zamiast tego.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Big Data z PostgreSQL i Apache Spark

  2. Najlepsze narzędzia GUI dla PostgreSQL

  3. Zarządzanie wysoką dostępnością PostgreSQL – Część I:Automatyczne przełączanie awaryjne PostgreSQL

  4. Django + Psycopg2:InterfaceError:obsługiwany tylko protokół 3

  5. Unikanie zakleszczeń PostgreSQL podczas wykonywania zbiorczych operacji aktualizacji i usuwania