IlmHamroh
JavaScript Full-stack/26-qism. SQL va PostgreSQL15/64-dars20 daqiqa
Mundarija (32)

SQL INSERT va RETURNING: PostgreSQL'ga ma'lumot qo'shish va yaratilgan qatorni olish

Qisqacha: INSERT INTO jadval (ustunlar) VALUES (...) bazaga yangi qator qo'shadi. Bir buyruqda bir nechta qator (VALUES (...), (...)) yoki boshqa so'rov natijasini (INSERT … SELECT) yozish mumkin. RETURNING id esa yaratilgan qatorni darhol qaytaradi — yangi id ni bilish uchun ikkinchi so'rov kerak emas. Ko'p qatorli INSERT da bitta qator xato bo'lsa, hech qaysi qator yozilmaydi.

Bu darsda

  • Bitta va ko'p qatorli INSERT yozasiz, ustunlar ro'yxati nega majburiyligini tushunasiz.
  • DEFAULT kalit so'zi va DEFAULT VALUES bilan sukut qiymatlarni ishlatasiz.
  • INSERT … SELECT bilan bir jadvaldan ikkinchisiga (yoki o'ziga) nusxa olasiz.
  • RETURNING bilan yangi id, sukut qiymatlar va ifodalarni bir so'rovda olasiz.
  • vazifalar bazasiga namuna ma'lumot — 8 foydalanuvchi va 40 vazifa yozib, holat 15 ni commit qilasiz.

Oldin bilishingiz kerak: Foreign key va referential actions, CREATE TABLE va cheklovlar, Primary key tanlovi.

1. Nega bu kerak?

Foreign key darsida bazamiz sxemasi tayyor bo'ldi: ikki jadval, cheklovlar, tashqi kalit. Lekin bo'sh. Bo'sh bazada SELECT ni, saralashni, guruhlashni o'rganib bo'lmaydi. Bizga namuna ma'lumot (seed data) kerak — sinash va o'rganish uchun oldindan tayyorlangan qatorlar.

Ikkinchi sabab — ilova. 25-qism dagi vazifalar-api da POST /vazifalar so'rovi kelganda server bazaga bitta qator yozadi. Keyin javobda yangi vazifani qaytarishi kerak — uning id si, yaratilgan_vaqt i bilan. Bu qiymatlarni baza o'zi yaratadi (IDENTITY, DEFAULT now()). Ilova ularni qanday biladi?

Eski usul — ikkita so'rov: avval INSERT, keyin "oxirgi qo'shilganni" SELECT. Bu sekin (ikki marta tarmoq aylanishi) va xavfli: shu orada boshqa foydalanuvchi ham vazifa qo'shsa, "oxirgisi" uniki bo'lib chiqadi. PostgreSQL'da yechim — RETURNING.

sequenceDiagram
  participant I as Ilova
  participant B as PostgreSQL
  Note over I,B: Ikki so'rov (eski usul)
  I->>B: INSERT ...
  B-->>I: INSERT 0 1
  I->>B: SELECT ... oxirgisi?
  B-->>I: id = 41 (balki birovniki)
  Note over I,B: RETURNING bilan
  I->>B: INSERT ... RETURNING id
  B-->>I: id = 41

Diagrammaga qarang: pastdagi usulda bitta aylanish va noaniqlik yo'q.

O'xshatish: navbat chiptasi. Bankka kirasiz, apparat chipta beradi — raqam darhol qo'lingizda. Ikkinchi usul esa chipta bermay, "keyin tablodan oxirgi raqamni qarang" deyishga o'xshaydi. Shu orada yana uch kishi kirgan bo'lsa-chi?

2. INSERT asoslari

2.1 Bitta qator

Bu bo'limdagi misollar holat 15 bazasida (namuna ma'lumot bilan), hammasi BEGIN … ROLLBACK ichida — baza o'zgarmaydi. Kanon chiqishi (PostgreSQL 18.6):

sql
BEGIN;
INSERT INTO vazifalar (egasi_id, matn)
VALUES (8, 'Birinchi vazifam')
RETURNING id, bajarildi;

Natija:

text
BEGIN
 id | bajarildi 
----+-----------
 41 | f
(1 row)

INSERT 0 1

Qismlarga ajratamiz:

Qism Ma'nosi
INSERT INTO vazifalar Qaysi jadvalga
(egasi_id, matn) Qaysi ustunlarga — tartib shu yerda belgilanadi
VALUES (8, 'Birinchi vazifam') Qiymatlar — ustunlar tartibida
RETURNING id, bajarildi Yozilgan qatordan nimani qaytarish

INSERT 0 1 — psql'ning holat qatori. Ikkinchi son — yozilgan qatorlar soni (1). Birinchi son (0) — tarixiy qoldiq, doim 0; e'tibor bermang.

Yozmagan ustunlarimiz-chi? id — IDENTITY yaratdi (41). bajarildi — DEFAULT false (f). yaratilgan_vaqt — DEFAULT now(). Biz faqat foydalanuvchi bergan narsani yozdik, qolganini baza to'ldirdi. Aynan shunday bo'lishi kerak.

Nega id 41? Namuna ma'lumotda 40 ta vazifa bor, keyingi raqam — 41.

Kanon bu darsdagi beshta INSERT ni bitta tranzaksiyada ketma-ket bajargan, oxirida ROLLBACK. Dars ularni mavzu bo'yicha boshqa tartibda ko'rsatadi, shuning uchun id lar sakrab ko'rinadi. Haqiqiy tartib:

Qadam Kanon buyrug'i Olingan id Darsdagi bo'lim
1 BEGIN + bitta qator 41 «Bitta qator»
2 uch qatorli VALUES 42–44 «Bir buyruq — bir nechta qator»
3 INSERT … SELECT (5 qator) 45–49 «So'rov natijasini yozish»
4 apostrofli matn 50 «Matndagi apostrof»
5 DEFAULT VALUES — xato 51 (kuydi) «DEFAULT VALUES»
6 ROLLBACK — baza o'zgarmadi

O'zingiz takrorlasangiz, oxirida ROLLBACK; yozishni unutmang — aks holda namuna ma'lumotga sinov qatorlari qo'shilib qoladi.

2.2 Matndagi apostrof

O'zbekcha matnda apostrof ko'p: o'qish, to'lov. SQL'da esa satr bitta tirnoq (') bilan o'raladi. Matn ichidagi apostrofni ikki marta yozasiz: 'Kitob o''qish'. Bu SQL standartidagi "ekranlash" (escaping).

sql
INSERT INTO vazifalar (egasi_id, matn)
VALUES (8, 'Kitob o''qish')
RETURNING id, upper(matn) AS katta,
          now() - yaratilgan_vaqt AS farq;

Natija (kanon, PostgreSQL 18.6; jadvaldagi 4-qadam — shuning uchun id 50):

text
 id |    katta     |   farq   
----+--------------+----------
 50 | KITOB O'QISH | 00:00:00
(1 row)

INSERT 0 1

Bazaga bitta apostrof yozildi: KITOB O'QISH. Bitta tirnoq bilan ('Kitob o'qish') yozilsa, satr o dan keyin tugaydi va qolgani sintaksis xatosi beradi.

Diqqat: Bu faqat qo'lda yozilgan SQL uchun. Ilovadan kelgan matnni hech qachon SQL satriga yopishtirmang — bu SQL injection eshigi. To'g'ri usul — parametrlar ($1), Parametrli so'rovlar va SQL injection darsida.

RETURNING da ifoda ham bo'lishi mumkin — upper(matn) (katta harflar), ayirma. farq nega aynan nol? now() — tranzaksiya boshlangan vaqt, yaratilgan_vaqt ham shu now() dan olingan. Bu qiziq xususiyatni Ifodalar va o'rnatilgan funksiyalar darsida batafsil ko'ramiz.

2.3 Ustunlar ro'yxatini tashlab ketmang

Ustunlar ro'yxatisiz ham yozish mumkin: INSERT INTO vazifalar VALUES (...). Unda qiymatlar jadvaldagi ustunlar tartibida ketadi: id, egasi_id, matn, …. Biz ishga tushirdik (PG 17.6, holat 15):

sql
INSERT INTO vazifalar VALUES (8, 'Ustun ro''yxatisiz');

Natija:

text
ERROR:  invalid input syntax for type bigint: "Ustun ro'yxatisiz"
LINE 1: INSERT INTO vazifalar VALUES (8, 'Ustun ro''yxatisiz');
                                         ^

Tarjimasi: "bigint turi uchun noto'g'ri kiritish: «Ustun ro'yxatisiz»". 8 soni id ga, matn esa ikkinchi ustun — egasi_id (bigint) ga tushdi. ^ belgisi aynan shu qiymatni ko'rsatadi.

Bu safar xato chiqdi — omadimiz bor. Turlar tasodifan mos kelganda esa ma'lumot jim noto'g'ri ustunga yoziladi. Ertaga kimdir jadvalga yangi ustun qo'shsa, ro'yxatsiz INSERT lar hammasi buziladi. Qoida: ustunlar ro'yxatini doim yozing.

Qiymatlar soni ustunlardan kam bo'lsa ham xato. Biz ishga tushirdik (PG 17.6):

sql
INSERT INTO vazifalar (egasi_id, matn) VALUES (8);

Natija:

text
ERROR:  INSERT has more target columns than expressions
LINE 1: INSERT INTO vazifalar (egasi_id, matn) VALUES (8);
                                         ^

Tarjimasi: "INSERT'da maqsad ustunlar ifodalardan ko'p". Ikki ustun aytdik, bitta qiymat berdik — matn ga qiymat yetmadi.

Tekshirib ko'ring: INSERT INTO vazifalar (matn, egasi_id) VALUES ('Non olish', 3) ishlaydimi?

Javob

Ha. Ustunlar tartibi jadvaldagi tartibga bog'liq emas — siz ro'yxatda qanday yozsangiz, qiymatlar shu tartibda moslanadi. Muhimi — ro'yxat va VALUES bir-biriga mos bo'lsin.

3. Ko'p qatorli INSERT

3.1 Bir buyruq — bir nechta qator

Uchta vazifani uchta INSERT bilan emas, bittasi bilan yozamiz. Kanon (PostgreSQL 18.6), o'sha tranzaksiyada davomi:

sql
INSERT INTO vazifalar (egasi_id, matn) VALUES
  (8, 'Non olish'),
  (8, 'Sut olish'),
  (8, 'Choy damlash')
RETURNING id, matn;

Natija:

text
 id |     matn     
----+--------------
 42 | Non olish
 43 | Sut olish
 44 | Choy damlash
(3 rows)

INSERT 0 3

Qavslar vergul bilan ajratiladi. Har qavsda qiymatlar soni bir xil bo'lishi shart. Aks holda — biz ishga tushirdik (PG 17.6):

sql
INSERT INTO vazifalar (egasi_id, matn)
VALUES (8, 'Bir'), (8, 'Ikki', true);
text
ERROR:  VALUES lists must all be the same length
LINE 2: VALUES (8, 'Bir'), (8, 'Ikki', true);
                            ^

Tarjimasi: "VALUES ro'yxatlari bir xil uzunlikda bo'lishi kerak".

Nega bitta buyruq yaxshi? Har buyruq — ilovadan bazagacha va qaytib bitta tarmoq aylanishi (round trip). Bazada yozish tez, aylanish esa qimmat. 1000 ta qatorni 1000 ta INSERT bilan yozish bir necha barobar sekin. Aniq o'lchovlarni Ommaviy ma'lumot: COPY, batch va cursor darsida qilamiz.

3.2 Hammasi yoki hech biri

Uch qatorning o'rtasidagisi xato bo'lsa nima bo'ladi? Kanon (PostgreSQL 18.6), endi tranzaksiyasiz:

sql
INSERT INTO vazifalar (egasi_id, matn)
VALUES (8, 'Yaxshi'), (8, ''), (8, 'Bu ham yaxshi');

SELECT count(*) AS shahnoza_vazifalari
FROM vazifalar WHERE egasi_id = 8;

Natija (DETAIL qatoridagi vaqt har safar boshqa):

text
ERROR:  new row for relation "vazifalar" violates check constraint "vazifalar_matn_check"
DETAIL:  Failing row contains (53, 8, , f, 2026-10-24 04:55:37.156086+00).
 shahnoza_vazifalari 
---------------------
                   0
(1 row)

Tarjimasi: "vazifalar jadvalidagi yangi qator vazifalar_matn_check tekshiruv cheklovini buzadi". Bo'sh matn ('') — CREATE TABLE darsidagi "matn bo'sh bo'lmasin" qoidasi.

Shahnozada (id 8) bitta ham vazifa yo'q. Birinchi qator — 'Yaxshi' — to'g'ri edi, lekin u ham yozilmadi. Bitta buyruq — bitta butun amal. U yoki to'liq bajariladi, yoki umuman.

DETAIL dagi 53 ga ham qarang. Yozilmagan qatorga ham id ajratildi va u endi qaytmaydi — jadvalda id'lar orasida "teshik" bo'ladi. Bu Primary key tanlovi darsida ko'rgan xususiyat: IDENTITY raqamlari tranzaksiya bekor bo'lsa ham qaytarilmaydi.

Tekshirib ko'ring: 1000 ta vazifani import qilyapsiz. 999 tasi to'g'ri, bittasi bo'sh. Ko'p qatorli bitta INSERT nechta qator yozadi? 1000 ta alohida INSERT (tranzaksiyasiz)-chi?

Javob

Bitta INSERT — 0 qator (hammasi bekor). 1000 ta alohida INSERT — 999 qator, bittasi xato. Qaysi biri yaxshi — vaziyatga bog'liq: "hammasi yoki hech narsa" kerak bo'lsa — birinchisi; "yaroqlilarini yoz, xatolarni hisobotda ko'rsat" kerak bo'lsa — ikkinchisi yoki COPY'ning ON_ERROR rejimi (COPY darsida).

4. DEFAULT bilan ishlash

4.1 DEFAULT kalit so'zi

Ba'zan ustunni ro'yxatda yozasiz, lekin qiymatini baza bersin deysiz. Masalan, bir nechta qatorda ba'zilari uchun vaqt berilgan, ba'zilari uchun — yo'q. Buning uchun DEFAULT kalit so'zi bor. Biz ishga tushirdik (PG 17.6, holat 15). yaratilgan_vaqt = now() — "vaqt hozirgi tranzaksiya vaqtiga tengmi?" degan tekshiruv:

sql
BEGIN;
INSERT INTO vazifalar
  (egasi_id, matn, bajarildi, yaratilgan_vaqt)
VALUES (8, 'Kalit so''z DEFAULT', DEFAULT, DEFAULT)
RETURNING bajarildi, yaratilgan_vaqt = now() AS hozirgi_vaqt;
ROLLBACK;

Natija:

text
BEGIN
 bajarildi | hozirgi_vaqt
-----------+--------------
 f         | t
(1 row)

INSERT 0 1
ROLLBACK

Ikkala ustun sukut qiymatini oldi: bajarildi — false, yaratilgan_vaqt — now(). DEFAULT — qiymat emas, buyruq: "shu ustunning sukut qiymatini qo'y". NULL bilan adashtirmang. NULL yozsangiz, bajarildi ga NULL tushadi va NOT NULL uni rad etadi.

4.2 DEFAULT VALUES

Hamma ustun sukut bo'yicha bo'lsa — qisqa shakl: INSERT INTO jadval DEFAULT VALUES. Bizda egasi_id va matn majburiy, sukut qiymati yo'q. Kanon (PostgreSQL 18.6):

sql
INSERT INTO vazifalar DEFAULT VALUES;

Natija:

text
ERROR:  null value in column "egasi_id" of relation "vazifalar" violates not-null constraint
DETAIL:  Failing row contains (51, null, null, f, 2026-10-24 04:55:37.144374+00).

Tarjimasi: "vazifalar jadvalidagi egasi_id ustunidagi null qiymat NOT NULL cheklovini buzadi". DETAIL — baza yozmoqchi bo'lgan qator: id 51, ikkita null, f, vaqt.

DEFAULT VALUES qachon ishlaydi? Hamma ustunida sukut bor jadvalda: masalan, tashriflar (id, vaqt DEFAULT now()) — "yana bir tashrif bo'ldi" deb yozish.

4.3 id'ni qo'lda yozib bo'lmaydi

GENERATED ALWAYS AS IDENTITY ustuniga qiymat berilsa — biz ishga tushirdik (PG 17.6):

sql
INSERT INTO vazifalar (id, egasi_id, matn)
VALUES (100, 8, 'Qo''lda id');

Natija:

text
ERROR:  cannot insert a non-DEFAULT value into column "id"
DETAIL:  Column "id" is an identity column defined as GENERATED ALWAYS.
HINT:  Use OVERRIDING SYSTEM VALUE to override.

HINT (maslahat) yo'lni ko'rsatadi: OVERRIDING SYSTEM VALUE. Bu zaxiradan tiklash yoki ko'chirish uchun. Oddiy ilova kodida kerak emas. Batafsil — Primary key tanlovi darsida.

5. INSERT … SELECT

5.1 So'rov natijasini yozish

VALUES o'rniga istalgan SELECT bo'lishi mumkin. Uning natijasidagi har qator — yangi yozuv. Kanon (PostgreSQL 18.6): Shahnozaga Azizning bajarilmagan vazifalaridan nusxa.

sql
INSERT INTO vazifalar (egasi_id, matn)
SELECT 8, matn
FROM vazifalar
WHERE egasi_id = 1 AND NOT bajarildi
RETURNING id, matn;

Natija:

text
 id |                       matn                       
----+--------------------------------------------------
 45 | JavaScript kursining 26-qismini boshlash #o'qish
 46 | Portfolio saytiga blog yozish #portfolio
 47 | Oshxonadagi jo'mrakni almashtirish #ta'mir #uy
 48 | SQL JOIN mashqlarini yechish #o'qish
 49 | Velosiped zanjirini moylash
(5 rows)

INSERT 0 5

O'qilishi: "vazifalardan egasi_id = 1 va bajarilmagan (NOT bajarildi) qatorlarni tanla. Har biridan 8 va matn ni olib, vazifalar ga yangi qator qilib yoz". SELECT 8, matn dagi 8 — o'zgarmas qiymat, har qatorda bir xil. WHERE ni keyingi darsda — SELECT va WHERE — to'liq o'rganamiz.

Jadval o'ziga o'zidan nusxa oldi va bu xavfsiz: SELECT buyruq boshlanishidagi holatni ko'radi, yangi yozilgan qatorlarni qayta o'qimaydi. Cheksiz sikl bo'lmaydi.

5.2 Namuna ma'lumot: id'lar qo'lda yozilmaydi

Namuna ma'lumot faylimiz INSERT … SELECT ning foydali shaklini ishlatadi. Vazifalarni yozganda egasining id sini bilmaymiz — uni baza bergan. Biz faqat email'ni bilamiz. Faylning boshi (kanon 003-namuna-asosiy.sql, qatorlar telefonga qisqartirilgan, ikkita vazifa ko'rsatildi):

sql
INSERT INTO foydalanuvchilar (email, ism, yaratilgan_vaqt)
VALUES
  ('aziz@example.com', 'Aziz Karimov',
   '2026-09-01 09:00+05'),
  -- ... yana 7 foydalanuvchi
  ('shahnoza@example.com', 'Shahnoza Ergasheva',
   '2026-10-12 10:00+05');

INSERT INTO vazifalar (egasi_id, matn, bajarildi, yaratilgan_vaqt)
SELECT f.id, v.matn, v.bajarildi, v.vaqt::timestamptz
FROM (VALUES
  ('aziz@example.com',
   'JavaScript kursining 26-qismini boshlash #o''qish',
   false, '2026-10-01 08:30+05'),
  ('aziz@example.com',
   'Postgres''ni Docker''da ko''tarish #o''qish #docker',
   true, '2026-10-01 09:10+05')
  -- ... yana 38 vazifa
) AS v (email, matn, bajarildi, vaqt)
JOIN foydalanuvchilar f ON f.email = v.email
ORDER BY v.vaqt;  -- id'lar vaqt tartibida berilsin

Bu yerda uchta yangi narsa bor. Ularni chuqur o'rganish shart emas, faqat nima qilishini biling:

  • (VALUES …) AS v (email, matn, …) — VALUES ro'yxati vaqtinchalik jadvaldek ishlatildi, ustunlariga nom berildi.
  • JOIN foydalanuvchilar f ON f.email = v.email — har vazifa qatoriga email'i mos foydalanuvchini "ulaydi". Shunda f.id — haqiqiy id. Bu birlashtirish (JOIN) — JOIN'lar darsining mavzusi.
  • v.vaqt::timestamptz — matnni vaqt turiga aylantirish (:: — tur o'zgartirish, Ifodalar darsida).
  • ORDER BY v.vaqt — qatorlar yaratilgan vaqti bo'yicha tartiblanib yoziladi, shuning uchun id lar ham shu tartibda beriladi.

Natija: id hech qayerda qo'lda yozilmagan. Fayl istalgan bazada ishlaydi, id'lar boshqacha bo'lsa ham.

Vaqtlar ham qat'iy: '2026-10-01 08:30+05', now() emas. Shuning uchun darslardagi chiqishlar har qurishda bir xil bo'ladi. +05 — Toshkent vaqti. Bazada u UTC'da saqlanadi va psql UTC'da ko'rsatadi (03:30:00+00) — SQL'da sana va vaqt zonalari darsida buni chuqur ko'ramiz.

Fayl nomida -namuna- bor. Bu kelishuv: bunday fayl faqat ishlab chiqish va sinov bazasiga qo'llanadi, haqiqiy (production) bazaga emas.

5.3 Qo'shimcha: id'lar qaysi tartibda beriladi

Biz ishga tushirgan bazada (PostgreSQL 17.6, holat 15) birinchi uchta vazifa:

sql
SELECT id, egasi_id, matn, bajarildi
FROM vazifalar WHERE id <= 3;

Natija:

text
 id | egasi_id |                       matn                       | bajarildi 
----+----------+--------------------------------------------------+-----------
  1 |        1 | JavaScript kursining 26-qismini boshlash #o'qish | f
  2 |        1 | Postgres'ni Docker'da ko'tarish #o'qish #docker  | t
  3 |        5 | Mijozga taklifnoma yuborish #ish                 | t
(3 rows)

Faylda uchinchi bo'lib Azizning "Bozordan kartoshka…" vazifasi yozilgan. Lekin 3-id Javohirniki (egasi_id 5): uning vazifasi 1-oktabr soat 10:00 da yaratilgan — Azizning bozor vazifasidan (2-oktabr) oldin. ORDER BY v.vaqt ishladi: id'lar fayldagi qator tartibida emas, vaqt tartibida.

ORDER BY siz-chi? Unda JOIN qatorlarni o'zi qulay ko'rgan tartibda qaytaradi va id'lar shunga qarab beriladi. SQL'da "yozilgan tartib" degan narsa kafolatlanmaydi — tartib kerak bo'lsa, uni ORDER BY bilan aniq aytasiz (ORDER BY, LIMIT va OFFSET).

6. RETURNING chuqurroq

6.1 Nima qaytarish mumkin

RETURNING — SELECT ro'yxatiga o'xshaydi, faqat yozilgan qatorlar ustidan:

Shakl Misol
Ustunlar RETURNING id, yaratilgan_vaqt
Hammasi RETURNING *
Ifoda va taxallus RETURNING upper(matn) AS katta

Biz ishga tushirdik (PG 17.6, ROLLBACK bilan) — RETURNING *:

sql
INSERT INTO vazifalar (egasi_id, matn)
VALUES (8, 'Hammasini qaytar')
RETURNING *;

Natija (vaqt — ishga tushirilgan payt):

text
 id | egasi_id |       matn       | bajarildi |        yaratilgan_vaqt        
----+----------+------------------+-----------+-------------------------------
 42 |        8 | Hammasini qaytar | f         | 2026-10-24 06:00:37.616802+00
(1 row)

INSERT 0 1

Ilova uchun bu tayyor javob: POST /vazifalar aynan shu qatorni JSON qilib qaytaradi. RETURNING * qulay, lekin ilova kodida ustunlarni aniq yozish yaxshi — jadvalga keyin qo'shilgan maxfiy ustun (masalan parol hash'i) tasodifan javobga tushmaydi.

RETURNING faqat INSERT da emas — UPDATE va DELETE da ham bor (UPDATE va DELETE xavfsiz). PostgreSQL 18 da RETURNING old.matn, new.matn ham yozish mumkin — o'zgarishdan oldingi va keyingi qiymat. Oddiy INSERT da old — NULL (eski qator yo'q; istisno — UPSERT va MERGE dagi ON CONFLICT … DO UPDATE). Bu imkoniyat 17 da yo'q, biz uni sinamadik — hujjat bo'yicha (postgresql.org/docs/18, INSERT, 2026-10-24).

6.2 Boshqa bazalarda

Bu bo'lim faqat ma'lumot uchun. MySQL'da RETURNING yo'q — u yerda LAST_INSERT_ID() bilan ikkinchi so'rov qilinadi (bitta ulanish ichida xavfsiz). SQLite 3.35 dan beri RETURNING bor. SQL Server'da — OUTPUT. PostgreSQL'dagi RETURNING eng qulaylaridan biri.

Tekshirib ko'ring: INSERT … SELECT … RETURNING id 5 qator yozsa, nechta id qaytadi?

Javob

5 ta — har yozilgan qator uchun bittadan. RETURNING natijasi ham oddiy jadval: bitta qator yozilsa — 1 qator, nol yozilsa — (0 rows).

7. SQL qadami: holat 15 — namuna ma'lumot

7.1 Fayl va qo'llash

sql/003-namuna-asosiy.sql — kanon fayli: 8 foydalanuvchi (Shahnoza — vazifasiz, keyingi darslarda kerak bo'ladi), 40 vazifa, matnda #teg lar (teglarni UPSERT va MERGE darsida alohida jadvalga ajratamiz). Faylni ochib, «Namuna ma'lumot» bo'limidagi tuzilmani ko'ring va qo'llang:

bash
psql -X -1 -v ON_ERROR_STOP=1 \
  -d postgres://postgres@127.0.0.1:5499/vazifalar \
  -f sql/003-namuna-asosiy.sql

Tekshirish — biz ishga tushirdik (PG 17.6, holat 15 noldan qurilgan):

sql
SELECT count(*) AS foydalanuvchilar FROM foydalanuvchilar;
SELECT count(*) AS vazifalar FROM vazifalar;

Natija:

text
 foydalanuvchilar 
------------------
                8
(1 row)

 vazifalar 
-----------
        40
(1 row)

Kanonda har foydalanuvchining vazifalari soni (PostgreSQL 18.6; bu jadvalni chiqargan so'rovni Agregatlar, GROUP BY darsida yozamiz):

text
        ism         | vazifalar | bajarilgan 
--------------------+-----------+------------
 Aziz Karimov       |         9 |          4
 Dilnoza Rahimova   |         6 |          3
 Sardor Tursunov    |         6 |          3
 Madina Yusupova    |         5 |          3
 Javohir Olimov     |         6 |          3
 Gulnora Saidova    |         4 |          2
 Bekzod Nazarov     |         4 |          2
 Shahnoza Ergasheva |         0 |          0
(8 rows)

Diqqat: Faylni ikkinchi marta qo'llasangiz — duplicate key value violates unique constraint "foydalanuvchilar_email_key". -1 tufayli hech narsa yozilmaydi. Bazani noldan qurish — PostgreSQL o'rnatish va ulanish darsidagi tartib: DROP DATABASE, CREATE DATABASE, keyin 001, 002, 003 ketma-ket.

7.2 Commit

bash
git add sql/003-namuna-asosiy.sql
git commit -m "db: namuna ma'lumot — 8 foydalanuvchi, 40 vazifa (003-namuna)"

Holat 15 — keyingi oltita darsning bazasi. SELECT, saralash, UPDATE, NULL va guruhlashni shu ma'lumotda o'rganamiz.

8. Ko'p uchraydigan xatolar

Xato Sabab Davosi
syntax error at or near "qish" Matndagi apostrof ekranlanmagan 'o''qish'
invalid input syntax for type bigint Ustunlar ro'yxati yo'q yoki tartib noto'g'ri Ro'yxatni doim yozing
INSERT has more target columns than expressions Qiymat kam Ro'yxat va VALUES ni tenglang
VALUES lists must all be the same length Qavslarda qiymatlar soni har xil Har qatorda bir xil son
cannot insert a non-DEFAULT value into column "id" IDENTITY ALWAYS ga qiymat id ni ro'yxatdan olib tashlang
violates not-null constraint Majburiy ustun berilmadi Ustunni yozing yoki DEFAULT qo'shing
Ko'p qatorli INSERT dan hech narsa yozilmadi Bitta qator xato — hammasi bekor Xato qatorni toping (DETAIL)

9. Mashqlar

1-mashq (oson): natijani toping

  • INSERT … VALUES (1,'a'), (1,'b'), (1,'c') muvaffaqiyatli bo'lsa, psql holat qatori: INSERT 0
  • SQL'da Ali'ning kitobi matni qanday yoziladi: 'Ali ning kitobi'
  • Ko'p qatorli INSERT ning 5 qatoridan bittasi CHECK ni buzdi. Nechta qator yoziladi:
  • Hamma ustuni sukut bo'yicha qator qo'shadigan qisqa shakl: INSERT INTO t
Yechim
  • 3 — ikkinchi son yozilgan qatorlar soni.
  • '' — apostrof ikki marta: 'Ali''ning kitobi'.
  • 0 — bitta buyruq butunligicha bajariladi yoki umuman bajarilmaydi.
  • DEFAULT VALUES — faqat hamma majburiy ustunda sukut qiymat bo'lsa ishlaydi.

2-mashq (o'rta): Gulnoraga uch vazifa

Holat 15 bazasida Gulnora'ning (id 6) uchta vazifasini bitta INSERT bilan qo'shing: "Choyxonaga joy band qilish", "Ota-onamga qo'ng'iroq #oila", "Click orqali internet to'lovi #to'lov". Yaratilgan id va matn ni qaytaring. So'ng Gulnora'ning vazifalari sonini chiqaring (count(*) — SQL tili darsida ko'rgansiz).

Yechim
sql
INSERT INTO vazifalar (egasi_id, matn) VALUES
  (6, 'Choyxonaga joy band qilish'),
  (6, 'Ota-onamga qo''ng''iroq #oila'),
  (6, 'Click orqali internet to''lovi #to''lov')
RETURNING id, matn;

SELECT count(*) AS gulnora_vazifalari
FROM vazifalar WHERE egasi_id = 6;

Natija (biz ishga tushirdik, PG 17.6, psql -q — holat qatorlari yashirin):

text
 id |                 matn                  
----+---------------------------------------
 41 | Choyxonaga joy band qilish
 42 | Ota-onamga qo'ng'iroq #oila
 43 | Click orqali internet to'lovi #to'lov
(3 rows)

 gulnora_vazifalari 
--------------------
                  7
(1 row)

Gulnora'da 4 ta vazifa bor edi, endi 7. qo''ng''iroq da ikkita apostrof — har biri ikki martadan.

3-mashq (qiyin): email bo'yicha nusxa

Shahnoza (shahnoza@example.com) Dilnoza'dan (egasi_id 2) uning bajarilmagan vazifalarini o'ziga ko'chirib olmoqchi. Bitta INSERT … SELECT yozing. Shart: Shahnoza'ning id sini qo'lda yozmang — email bo'yicha toping. Yangi id, egasi_id va matn ni qaytaring.

Ishora: FROM foydalanuvchilar f, vazifalar v — ikki jadvalning har qator juftligi; WHERE da kerakli juftliklarni qoldiring (f.email = … va v.egasi_id = 2 …). Bu namuna fayldagi JOIN ning sodda shakli.

Yechim
sql
INSERT INTO vazifalar (egasi_id, matn)
SELECT f.id, v.matn
FROM foydalanuvchilar f, vazifalar v
WHERE f.email = 'shahnoza@example.com'
  AND v.egasi_id = 2
  AND NOT v.bajarildi
RETURNING id, egasi_id, matn;

Natija (biz ishga tushirdik, PG 17.6, psql -q):

text
 id | egasi_id |                   matn                    
----+----------+-------------------------------------------
 41 |        8 | IELTS writing task 2 mashqi #ingliz
 42 |        8 | Dugonamning tug'ilgan kuniga sovg'a #oila
 43 |        8 | Rezyume yangilash #ish
(3 rows)

f dan faqat Shahnoza qatori, v dan faqat Dilnoza'ning 3 ta bajarilmagan vazifasi qoldi: 1 × 3 = 3 juftlik. Email noto'g'ri yozilsa, f dan nol qator qoladi va INSERT 0 0 — xato emas, lekin hech narsa yozilmaydi. Shuning uchun RETURNING foydali: nima yozilganini darhol ko'rasiz.

4-mashq: SQL qadami

sql/003-namuna-asosiy.sql ni loyihangizga qo'shing (kanon fayli), bazaga qo'llang va count(*) bilan 8 va 40 ni tekshiring. So'ng commit qiling («SQL qadami» bo'limidagi buyruq).

Yechim

Kutilgan: foydalanuvchilar 8, vazifalar 40. Agar 0 chiqsa — fayl xato bilan to'xtagan (-1 hammasini bekor qiladi); psql chiqishidagi ERROR qatorini o'qing. Ko'pincha sabab — fayl ikkinchi marta qo'llangan (email takrori) yoki 002 qo'llanmagan bazada egasi_id noto'g'ri. Toza yo'l — bazani noldan qurish.

10. Real ishda

  • API'da yaratish — POST so'rovi deyarli doim INSERT … RETURNING bilan yoziladi: yangi resurs id'si va server maydonlari bitta so'rovda olinadi, javob 201 Created bilan qaytadi. Node'dan bazaga ulanishni node-postgres asoslari darsida yozamiz.
  • Seed fayllari — har jamoada bor: seed.sql, prisma/seed.ts. Qoida bir xil: qat'iy qiymatlar, id'lar qo'lda yozilmaydi, prod'ga tushmaydi.
  • Ommaviy yozish — import, migratsiya, log yig'ish. Ko'p qatorli INSERT va COPY farqi ming barobargacha yetishi mumkin (COPY, batch va cursor).
  • "Mavjud bo'lsa — yangila" — INSERT … ON CONFLICT (UPSERT va MERGE), takror email kabi vaziyatlar uchun.
  • Intervyu savoli: "Yangi qatorning id'sini qanday olasiz?" — PostgreSQL'da to'g'ri javob RETURNING, SELECT max(id) emas.

Xulosa

  • INSERT INTO jadval (ustunlar) VALUES (...) — ustunlar ro'yxatini doim yozing; matndagi apostrof — ''.
  • Ko'p qatorli VALUES (...), (...) — bitta aylanish, bitta butun amal: bitta xato qator hammasini bekor qiladi.
  • DEFAULT — "sukut qiymatni qo'y"; NULL emas. DEFAULT VALUES — hamma ustun sukut bo'yicha.
  • INSERT … SELECT — so'rov natijasini yozish; namuna faylda id'lar email orqali topiladi.
  • RETURNING — yaratilgan id, sukut qiymatlar va ifodalar bir so'rovda; ikkinchi SELECT kerak emas.
  • Holat 15: 8 foydalanuvchi, 40 vazifa — keyingi darslar shu ma'lumotda.

Keyingi dars: SELECT va WHERE — 40 vazifadan kerakli qatorlarni tanlaymiz: AND/OR ustuvorligi, IN, BETWEEN, LIKE/ILIKE va DISTINCT ON.

Manbalar

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
SQL INSERT va RETURNING: PostgreSQL'ga ma'lumot qo'shish va yaratilgan qatorni olish — IlmHamroh