CodeGym /Kurslar /SQL SELF /İç-içə transaction-larla işləməyə dair praktiki nümunələr...

İç-içə transaction-larla işləməyə dair praktiki nümunələr

SQL SELF
Səviyyə, Dərs
Mövcuddur

Bu gün bizim məqsədimiz belə bir funksiya yazmaqdır ki:

  1. Müştərinin balansını yoxlayır. Məbləği silməzdən əvvəl kifayət qədər vəsait olub-olmadığını yoxlamaq lazımdır.
  2. Balansdan vəsaiti silir. Əgər balansda kifayət qədər pul varsa, silinir.
  3. Uğurlu və uğursuz əməliyyatları log-layır. Bütün hərəkətlər sonradan analiz üçün log cədvəlinə yazılır.

Bu, sadəcə darıxdırıcı çıxma funksiyası deyil. Burada biz iç-içə transaction-lardan istifadə edəcəyik ki, nəsə düz getməsə (məsələn, balansda pul çatmırsa və ya log yazarkən səhv çıxsa) dəyişiklikləri geri qaytaraq. SAVEPOINT nöqtələrinin faydasını görəcəyik və prosedurları səhvlərə davamlı etməyi öyrənəcəyik.

İlkin cədvəlləri yaradırıq

Funksiya yazmağa başlamazdan əvvəl gəlin bazanı hazırlayaq. Bizə üç cədvəl lazım olacaq:

  1. clients — müştərilərin məlumatları və balansı üçün.
  2. payments — uğurlu transaction-ların yazılması üçün.
  3. logs — bütün ödəniş cəhdlərinin (uğurlu və uğursuz) saxlanması üçün.
-- Müştərilər cədvəli
CREATE TABLE clients (
    client_id SERIAL PRIMARY KEY,
    full_name TEXT NOT NULL,
    balance NUMERIC(10, 2) NOT NULL DEFAULT 0
);

-- Uğurlu ödənişlər cədvəli
CREATE TABLE payments (
    payment_id SERIAL PRIMARY KEY,
    client_id INT NOT NULL REFERENCES clients(client_id),
    amount NUMERIC(10, 2) NOT NULL,
    payment_date TIMESTAMP DEFAULT NOW()
);

-- Log cədvəli
CREATE TABLE logs (
    log_id SERIAL PRIMARY KEY,
    client_id INT NOT NULL REFERENCES clients(client_id),
    message TEXT NOT NULL,
    log_date TIMESTAMP DEFAULT NOW()
);

clients cədvəlini test dataları ilə dolduraq

INSERT INTO clients (full_name, balance)
VALUES 
    ('Otto Song', 100.00),
    ('Maria Chi', 50.00),
    ('Anna Vel', 0.00);

İndi bizdə üç müştəri var: Otto-nun hesabında 100, Maria-da — 50, Anna-da isə — 0 var.

Biznes-məntiqin reallaşdırılması: PROSEDUR vs FUNKSIYA

Qısa olaraq:

  • "Hamısı və ya heç nə" tipli biznes əməliyyatları üçün funksiya kifayətdir.
  • Transaction-lara mərhələli nəzarət, qismən commit, rollback, error log-lama üçün — prosedur (CREATE PROCEDURE) istifadə et.

Niyə funksiya yox? Çünki PostgreSQL 17-də funksiya daxilində nə COMMIT, nə SAVEPOINT, nə də ROLLBACK istifadə edə bilməzsən. Bütün dəyişikliklər xarici transaction daxilində atomik olur.

Yalnız prosedur (CREATE PROCEDURE ... LANGUAGE plpgsql) SAVEPOINT, COMMIT, ROLLBACK istifadə etməyə imkan verir — amma vacib məhdudiyyətlərlə:

  • Prosedur daxilində SAVEPOINT, COMMIT, RELEASE SAVEPOINT icazəlidir.
  • ROLLBACK TO SAVEPOINT PL/pgSQL-də qadağandır (səhv çıxacaq), bunun əvəzinə BEGIN ... EXCEPTION ... END bloklarından istifadə olunur, hansı ki "virtual savepoint" yaradır.

Kodun bir hissəsini geri qaytarmaq üçün əsas texnika:

BEGIN
    -- sənin kodun
EXCEPTION
    WHEN OTHERS THEN
        -- Bu blokda səhv olsa, DAXİLİNDƏKİ bütün dəyişikliklər geri qaytarılır!
        -- Log-da məlumat saxlamaq olar:
        INSERT INTO logs (...) VALUES (...);
END;

Qismən rollback və log-lama ilə ödəniş proseduru yaradırıq

CREATE OR REPLACE PROCEDURE process_payment(
    in_client_id INT,
    in_payment_amount NUMERIC
)
LANGUAGE plpgsql
AS $$
DECLARE
    current_balance NUMERIC;
BEGIN
    -- Müştərinin balansını alırıq
    SELECT balance INTO current_balance
    FROM clients
    WHERE client_id = in_client_id;

    IF NOT FOUND THEN
        INSERT INTO logs (client_id, message)
        VALUES (in_client_id, 'Müştəri tapılmadı, əməliyyat rədd edildi');
        RAISE EXCEPTION 'ID-si % olan müştəri tapılmadı', in_client_id;
    END IF;

    -- Vəsaitin kifayətliliyini yoxlayırıq
    IF current_balance < in_payment_amount THEN
        INSERT INTO logs (client_id, message)
        VALUES (in_client_id, 'Silinmək üçün kifayət qədər vəsait yoxdur ' || in_payment_amount || ' azn.');
        -- Proseduru bitiririk
        RETURN;
    END IF;

    -- Atomik dəyişikliklər üçün blok; səhv olsa — rollback (virtual savepoint)
    BEGIN
        -- Balansı silirik
        UPDATE clients
        SET balance = balance - in_payment_amount
        WHERE client_id = in_client_id;

        -- Uğurlu ödənişi əlavə edirik
        INSERT INTO payments (client_id, amount)
        VALUES (in_client_id, in_payment_amount);

        -- Uğuru log-layırıq
        INSERT INTO logs (client_id, message)
        VALUES (in_client_id, 'Uğurlu silinmə ' || in_payment_amount || ' azn.');

    EXCEPTION
        WHEN OTHERS THEN
            -- Bu blokdakı bütün dəyişikliklər ləğv olunur
            INSERT INTO logs (client_id, message)
            VALUES (in_client_id, 'Ödəniş zamanı səhv: ' || SQLERRM);
            -- (açıq ROLLBACK TO SAVEPOINT lazım deyil — qadağandır və lazım deyil)
    END;
END;
$$;

Qısa olaraq nə baş verir:

  • Əgər pul azdır/müştəri yoxdur — log-layırıq və çıxırıq.
  • Bütün kritik kod BEGIN ... EXCEPTION ... END blokuna qoyulub.
  • Bu blokda istənilən səhv olsa — bütün dəyişikliklər avtomatik geri qaytarılır; səhv log-a yazılır.
  • Birbaşa SAVEPOINT və ROLLBACK TO SAVEPOINT istifadə olunmur — belə də olmalıdır, PL/pgSQL-də yalnız EXCEPTION blokları ilə işləyir.

Prosedurun çağırılması

Vacibdir: proseduru CALL ... komandası ilə çağırmaq lazımdır, üstəlik bazaya xarici bağlantı autocommit rejimində və ya açıq böyük transaction xaricində olmalıdır!

CALL process_payment(1, 30.00);   -- Uğurlu ödəniş
CALL process_payment(2, 100.00);  -- Kifayət qədər vəsait yoxdur
CALL process_payment(999, 50.00); -- Müştəri yoxdur

Nəticələrin yoxlanması

  • Müştərinin balansı yalnız ödəniş keçərsə dəyişir.
  • payments cədvəli — yalnız uğurlu silinmədə qeyd olur.
  • logs — bütün cəhdlərin (və səhvlərin) tarixi.
SELECT * FROM clients;
SELECT * FROM payments;
SELECT * FROM logs;

Real tətbiq

Transaction-ların işlənməsi üçün prosedurlar — fintech, e-commerce və hətta oyun platformalarında əsas hissələrdən biridir. Təsəvvür elə, internet mağazası var, hansı ki, hədiyyə sertifikatlarının balansını nəzərə almalı və alış zamanı onları silməlidir, ya da bank sistemi minlərlə əməliyyatı saniyədə emal edir.

Bu biliklər sənə praktikada lazım olacaq, müştərilərinin məlumatlarını qorumağa və ödənişlərin işlənməsində fəlakətli səhvlərdən qaçmağa kömək edəcək.

2
Tapşırıq
SQL SELF, səviyyə, dərs
Bağlanıb
Müştəri üçün ödənişin işlənməsi
Müştəri üçün ödənişin işlənməsi
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION