Stored Procedure — Function dan farqi, CALL
Muammodan boshlaylik
Hisobdan hisobga pul o'tkazish kerak. Ikkita operatsiya: biridan yeching, ikkinchisiga qo'shing. Agar birontasi muvaffaqiyatsiz bo'lsa — ikkalasi ham bekor bo'lishi kerak.
Funksiya bilan yozsangiz — muammo chiqadi. Funksiya ichida COMMIT va ROLLBACK yozib bo'lmaydi. Funksiya bitta tranzaksiya ichida ishlaydi, uni o'zi boshqara olmaydi.
Aynan shu holat uchun Stored Procedure yaratilgan: u tranzaksiyani o'zi boshqaradi — ichida COMMIT va ROLLBACK yozish mumkin.
Function va Procedure — asosiy farq
Ikkisi juda o'xshash — lekin bir muhim farqi bor.
Function: Procedure:
────────────────────────────── ──────────────────────────────
Qiymat qaytaradi Qiymat qaytarmaydi
SELECT bilan chaqiriladi CALL bilan chaqiriladi
Tranzaksiyani boshqara olmaydi COMMIT / ROLLBACK yoza oladi
SQL so'rov ichida ishlatiladi Mustaqil ishlaydi
Hayotiy o'xshatma: funksiya — kalkulyator. Siz raqam berasiz, u hisoblaydi, natija qaytaradi. Procedure — muhandis. U ish qiladi, moslamalarni sozlaydi, jarayonni boshqaradi — lekin qo'lida raqam ushlayotgani yo'q.
SELECT chegirma_hisobla(1000); ← function, natija qaytaradi
CALL pul_otkazish(1, 2, 50000); ← procedure, ish qiladi
Procedure yaratish
CREATE OR REPLACE PROCEDURE pul_otkazish(
p_kimdan INT,
p_kimga INT,
p_summa NUMERIC
)
LANGUAGE plpgsql
AS $
BEGIN
-- 1. Balansni tekshirish
IF (SELECT balans FROM hisoblar WHERE id = p_kimdan) < p_summa THEN
RAISE EXCEPTION 'Balans yetarli emas';
END IF;
-- 2. Pul yechish
UPDATE hisoblar
SET balans = balans - p_summa
WHERE id = p_kimdan;
-- 3. Pul qo'shish
UPDATE hisoblar
SET balans = balans + p_summa
WHERE id = p_kimga;
-- 4. Ikkalasi bajarildi — tasdiqlash
COMMIT;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
RAISE; -- xatoni yuqoriga uzatish
END;
$;
Chaqirish:
CALL pul_otkazish(1, 2, 50000);
COMMIT va ROLLBACK — procedure ning kuchi
Function ichida tranzaksiya boshqaruvi yo'q — u tashqaridan boshlangantranzaksiyada ishlaydi. Procedure esa o'zi boshqaradi.
Bu qachon kerak? Bir procedure ichida bir nechta mustaqil tranzaksiya kerak bo'lganda. Masalan: mingta foydalanuvchiga xabar yuborish. Biri muvaffaqiyatsiz bo'lsa — qolganlarini bekor qilmaslik kerak.
CREATE OR REPLACE PROCEDURE ommaviy_xabar_yuborish()
LANGUAGE plpgsql
AS $
DECLARE
foydalanuvchi RECORD;
yuborildi INT := 0;
xato INT := 0;
BEGIN
FOR foydalanuvchi IN SELECT id, email FROM foydalanuvchilar LOOP
BEGIN
-- Har bir foydalanuvchi uchun alohida urinish
INSERT INTO xabarlar (foydalanuvchi_id, matn)
VALUES (foydalanuvchi.id, 'Yangiliklar chiqdi!');
yuborildi := yuborildi + 1;
COMMIT; -- ← har muvaffaqiyatli xabar alohida saqlanadi
EXCEPTION
WHEN OTHERS THEN
xato := xato + 1;
ROLLBACK; -- ← faqat shu xabar bekor, qolganlar saqlanib qoladi
END;
END LOOP;
RAISE NOTICE 'Yuborildi: %, Xato: %', yuborildi, xato;
END;
$;
Agar bu function bilan yozilganida — bitta xato hammani bekor qilardi. Procedure — har xabarni mustaqil saqlaydi.
OUT parametrlar — natija qaytarish
Procedure qiymat qaytarmaydi, lekin OUT parametrlar orqali natijani tashqariga chiqarish mumkin:
CREATE OR REPLACE PROCEDURE hisobot_yaratish(
p_yil INT,
OUT p_jami NUMERIC,
OUT p_buyurtmalar BIGINT
)
LANGUAGE plpgsql
AS $
BEGIN
SELECT
SUM(summa),
COUNT(*)
INTO p_jami, p_buyurtmalar
FROM buyurtmalar
WHERE EXTRACT(YEAR FROM sana) = p_yil;
END;
$;
Chaqirish va natijani olish:
-- Natijani o'zgaruvchiga olish
DO $
DECLARE
jami NUMERIC;
buyurtmalar BIGINT;
BEGIN
CALL hisobot_yaratish(2024, jami, buyurtmalar);
RAISE NOTICE 'Jami: %, Buyurtmalar: %', jami, buyurtmalar;
END;
$;
INOUT parametr — kirish va chiqish
INOUT — parametr ham kirish, ham chiqish sifatida ishlaydi. Qiymat bilan kiradi, o'zgartirilgan qiymat bilan chiqadi:
CREATE OR REPLACE PROCEDURE soliq_qosh(INOUT narx NUMERIC, soliq_foiz NUMERIC DEFAULT 12)
LANGUAGE plpgsql
AS $
BEGIN
narx := narx * (1 + soliq_foiz / 100.0);
END;
$;
DO $
DECLARE
men_narxi NUMERIC := 100000;
BEGIN
CALL soliq_qosh(men_narxi);
RAISE NOTICE 'Soliqlari bilan: %', men_narxi; -- 112000
END;
$;
Function vs Procedure — qaysi birini tanlash
Function tanlang:
→ Hisoblash kerak, natija kerak (SELECT bilan chaqirish)
→ SQL so'rov ichida ishlatish (WHERE, JOIN, SELECT)
→ Qiymat qaytarish muhim
Procedure tanlang:
→ Ish bajarish kerak, natija shart emas
→ Tranzaksiyani o'zi boshqarish kerak (COMMIT/ROLLBACK)
→ Uzoq jarayon, bir necha bosqich
→ Ommaviy operatsiyalar (batch processing)
Shubha bo'lsa — Function. Ko'pchilik holatlarda Function yetarli. Procedure faqat tranzaksiyani o'zi boshqarish kerak bo'lganda kerak.
Amaliy misol — buyurtma yakunlash
Buyurtma yakunlash jarayoni: balansni kamaytirish, buyurtma holatini yangilash, tarix yozish — va agar biri muvaffaqiyatsiz bo'lsa, faqat o'sha bosqichni qaytarish:
CREATE OR REPLACE PROCEDURE buyurtma_yakunla(
p_buyurtma_id INT,
p_summa NUMERIC
)
LANGUAGE plpgsql
AS $
DECLARE
v_foydalanuvchi_id INT;
BEGIN
-- Buyurtma kimga tegishli
SELECT foydalanuvchi_id INTO v_foydalanuvchi_id
FROM buyurtmalar WHERE id = p_buyurtma_id;
IF NOT FOUND THEN
RAISE EXCEPTION 'Buyurtma topilmadi: %', p_buyurtma_id;
END IF;
-- Balansdan yechish
UPDATE hisoblar
SET balans = balans - p_summa
WHERE foydalanuvchi_id = v_foydalanuvchi_id
AND balans >= p_summa; -- yetarli bo'lsagina
IF NOT FOUND THEN
RAISE EXCEPTION 'Balans yetarli emas';
END IF;
-- Buyurtma holatini yangilash
UPDATE buyurtmalar
SET holat = 'paid', to_langan_sana = NOW()
WHERE id = p_buyurtma_id;
-- Tarix
INSERT INTO to_lovlar (buyurtma_id, summa, sana)
VALUES (p_buyurtma_id, p_summa, NOW());
COMMIT; -- hammasi muvaffaqiyatli
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
RAISE NOTICE 'Xato: %', SQLERRM;
RAISE;
END;
$;
Tez-tez uchraydigan xatolar
Procedure ni SELECT bilan chaqirishga urinish:
-- ❌ — procedure SELECT bilan chaqirilmaydi
SELECT pul_otkazish(1, 2, 50000);
-- ERROR: pul_otkazish() is a procedure
-- ✅ — CALL bilan
CALL pul_otkazish(1, 2, 50000);
Function ichida COMMIT yozishga urinish:
-- ❌ — function ichida transaction boshqarish mumkin emas
CREATE FUNCTION ... AS $
BEGIN
UPDATE ...;
COMMIT; -- ERROR: invalid transaction termination
END;
$;
-- ✅ — bu PROCEDURE da mumkin
CREATE PROCEDURE ... AS $
BEGIN
UPDATE ...;
COMMIT; -- ishlaydi
END;
$;
Xulosa
Procedure — Function ga o'xshash, lekin ish bajarish uchun, natija qaytarish uchun emas.
CREATE OR REPLACE PROCEDURE nom(parametrlar)
LANGUAGE plpgsql AS $ ... $;
CALL nom(qiymatlar); -- chaqirish
Asosiy farq yodda bo'lsin:
Function → SELECT, qiymat qaytaradi, tranzaksiya boshqara olmaydi
Procedure → CALL, ish bajaradi, COMMIT/ROLLBACK yoza oladi
OUT va INOUT parametrlar orqali procedure ham "natija" qaytarishi mumkin — lekin bu qiymat qaytarish emas, parametr o'zgartirish.
Keyingi dars
34-dars: Trigger — BEFORE, AFTER, INSTEAD OF, trigger funksiya