UPSERT — INSERT ON CONFLICT DO UPDATE / DO NOTHING

~15 daq · PostgreSQL · DDL va DML

Foydalanuvchi har kuni tizimga kiradi. Bugungi kirish statistikasini saqlash kerak. Qoida oddiy:

  • Bugun birinchi marta kirsa — yangi qator qo'sh
  • Bugun allaqachon kirgan bo'lsa — kirish sonini oshir

Buni oddiy INSERT bilan qilsangiz:

INSERT INTO statistika (foydalanuvchi_id, sana, kirish_soni)
VALUES (1, '2024-03-15', 1);
-- Ikkinchi marta:
-- ERROR: duplicate key value violates unique constraint

INSERT xato beradi — bu foydalanuvchining bugungi yozuvi allaqachon bor. Yechim uchun avval SELECT, keyin shart bo'yicha INSERT yoki UPDATE yozish — noqulay va ikki so'rov. UPSERT ikkalasini bitta so'rovda bajaradi.


UPSERT nima?

UPSERT — "UPDATE or INSERT" ning qisqartmasi. Yozuv bo'lmasa — qo'shadi, bo'lsa — yangilaydi.

Hayotiy o'xshatma

Telefon daftari. Yangi kontakt qo'shmoqchisiz. Agar bu raqam allaqachon bor bo'lsa — ismini yangilaysiz. Yo'q bo'lsa — yangi qator qo'shasiz. Ikki holat, bitta harakat.


ON CONFLICT DO UPDATE

INSERT INTO statistika (foydalanuvchi_id, sana, kirish_soni)
VALUES (1, '2024-03-15', 1)
ON CONFLICT (foydalanuvchi_id, sana)       -- ← bu juft allaqachon bormi?
DO UPDATE SET
    kirish_soni = statistika.kirish_soni + 1;  -- ← bor bo'lsa — yangilash

EXCLUDED — kiritilmoqchi bo'lgan yangi qiymatlar. statistika — mavjud jadval:

INSERT INTO foydalanuvchilar (id, ism, email)
VALUES (1, 'Alibek', 'alibek@mail.com')
ON CONFLICT (id)
DO UPDATE SET
    ism   = EXCLUDED.ism,    -- ← yangi kelgan ism
    email = EXCLUDED.email;  -- ← yangi kelgan email

To'liqroq misol — mahsulot narxlarini sinxronlashtirish:

INSERT INTO mahsulotlar (kod, nom, narx)
VALUES
    ('PRD001', 'Noutbuk',   8500000),
    ('PRD002', 'Telefon',   3200000),
    ('PRD003', 'Quloqchin',  450000)
ON CONFLICT (kod)
DO UPDATE SET
    nom  = EXCLUDED.nom,
    narx = EXCLUDED.narx;
-- Bor bo'lsa — nom va narx yangilanadi
-- Yo'q bo'lsa — yangi qator qo'shiladi

ON CONFLICT DO NOTHING

To'qnashuv bo'lsa — e'tibor bermay o'tib ketadi, xato bermaydi:

INSERT INTO teglar (nom)
VALUES ('javascript')
ON CONFLICT (nom) DO NOTHING;
-- 'javascript' allaqachon bo'lsa — hech narsa bo'lmaydi, xato yo'q

Qaysi holda foydali? Ko'p ma'lumot import qilayotganda — ba'zilari allaqachon bor, ba'zilari yangi. Bor bo'lganlarini o'tkazib, yangilarini qo'shish kerak:

INSERT INTO shaharslar (kod, nom)
SELECT kod, nom FROM vaqtinchalik_jadval
ON CONFLICT (kod) DO NOTHING;

ON CONFLICT — qaysi constraint ga

ON CONFLICT qaysi ustun yoki constraint da to'qnashuv bo'lishini aniq ko'rsatishi kerak:

-- Ustun nomi bilan
ON CONFLICT (email)

-- Bir nechta ustun (composite unique)
ON CONFLICT (foydalanuvchi_id, sana)

-- Constraint nomi bilan
ON CONFLICT ON CONSTRAINT foydalanuvchilar_email_key

Tez-tez uchraydigan xatolar

Constraint ko'rsatilmagan ustun

ON CONFLICT faqat UNIQUE yoki PRIMARY KEY bo'lgan ustunlar uchun ishlaydi:

-- ❌ — 'ism' ustunida UNIQUE yo'q
ON CONFLICT (ism) DO UPDATE SET ...
-- ERROR: there is no unique or exclusion constraint matching the ON CONFLICT specification

-- ✅ — avval UNIQUE qo'shing
ALTER TABLE foydalanuvchilar ADD CONSTRAINT unique_ism UNIQUE (ism);

DO UPDATE da jadval nomini unutish

-- ❌
DO UPDATE SET kirish_soni = kirish_soni + 1
-- Bu qaysi kirish_soni — yangi keladiganmi yoki mavjudmi? Noaniq.

-- ✅ — jadval nomi bilan aniq ko'rsating
DO UPDATE SET kirish_soni = statistika.kirish_soni + 1

Xulosa

UPSERT — bitta so'rovda: yozuv yo'q bo'lsa qo'shadi, bor bo'lsa yangilaydi.

INSERT INTO jadval (...)
VALUES (...)
ON CONFLICT (ustun)
DO UPDATE SET ustun = EXCLUDED.ustun;  -- yangilash

-- yoki
DO NOTHING;                            -- e'tibor bermay o'tish

EXCLUDED — kiritilmoqchi bo'lgan yangi qiymatlar. Mavjud jadval qiymatlari esa jadval nomi orqali olinadi.


Keyingi dars

11-dars: Aggregate funksiyalar, GROUP BY, HAVING, FILTER