رفتن به محتوای اصلی
بازگشت به وبلاگ

داده و اتصال فنی

تفاوت UPSERT و REPLACE در SQLite؛ چرا فرزند حذف شد؟

تفاوت UPSERT و REPLACE در SQLite را روی دو parent/child اصیل اجرا کنید؛ والد یکسان دیده می‌شود، اما CASCADE در مسیر جایگزینی سابقهٔ فرزند را حذف می‌کند.

تفاوت UPSERT و REPLACE در SQLite می‌تواند فراتر از نامِ دستور باشد. در تعارضِ کلید یکتا، REPLACE ردیفِ قبلیِ متعارض را حذف می‌کند؛ اگر رابطهٔ فرزند ON DELETE CASCADE داشته باشد، سابقهٔ فرزند هم ممکن است حذف شود. در تمرین این صفحه، دو پایگاه حافظه‌ایِ یکسان می‌سازیم: مقدار والد در هر دو عوض می‌شود، اما فرزند فقط در مسیر DO UPDATE باقی می‌ماند.

همان شناسه، لزوماً همان اثر نیست

جدول parent یک ردیف با id برابرِ۱ و name برابرِold دارد. جدول child نیز ردیفِ۱۰ را با parent_id برابرِ۱ و note برابرِhistory نگه می‌دارد. در هر دو اتصال، PRAGMA foreign_keys صریحاً روشن و مقدار خوانده‌شدهٔ آن۱ است. رابطهٔ حذف هم به‌روشنی CASCADE تعریف شده؛ رفتار این مثال متعلق به همین schema است.

دو شاخه از پایگاه‌های مستقل :memory: شروع می‌شوند. بنابراین نتیجهٔ اجرای اول وارد شرایط اجرای دوم نمی‌شود. پیش از تغییر، هم parent و هم child با انتظارِ مستقل مقایسه می‌شوند؛ پس از تغییر هم تمام ردیف‌های این دو جدول را می‌خوانیم. یک پیامِ موفق یا شمارشِ فقطِ والد، اثر روی فرزند را پنهان می‌کرد.

شاخهٔ REPLACE چه می‌کند؟

INSERT OR REPLACE INTO parent VALUES(1,'new')

این دستور روی کلیدِ موجودِ۱ تعارض دارد. در قراردادِ این uniqueness conflict، ردیف قبلی حذف و دادهٔ جدید درج می‌شود. حذفِ والد، عمل CASCADE را روی فرزند فعال می‌کند؛ پس از اجرا parent هنوز یک ردیفِ۱ با name جدید دارد، اما child خالی است. نگاه‌کردن به id والد به‌تنهایی نمی‌گفت که history از بین رفته است.

از این نتیجه نمی‌توان گفت هر کاربرد REPLACE برای هر قید، دقیقاً همین اثر را دارد. رفتار NOT NULL، CHECK یا FOREIGN KEY شرایط جداگانه دارد. این مقاله فقط تعارضِ PRIMARY KEY اعلامی را مقایسه می‌کند؛ تبدیل آن به توصیهٔ جایگزینی همهٔ دستورهای برنامه، از دامنهٔ شاهد بیرون است.

شاخهٔ DO UPDATE چه فرقی دارد؟

INSERT INTO parent VALUES(1,'new')
ON CONFLICT(id) DO UPDATE SET name=excluded.name

در شاخهٔ دوم، conflict target همان id است و فقط name به مقدار ورودیِ excluded.name به‌روز می‌شود. کلید والد عوض نمی‌شود و حذفِ والد هم رخ نمی‌دهد. پس از اجرا، parent برابرِ ردیفِ۱ با name جدید است و child همچنان ردیفِ۱۰ با یادداشت history را دارد. این تفاوت واقعاً در دو اجرای مستقلِ کد دیده می‌شود.

مشاهدهٔ پس از اجراREPLACE نمونهDO UPDATE نمونه
والدid=1 و name=newid=1 و name=new
فرزندهیچ ردیفی نداردردیفِ۱۰ و history حفظ شده
foreign_key_checkبدون مغایرتبدون مغایرت

نداشتن orphan، حفظ سابقه را ثابت نمی‌کند

در هر دو شاخه، PRAGMA foreign_key_check نتیجهٔ خالی می‌دهد. در شاخهٔ اول، رابطهٔ شکسته باقی نمانده چون خود فرزند حذف شده است؛ از خالی‌بودن این بررسی نمی‌توان نتیجه گرفت سابقهٔ موردنیاز کسب‌وکار حفظ شده. آزمون باید علاوه بر قید، انتظارِ ردیف‌های موجود را هم بررسی کند.

اگر فقط تعداد والدها یا موفقیتِ درج را به‌عنوان معیار پذیرش بگیریم، هر دو مسیر ممکن است ظاهراً یکسان دیده شوند. برای این نیاز، قبل و بعدِ فرزندان و هویت منطقی آن‌ها معیار مهم‌تری است. این مسئله با پاک‌سازی رکورد تکراری یا مجوز مشاهدهٔ داده تفاوت دارد؛ هیچ‌کدام از دو دستور، به‌خودی‌خود کنترلِ دسترسی نیست.

مرزِ UPSERT را بشناسیم

مستند SQLite، UPSERT را مرتبط با uniqueness constraint معرفی می‌کند. DO UPDATE راهِ ترمیمِ خودکارِ هر خطای قید یا دادهٔ نامعتبر نیست. در مثال، شرط name غیر NULL است و فقط مقدار مجازِ new نوشته می‌شود؛ رفتار خطای چندردیفی، تغییرِ کلید، triggerها و recursive_triggers در این تمرین اجرا نشده‌اند.

نسخهٔ واقعی SQLite ۳٫۵۳٫۱ و Python ۳٫۱۲٫۱۴ است. هیچ جدولِ CRM، رکورد مشتری یا دادهٔ سایت باز و تغییر داده نمی‌شود. حتی اگر برنامهٔ واقعی شما SQLite داشته باشد، schema، trigger، transaction و قواعدِ نگهداریِ همان برنامه باید جدا بررسی شوند؛ این دو جدول جای آزمونِ مهاجرتِ آن را نمی‌گیرند.

برای تغییرِ واقعی چه شاهدی بخواهیم؟

راهنمای اجرای کلید خارجی روشن‌بودن قید در هر اتصال را می‌سنجد؛ اینجا اثرِ دو دستور با CASCADE را مقایسه می‌کنیم. SAVEPOINT و rollback نیز دامنهٔ برگشتِ تراکنش را جدا توضیح می‌دهد.

برای بررسی اتصال در راهکارهای اپرا، عملیات مجاز، رفتارِ تعارض و انتظارِ حفظ فرزند را همراهِ نمونهٔ ساختگی مشخص کنید. استفاده از SQLite یا داشتنِ عملیات UPSERT در محصول اپرا از این مقاله نتیجه نمی‌شود؛ هدف، فهمیدنِ تفاوتی است که شناسهٔ ظاهراً ثابت می‌تواند پنهان کند.

فایل تمرین و منبع

کد کامل تمرینِ محلی و کنترل‌های آن · نمونه‌های اصیل و نتیجهٔ اجرای واقعیِ همین کد

منابع در 2026-11-04 خوانده شدند؛ این تاریخ، تاریخ انتشار منبع نیست. مثال‌ها و فایل‌های این راهنما آموزشی و ساختهٔ تحریریه‌اند.

برای این نیاز، کدام خدمت مناسب‌تر است؟

تماس و منشی، چت سایت و تحلیل مکالمه سه مسیر متفاوت‌اند. ابتدا خدمت متناسب نیازتان را انتخاب کنید.

انتخاب میان سه خدمتراهنمای شروع با تیم اپرا

برآورد هزینه