CodeGym /Kurslar /SQL SELF /Çoxmərhələli prosedurun kompleks debug və optimizasiyası

Çoxmərhələli prosedurun kompleks debug və optimizasiyası

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

Çoxmərhələli prosedurlar — bunlar database üçün "isveç bıçağı" kimidir. Onlar adətən input dataların validasiyası, dəyişikliklərin icrası (məsələn, record-ların yenilənməsi, logların əlavə olunması), bəzən isə analitikadan ibarət olur. Amma problem burdadır: prosedur nə qədər mürəkkəbdirsə, səhv ehtimalı da bir o qədər artır. Məntiq səhvi, yavaş query, gözdən qaçan detal — və hər şey alt-üst ola bilər.

Kompleks debug aşağıdakı aspektləri əhatə edir:

  1. Input dataların analizi: parametrlər düzgün qurulub? Düzgün datalar ötürülüb?
  2. Əsas mərhələlərin icrasının yoxlanması: prosedurun bütün addımları düzgün işləyir?
  3. Aralıq nəticələrin loglanması: nə baş verdiyini bilmək üçün, nəsə "qırılmamışdan" əvvəl.
  4. Performance bottleneck-lərin optimizasiyası: query-ləri "yavaşdıran" zəif yerləri yaxşılaşdırırıq.

Tapşırığın qoyuluşu: çoxmərhələli prosedur nümunəsi

Praktiki nümunə üçün təsəvvür elə ki, biz internet mağazasının database-i ilə işləyirik. Bizə sifarişin işlənməsi üçün prosedur yaratmaq lazımdır. O, aşağıdakı addımları yerinə yetirəcək:

  1. Məhsulun anbarda olub-olmadığını yoxlamaq.
  2. Məhsulu rezerv etmək.
  3. Sifarişin statusunu yeniləmək.
  4. Hadisələri (məsələn, uğurlu rezervasiya və ya səhv) log cədvəlinə yazmaq.

Database strukturunun script-i:

-- Məhsullar cədvəli
CREATE TABLE products (
    product_id SERIAL PRIMARY KEY,
    name TEXT NOT NULL,
    stock_quantity INTEGER NOT NULL
);

-- Sifarişlər cədvəli
CREATE TABLE orders (
    order_id SERIAL PRIMARY KEY,
    product_id INTEGER REFERENCES products(product_id),
    order_status TEXT NOT NULL
);

-- Log cədvəli
CREATE TABLE order_logs (
    log_id SERIAL PRIMARY KEY,
    order_id INTEGER,
    log_message TEXT,
    log_time TIMESTAMP DEFAULT NOW()
);

Addım 1: Çoxmərhələli prosedurun yaradılması

Gəlin process_order adlı əsas proseduru yaradaq. O, sifarişin id-sini qəbul edib bütün işləmə mərhələlərini icra edəcək.

CREATE OR REPLACE FUNCTION process_order(p_order_id INTEGER)
RETURNS VOID LANGUAGE plpgsql AS $$
DECLARE
    v_product_id INTEGER;
    v_stock_quantity INTEGER;
BEGIN
    -- 1. Məhsulun id-sini və sifarişin statusunu alırıq
    SELECT product_id INTO v_product_id
    FROM orders
    WHERE order_id = p_order_id;

    IF v_product_id IS NULL THEN
        RAISE EXCEPTION 'Sifariş % mövcud deyil və ya product_id yoxdur', p_order_id;
    END IF;

    -- 2. Məhsulun anbarda olub-olmadığını yoxlayırıq
    SELECT stock_quantity INTO v_stock_quantity
    FROM products
    WHERE product_id = v_product_id;

    IF v_stock_quantity <= 0 THEN
        RAISE EXCEPTION 'Məhsul % anbarda yoxdur', v_product_id;
    END IF;

    -- 3. Anbardakı miqdarı yeniləyirik
    UPDATE products
    SET stock_quantity = stock_quantity - 1
    WHERE product_id = v_product_id;

    -- 4. Sifarişin statusunu yeniləyirik
    UPDATE orders
    SET order_status = 'İşləndi'
    WHERE order_id = p_order_id;

    -- 5. Uğurlu hadisəni log-a yazırıq
    INSERT INTO order_logs(order_id, log_message)
    VALUES (p_order_id, 'Sifariş uğurla işlənib.');
END;
$$;

Addım 2: RAISE NOTICERAISE EXCEPTION ilə error loglamaq

İndi isə əsl "sehr" başlayır. Addımların arasında loglama əlavə edəcəyik ki, error-ları tutaq və hər mərhələdə nə baş verdiyini başa düşək.

Loglamalı yenilənmiş kod:

CREATE OR REPLACE FUNCTION process_order(p_order_id INTEGER)
RETURNS VOID LANGUAGE plpgsql AS $$
DECLARE
    v_product_id INTEGER;
    v_stock_quantity INTEGER;
BEGIN
    RAISE NOTICE 'Sifariş işlənir %...', p_order_id;

    -- 1. Məhsulun id-sini alırıq
    SELECT product_id INTO v_product_id
    FROM orders
    WHERE order_id = p_order_id;

    IF v_product_id IS NULL THEN
        RAISE EXCEPTION 'Sifariş % mövcud deyil və ya product_id yoxdur', p_order_id;
    END IF;
    RAISE NOTICE 'Sifariş üçün məhsul ID-si %: %', p_order_id, v_product_id;

    -- 2. Məhsulun anbarda olub-olmadığını yoxlayırıq
    SELECT stock_quantity INTO v_stock_quantity
    FROM products
    WHERE product_id = v_product_id;

    IF v_stock_quantity <= 0 THEN
        RAISE EXCEPTION 'Məhsul % anbarda yoxdur', v_product_id;
    END IF;
    RAISE NOTICE 'Məhsulun % anbar miqdarı: %', v_product_id, v_stock_quantity;

    -- 3. Anbardakı miqdarı yeniləyirik
    UPDATE products
    SET stock_quantity = stock_quantity - 1
    WHERE product_id = v_product_id;

    -- 4. Sifarişin statusunu yeniləyirik
    UPDATE orders
    SET order_status = 'İşləndi'
    WHERE order_id = p_order_id;

    -- 5. Uğurlu icranı loglayırıq
    INSERT INTO order_logs(order_id, log_message)
    VALUES (p_order_id, 'Sifariş uğurla işlənib.');
    RAISE NOTICE 'Sifariş % uğurla işlənib.', p_order_id;

EXCEPTION WHEN OTHERS THEN
    -- Error-u loglayırıq
    INSERT INTO order_logs(order_id, log_message)
    VALUES (p_order_id, 'Səhv: ' || SQLERRM);
    RAISE;
END;
$$;

Addım 3: İndex-lərlə optimizasiya

Əgər database-də çoxlu məhsul və ya sifariş varsa, lazım olan sətri tapmaq bottleneck ola bilər. İndex əlavə edək ki, işləmə zamanı seçim sürətlənsin:

-- orders cədvəlində axtarışı sürətləndirmək üçün index
CREATE INDEX idx_orders_product_id ON orders(product_id);

-- products cədvəlində axtarışı sürətləndirmək üçün index
CREATE INDEX idx_products_stock_quantity ON products(stock_quantity);

Addım 4: EXPLAIN ANALYZE ilə performans analizi

İndi yoxlayaq görək, funksiyamız nə qədər tez işləyir. Bunun üçün onu performans analizi ilə çağırırıq:

EXPLAIN ANALYZE
SELECT process_order(1);

Nəticə göstərəcək ki, hər mərhələ nə qədər vaxt aparır. Hansı addım daha yavaşdır — bunu tapıb proseduru daha da optimizasiya edə bilərik.

Addım 5: Transaction-lardan istifadə ilə təkmilləşdirmə

Etibarlılığı artırmaq üçün bütün proseduru transaction-a bükmək olar. Beləliklə, nəsə səhv getsə, bütün dəyişikliklər geri qaytarılacaq.

BEGIN;

-- Funksiyanı çağırırıq
SELECT process_order(1);

-- Transaction-u commit edirik
COMMIT;

Funksiya kodunun özündə SAVEPOINTROLLBACK TO SAVEPOINT istifadə edib qismən error-ları idarə etmək olar.

Praktiki tapşırıq: kütləvi sifarişlərin işlənməsi

Dərsi bir neçə sifarişin eyni anda işlənməsi nümunəsi ilə bitirək. Elə bir funksiya yaradacağıq ki, statusu Pending olan bütün sifarişləri işləsin:

CREATE OR REPLACE FUNCTION process_all_orders()
RETURNS VOID LANGUAGE plpgsql AS $$
DECLARE
    v_order_id INTEGER;
BEGIN
    FOR v_order_id IN
        SELECT order_id
        FROM orders
        WHERE order_status = 'Pending'
    LOOP
        BEGIN
            PERFORM process_order(v_order_id);
        EXCEPTION WHEN OTHERS THEN
            RAISE NOTICE 'Sifariş işlənmədi %: %', v_order_id, SQLERRM;
        END;
    END LOOP;
END;
$$;

Bu funksiyanı çağıranda statusu Pending olan bütün sifarişlər işlənəcək və hər hansı error sadəcə loglanacaq.

Beləliklə, biz göstərdik ki, mürəkkəb prosedurları necə debug və optimizasiya etmək, onların etibarlılığını, performansını və oxunaqlılığını artırmaq olar. Bu biliklər real layihələrdə sənə çox lazım olacaq, çünki prosedurların keyfiyyəti tətbiqin uğurunu müəyyən edir.

2
Tapşırıq
SQL SELF, səviyyə, dərs
Bağlanıb
Aralıq addımların loglaşdırılması
Aralıq addımların loglaşdırılması
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION