sql >> Database teknologi >  >> RDS >> PostgreSQL

Slet forælder, hvis det ikke er refereret af et andet barn

I PostgreSQL 9.1 eller nyere du kan gøre dette med en enkelt erklæring ved hjælp af en datamodificerende CTE . Dette er generelt mindre fejltilbøjeligt. Det minimerer tidsrammen mellem de to DELETEs, hvor et løbsforhold kunne føre til overraskende resultater med samtidige operationer:

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.

Barnet slettes under alle omstændigheder. Jeg citerer manualen:

Datamodificerende udsagn i WITH udføres nøjagtigt én gang ogaltid til afslutning , uafhængigt af om den primære forespørgsel læser alle (eller faktisk nogen) af deres output. Bemærk, at dette er forskelligt fra reglen for SELECT i WITH :som angivet i det foregående afsnit, udførelse af en SELECT udføres kun så langt som den primære forespørgsel kræver sit output.

Forælderen slettes kun, hvis den ikke har nogen andre børn.
Bemærk den sidste betingelse. I modsætning til hvad man kunne forvente, er dette nødvendigt, da:

Underudsagn i WITH udføres samtidigt med hinanden og med hovedforespørgslen. Derfor, når du bruger data-modificerende udsagn i WITH , den rækkefølge, som de angivne opdateringer faktisk sker i, er uforudsigelig. Alle udsagn udføres med det samme øjebliksbillede (se kapitel 13), så de kan ikke "se" hinandens effekt på måltabellerne.

Fed fremhævelse min.
Jeg brugte kolonnenavnet parent_id i stedet for det ikke-beskrivende id .

Eliminér løbstilstand

For at eliminere mulige løbsforhold nævnte jeg fuldstændigt ovenfor , lås den overordnede række først . Selvfølgelig alle lignende operationer skal følge samme procedure for at få det til at fungere.

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

På denne måde kun én transaktion ad gangen kan låse den samme forælder. Så det kan ikke ske, at flere transaktioner sletter børn af samme forælder, stadig ser andre børn og skåner forælderen, mens alle børn er væk bagefter. (Opdateringer på ikke-nøglekolonner er stadig tilladt med FOR NO KEY UPDATE .)

Hvis sådanne tilfælde aldrig opstår, eller du kan leve med, at det (næsten aldrig) sker - er den første forespørgsel billigere. Ellers er dette den sikre vej.

FOR NO KEY UPDATE blev introduceret med Postgres 9.4. Detaljer i manualen. I ældre versioner, brug den stærkere lås FOR UPDATE i stedet.



  1. Hvordan vælger og optimerer man orakelindekser?

  2. SSIS kan ikke gemme pakker og genstarter Visual Studio

  3. Vil du gemme PHP-array til MySQL?

  4. Oracle Forms i R12/R12.2