تفاوت 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=new | id=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 در محصول اپرا از این مقاله نتیجه نمیشود؛ هدف، فهمیدنِ تفاوتی است که شناسهٔ ظاهراً ثابت میتواند پنهان کند.
فایل تمرین و منبع
کد کامل تمرینِ محلی و کنترلهای آن · نمونههای اصیل و نتیجهٔ اجرای واقعیِ همین کد
- مستند اصلی SQLite، بخش۲؛ تعارض یکتایی، DO UPDATE و excluded؛ بخشهای مرتبط
- مستند اصلی SQLite، بخش REPLACE برای تعارض یکتایی؛ قواعد قیدهای دیگر و trigger از دامنهٔ آزمون جدا شدهاند
منابع در 2026-11-04 خوانده شدند؛ این تاریخ، تاریخ انتشار منبع نیست. مثالها و فایلهای این راهنما آموزشی و ساختهٔ تحریریهاند.