ON DELETE SET NULL और ON DELETE CASCADE को हर कार्रवाई के लिए एक अलग फ़ॉरेन की देकर एक साथ इस्तेमाल करें। यह चरण-दर-चरण मार्गदर्शिका काल्पनिक लेखों के उदाहरणों से दिखाती है कि किसी संग्रह को हटाने पर उसके लेख कैसे सुरक्षित रह सकते हैं, जबकि किसी लेख को हटाने पर उसके अनुभाग हट जाते हैं।

इसका उद्देश्य यह व्यवहार तय करना, Mac के Terminal से हटाने के दोनों तरीकों की पुष्टि करना और node-postgres के साथ जाँच दोहराना है। उदाहरणों में सामान्य फ़ॉरेन की और अस्थायी तालिकाएँ इस्तेमाल की गई हैं, इसलिए हर बार चलाने पर अपनी नमूना पंक्तियाँ होंगी।

हर डिलीट कार्रवाई किस संबंध पर लागू होनी चाहिए?

डिलीट कार्रवाई उस संदर्भित तालिका की फ़ॉरेन की पर लागू होती है। संदर्भित पंक्ति हटने पर SET NULL संदर्भ को साफ़ कर देता है; CASCADE संदर्भित पंक्ति को हटा देता है। PostgreSQL के दस्तावेज़ों में वैकल्पिक संबंधों के लिए SET NULL और आश्रित घटकों के लिए CASCADE को उपयुक्त बताया गया है। Constraints

इस उदाहरण में, एक संग्रह लेखों को समूहित करता है, लेकिन कोई लेख संग्रह के बिना भी मौजूद रह सकता है। एक अनुभाग एक लेख से संबंधित होता है और उसकी कोई स्वतंत्र भूमिका नहीं होती। नमूने के लिए ये स्पष्ट रूप से चुने गए डिज़ाइन निर्णय हैं:

फ़ॉरेन की चुनी गई कार्रवाई अपेक्षित परिणाम
demo_articles.collection_id का संदर्भ demo_collections.collection_id से है ON DELETE SET NULL संग्रह हटाने पर उसके लेख सुरक्षित रहते हैं, लेकिन उनका संग्रह संदर्भ हट जाता है।
demo_sections.article_id का संदर्भ demo_articles.article_id से है ON DELETE CASCADE लेख हटाने पर उसके अनुभाग हट जाते हैं।

परिणामी व्यवहार इस बात पर निर्भर करता है कि हटाना कहाँ से शुरू होता है। संग्रह हटाने पर लेख का collection_id बदलता है; लेख नहीं हटता। इसलिए अनुभागों की फ़ॉरेन की में मौजूद ON DELETE CASCADE के पास प्रतिक्रिया देने के लिए लेख का कोई डिलीट नहीं होता। लेख को सीधे हटाने पर यह कैस्केड सक्रिय होता है। यह दोनों कार्रवाइयों की दस्तावेज़ित परिभाषाओं के अनुरूप है। CREATE TABLE

“संदर्भ साफ़ करें और पंक्ति हटाएँ” को व्यक्त करने के लिए एक ही संबंध पर दो परस्पर विरोधी फ़ॉरेन की न रखें। तय करें कि संदर्भित पंक्ति को बने रहना चाहिए या नहीं, फिर वही कार्रवाई चुनें जो इस निर्णय को लागू करती है।

अपने Mac पर पूरा नमूना बनाएँ

आपको चालू PostgreSQL सर्वर, ऐसा मौजूदा डेटाबेस चाहिए जिससे आप कनेक्ट कर सकें, और Terminal में उपलब्ध psql चाहिए। नीचे दिए गए कमांड मौजूदा स्थानीय postgres नाम के डेटाबेस का उपयोग करते हैं; ज़रूरत हो तो उसकी जगह अपने मौजूदा डेटाबेस का नाम लिखें। इंस्टॉलेशन और सर्वर सेटअप इस मार्गदर्शिका के दायरे में नहीं हैं।

निम्न सामग्री को कार्यशील फ़ोल्डर में setup.sql नाम की सादा-पाठ फ़ाइल के रूप में सहेजें। तीनों तालिकाएँ अस्थायी हैं: सत्र समाप्त होने पर PostgreSQL अस्थायी तालिकाएँ हटा देता है। इस उदाहरण में संदर्भित हर तालिका भी अस्थायी है, क्योंकि PostgreSQL अस्थायी और स्थायी तालिकाओं के बीच फ़ॉरेन की की अनुमति नहीं देता। CREATE TABLE

CREATE TEMP TABLE demo_collections (
    collection_id integer PRIMARY KEY,
    title text NOT NULL
);

CREATE TEMP TABLE demo_articles (
    article_id integer PRIMARY KEY,
    collection_id integer,
    title text NOT NULL,
    CONSTRAINT demo_articles_collection_fk
        FOREIGN KEY (collection_id)
        REFERENCES demo_collections (collection_id)
        ON DELETE SET NULL
);

CREATE TEMP TABLE demo_sections (
    section_id integer PRIMARY KEY,
    article_id integer NOT NULL,
    heading text NOT NULL,
    CONSTRAINT demo_sections_article_fk
        FOREIGN KEY (article_id)
        REFERENCES demo_articles (article_id)
        ON DELETE CASCADE
);

CREATE INDEX demo_articles_collection_idx
    ON demo_articles (collection_id);
CREATE INDEX demo_sections_article_idx
    ON demo_sections (article_id);

INSERT INTO demo_collections (collection_id, title) VALUES
    (1, 'Layout Notes'),
    (2, 'Color Notes');

INSERT INTO demo_articles (article_id, collection_id, title) VALUES
    (101, 1, 'Grid Spacing'),
    (102, 2, 'Color Pairings');

INSERT INTO demo_sections (section_id, article_id, heading) VALUES
    (1001, 101, 'Overview'),
    (1002, 101, 'Examples'),
    (1003, 102, 'Overview');

ध्यान दें कि collection_id में NULL हो सकता है, जबकि अनुभाग तालिका में article_id आवश्यक है। SET NULL कार्रवाई के बाद भी तालिका की अन्य बाधाओं का पालन होना चाहिए। स्पष्ट रूप से बनाए गए इंडेक्स संदर्भित कॉलमों को कवर करते हैं; फ़ॉरेन की घोषित करने पर PostgreSQL ये इंडेक्स अपने-आप नहीं बनाता। Constraints

एक ही सत्र में हटाने के दोनों तरीकों की पुष्टि करें

अगला ब्लॉक verify.sql नाम से, setup.sql के साथ सहेजें। यह पूरी psql स्क्रिप्ट है: \ir सेटअप फ़ाइल को पुष्टि स्क्रिप्ट के सापेक्ष पथ से शामिल करता है। अस्थायी तालिकाओं को उसी सत्र में बनाए रखने के लिए दोनों फ़ाइलें इसी स्क्रिप्ट के ज़रिए चलाएँ। -f विकल्प किसी फ़ाइल को चलाता है, और ON_ERROR_STOP त्रुटि होने पर स्क्रिप्ट का आगे चलना रोकता है। psql

BEGIN;
\ir setup.sql

DELETE FROM demo_collections
WHERE collection_id = 1
RETURNING collection_id;

SELECT a.article_id,
       a.title,
       a.collection_id,
       c.title AS collection_title,
       count(s.section_id) AS section_count
FROM demo_articles AS a
LEFT JOIN demo_collections AS c
    ON c.collection_id = a.collection_id
LEFT JOIN demo_sections AS s
    ON s.article_id = a.article_id
GROUP BY a.article_id, a.title, a.collection_id, c.title
ORDER BY a.article_id;

DELETE FROM demo_articles
WHERE article_id = 101
RETURNING article_id;

SELECT section_id, article_id, heading
FROM demo_sections
ORDER BY section_id;

ROLLBACK;

Terminal में फ़ाइलों वाले फ़ोल्डर पर जाएँ और यह चलाएँ:

psql -X -v ON_ERROR_STOP=1 -d postgres -f verify.sql

पहली क्वेरी के अपेक्षित परिणाम नमूना पंक्तियों से निकाले गए हैं:

लेख संग्रह हटाने के बाद संग्रह ID संग्रह का शीर्षक अनुभागों की संख्या
101 NULL NULL 2
102 2 Color Notes 1

लेख 101 अपने दो अनुभागों के साथ बना रहता है। दूसरी बार हटाने के बाद, अनुभागों की क्वेरी में केवल अनुभाग 1003 आना चाहिए, जो लेख 102 का है। जाँच की यह जोड़ी लेख को सुरक्षित रखने और लेख के साथ उसकी आश्रित पंक्तियों को हटाने के बीच अंतर स्पष्ट करती है।

पुष्टि क्वेरी जानबूझकर LEFT JOIN का उपयोग करती है। PostgreSQL बाईं तालिका की ऐसी पंक्ति बनाए रखता है जिसका कोई मेल नहीं मिलता और दाईं ओर के अनुपलब्ध मानों की जगह NULL देता है। संदर्भ NULL हो जाने के बाद संग्रहों से inner join करने पर लेख 101 छूट जाएगा, जिससे सुरक्षित रखा गया लेख क्वेरी के परिणामों में दिखाई नहीं देगा। Table Expressions

RETURNING क्लॉज़ लक्षित तालिकाओं से हटाई गई पंक्तियों की जानकारी देते हैं। वे कैस्केड से हटाए गए सभी अनुभागों की संयुक्त सूची नहीं देते; उस परिणाम की पुष्टि अलग अनुभाग क्वेरी करती है। DELETE

आख़िरी ROLLBACK प्रदर्शन के लिए किए गए बदलाव रद्द कर देता है। PostgreSQL एक अकेले स्टेटमेंट को भी ट्रांज़ैक्शन मानता है, इसलिए सेटअप, डिलीट, निरीक्षण और सफ़ाई को एक साथ रखने के लिए स्पष्ट ट्रांज़ैक्शन उपयोगी है। Transactions

node-postgres के साथ यही जाँच चलाएँ

JavaScript संस्करण के लिए setup.sql को बिना बदले रखें। इस उदाहरण के लिए Node.js 24 इस्तेमाल करें और कार्यशील फ़ोल्डर में दस्तावेज़ित pg पैकेज इंस्टॉल करें: node-postgres

npm install pg

निम्न पूरा प्रोग्राम verify.mjs नाम से, setup.sql के साथ सहेजें। यह वही काल्पनिक नमूना डेटा पढ़ता है, पुष्टि करता है कि संग्रह हटाने पर दोनों लेख और तीनों अनुभाग सुरक्षित रहते हैं, फिर पुष्टि करता है कि लेख हटाने पर केवल उससे मेल खाने वाले अनुभाग हटते हैं।

node-postgres के अनुसार ट्रांज़ैक्शन के दौरान एक ही कनेक्टेड क्लाइंट इस्तेमाल करना आवश्यक है; प्रोग्राम ऐसा ही करता है। क्वेरी के मान $1 का उपयोग करते हुए दस्तावेज़ित पैरामीटर-अर्रे इंटरफ़ेस से दिए जाते हैं। Transactions, Queries

import pg from 'pg';
import { readFile } from 'node:fs/promises';
import assert from 'node:assert/strict';

const { Client } = pg;
const client = new Client();
const setup = await readFile(
  new URL('./setup.sql', import.meta.url),
  'utf8'
);

await client.connect();
try {
  await client.query('BEGIN');
  await client.query(setup);

  const removed = await client.query(
    'DELETE FROM demo_collections WHERE collection_id = $1 RETURNING collection_id',
    [1]
  );
  assert.deepEqual(removed.rows, [{ collection_id: 1 }]);

  const articles = await client.query(
    'SELECT article_id, collection_id FROM demo_articles ORDER BY article_id'
  );
  assert.deepEqual(articles.rows, [
    { article_id: 101, collection_id: null },
    { article_id: 102, collection_id: 2 }
  ]);

  const before = await client.query(
    'SELECT section_id FROM demo_sections ORDER BY section_id'
  );
  assert.deepEqual(
    before.rows.map(row => row.section_id),
    [1001, 1002, 1003]
  );

  await client.query(
    'DELETE FROM demo_articles WHERE article_id = $1',
    [101]
  );
  const after = await client.query(
    'SELECT section_id FROM demo_sections ORDER BY section_id'
  );
  assert.deepEqual(after.rows, [{ section_id: 1003 }]);

  console.log('Expected SET NULL and CASCADE outcomes verified.');
  await client.query('ROLLBACK');
} catch (error) {
  try {
    await client.query('ROLLBACK');
  } catch (rollbackError) {
    console.error('Rollback failed:', rollbackError.message);
  }
  throw error;
} finally {
  await client.end();
}

इसे उसी मौजूदा स्थानीय डेटाबेस पर चलाएँ:

PGDATABASE=postgres node verify.mjs

new Client() PostgreSQL के कनेक्शन एनवायरनमेंट वैरिएबल पढ़ता है। अपने स्थानीय सर्वर के लिए ज़रूरी कनेक्शन सेटिंग्स को जस का तस रखें; केवल डेटाबेस का नाम बदलने से अलग सर्वर कॉन्फ़िगर नहीं होता। Connecting

किसी पुष्टि के विफल होने पर प्रोग्राम रोलबैक पथ अपनाता है और त्रुटि दिखाता है। सफलता संदेश तभी दिखाई देता है जब परिणामों की सभी जाँचें पास हो जाती हैं। प्रोग्राम जानबूझकर सफलता पर भी रोलबैक करता है, ताकि प्रदर्शन बार-बार चलाया जा सके।

तैयारी के दौरान JavaScript सिंटैक्स की जाँच की गई थी। शोध परिवेश में PostgreSQL एक्ज़ीक्यूटेबल उपलब्ध नहीं था, इसलिए डेटाबेस के उदाहरण चलाने के बजाय दस्तावेज़ों से मिलाकर जाँचे गए; ऊपर दी गई तालिकाएँ अपेक्षित परिणाम दिखाती हैं।

कंपोज़िट फ़ॉरेन की होने पर क्या बदलता है?

मुख्य उदाहरण में एक-कॉलम वाली फ़ॉरेन की हैं। कंपोज़िट फ़ॉरेन की एक और फ़ैसला लेने की ज़रूरत पैदा करती है: क्या हटाने पर संदर्भित सभी कॉलम साफ़ होने चाहिए या केवल वैकल्पिक कॉलम? PostgreSQL 15 में फ़ॉरेन की ON DELETE SET कार्रवाइयों के लिए कॉलम सूची जोड़ी गई; उससे पहले फ़ॉरेन की के सभी कॉलम प्रभावित होते थे। PostgreSQL 15 Release Notes

उदाहरण के लिए, किसी कंपोज़िट लेख संदर्भ में आवश्यक संस्करण पहचानकर्ता और वैकल्पिक संबंधित-लेख पहचानकर्ता हो सकते हैं। PostgreSQL 15 या उसके बाद, ON DELETE SET NULL (related_article_id) वैकल्पिक लेख संदर्भ को साफ़ करते हुए संस्करण पहचानकर्ता को सुरक्षित रख सकता है। कॉलम सूची के बिना SET NULL दोनों कॉलमों पर लागू होता है। यह सिंटैक्स PostgreSQL का एक्सटेंशन है और ON DELETE के लिए उपलब्ध है, ON UPDATE के लिए नहीं। CREATE TABLE

आंशिक रूप से NULL करने के लिए मेल का नियम भी अनुकूल होना चाहिए। डिफ़ॉल्ट MATCH SIMPLE में NULL वाला कंपोज़िट फ़ॉरेन की मान्य है; MATCH FULL संदर्भित मानों में NULL और गैर-NULL का मिश्रण अस्वीकार करता है। इसलिए आवश्यक संस्करण पहचानकर्ता को रखते हुए केवल संबंधित-लेख पहचानकर्ता को NULL करना MATCH FULL के अनुकूल नहीं है। Constraints

PostgreSQL 14 पर, अगर वह संबंध को सही ढंग से दर्शाता है, तो मुख्य उदाहरण की तरह वैकल्पिक संदर्भ के लिए अलग एक-कॉलम स्कीमा इस्तेमाल करें। अगर कंपोज़िट संबंध बनाए रखना ज़रूरी है, तो संदर्भित पंक्ति हटाने से पहले एक ही ट्रांज़ैक्शन में उसके वैकल्पिक संदर्भ को स्पष्ट रूप से साफ़ करें। यह एप्लिकेशन द्वारा प्रबंधित क्रम है, समर्थित कॉलम-सूची सिंटैक्स नहीं।

PostgreSQL 18 में एक और दस्तावेज़ित सीमा है: PERIOD इस्तेमाल करने वाली टेम्पोरल फ़ॉरेन की में CASCADE या SET NULL समर्थित नहीं हैं। जब संबंध में अवधि को कवर करना शामिल न हो, तब यहाँ दिखाई गई डिलीट कार्रवाइयों के लिए सामान्य फ़ॉरेन की समर्थित तरीका हैं। CREATE TABLE

अप्रत्याशित परिणाम का पता कैसे लगाएँ?

निदान के लिए डिलीट की प्रक्रिया के क्रम में आगे बढ़ें:

  1. संदर्भित तालिका की पुष्टि करें। जिन सत्रों में तालिकाएँ मौजूद हैं, उनमें \d demo_articles और \d demo_sections देखें। कॉलम नामों से अंदाज़ा लगाने के बजाय वास्तविक फ़ॉरेन की कार्रवाइयाँ जाँचें। PostgreSQL दस्तावेज़ों में \d को तालिका के विवरण और बाधा नाम देखने का एक तरीका बताया गया है। Modifying Tables
  2. NULL की अनुमति और अन्य बाधाएँ जाँचें। अगर संग्रह हटाने में विफलता आती है, तो पुष्टि करें कि साफ़ किए जा रहे कॉलम में NULL की अनुमति है और अन्य बाधाएँ बनी हुई पंक्ति को स्वीकार करती हैं। SET NULL उन्हें दरकिनार नहीं करता। Constraints
  3. सभी संबंधित फ़ॉरेन की देखें। किसी अतिरिक्त संबंध पर अलग कार्रवाई हो सकती है। कार्रवाई न बताने पर डिफ़ॉल्ट NO ACTION लागू होता है, जो संदर्भ मौजूद रहने पर डिलीट रोक सकता है। CREATE TABLE
  4. जॉइन समझने से पहले मूल तालिका पर क्वेरी करें। अगर लेख वहाँ मौजूद है, तो जॉइन और फ़िल्टर देखें; हो सकता है वे उसके नए NULL संदर्भ वाली पंक्ति को छोड़ रहे हों। Table Expressions
  5. ट्रांज़ैक्शन स्टेटमेंट एक ही क्लाइंट पर चलाएँ। पूल किए गए एप्लिकेशन में पूरे ट्रांज़ैक्शन के लिए एक क्लाइंट लें। node-postgres साफ़ तौर पर सलाह देता है कि ट्रांज़ैक्शन स्टेटमेंट के लिए pool.query() का उपयोग न करें। Transactions

इस लेख मॉडल में स्वीकृति जाँच स्पष्ट है: संग्रह 1 हटाने पर लेख 101 और उसके अनुभाग सुरक्षित रहते हैं, जबकि लेख 101 हटाने पर केवल लेख 102 का अनुभाग बचता है। स्कीमा को ढालते समय इन दोनों जाँचों को साथ रखें, ताकि अपेक्षित सुरक्षा और डिलीट व्यवहार स्पष्ट रहे।

स्रोत