18-bo‘lim

PL/pgSQL funksiyalari va triggerlar

SQL va PL/pgSQL funksiyalari, dollar qavs, shart va sikl, RETURNS TABLE, trigger yozish, NEW va OLD hamda audit jadvali.

🕑 11 daqiqa o‘qish 📄 773 so‘z 👁 0 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. Eng oddiy funksiya
  2. Funksiya turg'unligi
  3. PL/pgSQL - mantiq bor funksiya
  4. O'zgaruvchi va sikl
  5. Jadval qaytaruvchi funksiya
  6. Trigger
  7. Avtomatik yangilangan_sana
  8. Audit jadvali
  9. Qachon trigger ishlatmaslik kerak
  10. Xulosa

Ba'zi mantiqni bazaning ichida saqlash qulay: u har bir dasturga birdek qo'llanadi va ma'lumotga eng yaqin joyda ishlaydi.

SQL
CREATE TABLE xodim (
    id      int GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    ism     text NOT NULL,
    lavozim text NOT NULL,
    maosh   numeric(10,2) NOT NULL CHECK (maosh > 0)
);

INSERT INTO xodim (ism, lavozim, maosh) VALUES
    ('Husanboy', 'dasturchi',  9000000.00),
    ('Malika',   'tahlilchi',  7500000.00),
    ('Nodira',   'dasturchi', 10500000.00),
    ('Aziza',    'menejer',    6800000.00);

Eng oddiy funksiya #

Agar funksiya bitta SELECT dan iborat bo'lsa, LANGUAGE sql yetarli:

SQL
CREATE FUNCTION yillik_maosh(oylik numeric)
RETURNS numeric
LANGUAGE sql
IMMUTABLE
AS $$
    SELECT oylik * 12;
$$;

SELECT ism, maosh, yillik_maosh(maosh) AS yillik
FROM xodim
ORDER BY id;
Natija
CREATE FUNCTION
   ism    |    maosh    |    yillik
----------+-------------+--------------
 Husanboy |  9000000.00 | 108000000.00
 Malika   |  7500000.00 |  90000000.00
 Nodira   | 10500000.00 | 126000000.00
 Aziza    |  6800000.00 |  81600000.00
(4 rows)
Dollar qavs - $$ nima

Funksiya tanasi baza uchun matn. Uni oddiy qo'shtirnoq bilan ham yozish mumkin edi, lekin u holda tana ichidagi har bir qo'shtirnoqni ikkilantirish kerak bo'lar edi:

Natija
AS 'SELECT ''salom'';'

Dollar qavs bu muammoni yo'qotadi - ichida hech narsani qochirish shart emas.

Qavs orasiga belgi qo'yish ham mumkin: $tana$ ... $tana$. Bu tana ichida yana $$ uchrasa qo'l keladi - masalan funksiya ichida boshqa funksiya yaratilayotganda.

Funksiya turg'unligi #

IMMUTABLE so'zi tasodifiy emas. U bazaga funksiya qanday tutishini aytadi:

BelgiMa'nosiMisol
IMMUTABLEBir xil kirish - doim bir xil natijaoylik * 12
STABLEBitta so'rov ichida o'zgarmaydiJadvaldan o'qish
VOLATILEHar safar o'zgarishi mumkinrandom(), now()

Odatiy qiymat - VOLATILE, ya'ni eng ehtiyotkori. To'g'ri belgi qo'yilsa, baza funksiyani indeksda ishlatishi va natijasini qayta ishlatishi mumkin bo'ladi.

PL/pgSQL - mantiq bor funksiya #

Shart, sikl yoki o'zgaruvchi kerak bo'lsa - plpgsql:

SQL
CREATE FUNCTION maosh_darajasi(qiymat numeric)
RETURNS text
LANGUAGE plpgsql
IMMUTABLE
AS $$
BEGIN
    IF qiymat >= 10000000 THEN
        RETURN 'yuqori';
    ELSIF qiymat >= 7000000 THEN
        RETURN 'orta';
    ELSE
        RETURN 'boshlangich';
    END IF;
END;
$$;

SELECT ism, maosh, maosh_darajasi(maosh) AS daraja
FROM xodim
ORDER BY maosh DESC;
Natija
CREATE FUNCTION
   ism    |    maosh    |   daraja
----------+-------------+-------------
 Nodira   | 10500000.00 | yuqori
 Husanboy |  9000000.00 | orta
 Malika   |  7500000.00 | orta
 Aziza    |  6800000.00 | boshlangich
(4 rows)

O'zgaruvchi va sikl #

SQL
CREATE FUNCTION lavozim_royxati()
RETURNS text
LANGUAGE plpgsql
AS $$
DECLARE
    natija text := '';
    qator  record;
BEGIN
    FOR qator IN
        SELECT lavozim, count(*) AS soni
        FROM xodim GROUP BY lavozim ORDER BY lavozim
    LOOP
        natija := natija || qator.lavozim || '=' || qator.soni || ' ';
    END LOOP;

    RETURN trim(natija);
END;
$$;

SELECT lavozim_royxati();
Natija
CREATE FUNCTION
          lavozim_royxati
-----------------------------------
 dasturchi=2 menejer=1 tahlilchi=1
(1 row)

DECLARE bo'limida o'zgaruvchilar e'lon qilinadi, := esa qiymat berish belgisi.

record turi qulay: u qaysi ustunlar kelishini oldindan bilishni talab qilmaydi.

Jadval qaytaruvchi funksiya #

SQL
CREATE FUNCTION maoshi_kop(chegara numeric)
RETURNS TABLE (ism text, lavozim text, maosh numeric)
LANGUAGE plpgsql
STABLE
AS $$
BEGIN
    RETURN QUERY
        SELECT x.ism, x.lavozim, x.maosh
        FROM xodim x
        WHERE x.maosh > chegara
        ORDER BY x.maosh DESC;
END;
$$;

SELECT * FROM maoshi_kop(7000000);
Natija
CREATE FUNCTION
   ism    |  lavozim  |    maosh
----------+-----------+-------------
 Nodira   | dasturchi | 10500000.00
 Husanboy | dasturchi |  9000000.00
 Malika   | tahlilchi |  7500000.00
(3 rows)
Nom to'qnashuvi - PL/pgSQL ning eng ko'p uchraydigan xatosi

RETURNS TABLE (ism text, ...) yozganda ism o'zgaruvchi bo'lib qoladi. Jadvalda ham ism ustuni bor.

Endi WHERE ism = 'Malika' deb yozsangiz, baza qaysi ism ni nazarda tutayotganingizni bilmaydi:

Natija
ERROR:  column reference "ism" is ambiguous
DETAIL: It could refer to either a PL/pgSQL variable
        or a table column.

Uchta yechim bor va birinchisi eng yaxshisi:

YechimYozuv
Jadvalga taxallus beringFROM xodim x ... x.ism
Parametrga boshqa nomp_ism, _ism
Funksiyaga taxallus#variable_conflict use_column

Yuqoridagi maoshi_kop misolida aynan birinchi yo'l ishlatilgan - shuning uchun har joyda x. prefiksi bor. RETURN QUERY esa SELECT natijasini to'g'ridan-to'g'ri funksiya natijasiga uzatadi.

Trigger #

Trigger - jadvalga INSERT, UPDATE yoki DELETE bo'lganda avtomatik ishga tushadigan funksiya.

BEFORE va AFTER triggerlari UPDATE keldi BEFORE trigger NEW ni o'zgartira oladi Qator yoziladi AFTER trigger audit yozish Trigger ichidagi ikki maxsus o'zgaruvchi NEW - yangi qator qiymati (INSERT va UPDATE da bor) OLD - eski qator qiymati (UPDATE va DELETE da bor) BEFORE trigger NEW ni o'zgartirib qaytarishi mumkin AFTER trigger kech qoladi - o'zgartirish kuchga kirmaydi
BEFORE qatorni o'zgartirish uchun, AFTER esa yon ish uchun

Avtomatik yangilangan_sana #

Eng ko'p ishlatiladigan trigger - vaqt belgisini yangilash:

SQL
ALTER TABLE xodim
    ADD COLUMN yangilangan timestamptz NOT NULL DEFAULT now();

CREATE FUNCTION vaqtni_yangila()
RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
    NEW.yangilangan := now();
    RETURN NEW;
END;
$$;

CREATE TRIGGER xodim_vaqt
    BEFORE UPDATE ON xodim
    FOR EACH ROW
    EXECUTE FUNCTION vaqtni_yangila();

UPDATE xodim SET maosh = maosh + 500000 WHERE ism = 'Malika';

SELECT ism, maosh, yangilangan > now() - interval '1 minute' AS hozir
FROM xodim
WHERE ism IN ('Malika', 'Aziza')
ORDER BY ism;
Natija
ALTER TABLE
CREATE FUNCTION
CREATE TRIGGER
UPDATE 1
  ism   |   maosh    | hozir
--------+------------+-------
 Aziza  | 6800000.00 | t
 Malika | 8000000.00 | t
(2 rows)

Diqqat: yangilangan ustunining aniq qiymati o'rniga shart tanlandi. Sabab oddiy - aniq vaqt har safar boshqa bo'ladi.

Audit jadvali #

AFTER trigger o'zgarishlarni yozib borish uchun ideal:

SQL
CREATE TABLE maosh_tarixi (
    id        int GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    xodim_id  int NOT NULL,
    eski      numeric(10,2),
    yangi     numeric(10,2),
    kim       text NOT NULL
);

CREATE FUNCTION maoshni_yoz()
RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
    IF NEW.maosh IS DISTINCT FROM OLD.maosh THEN
        INSERT INTO maosh_tarixi (xodim_id, eski, yangi, kim)
        VALUES (OLD.id, OLD.maosh, NEW.maosh, current_user);
    END IF;

    RETURN NULL;
END;
$$;

CREATE TRIGGER xodim_maosh_audit
    AFTER UPDATE ON xodim
    FOR EACH ROW
    EXECUTE FUNCTION maoshni_yoz();

UPDATE xodim SET maosh = 11000000 WHERE ism = 'Nodira';
UPDATE xodim SET lavozim = 'katta menejer' WHERE ism = 'Aziza';

SELECT xodim_id, eski, yangi FROM maosh_tarixi ORDER BY id;
Natija
CREATE TABLE
CREATE FUNCTION
CREATE TRIGGER
UPDATE 1
UPDATE 1
 xodim_id |    eski     |    yangi
----------+-------------+-------------
        3 | 10500000.00 | 11000000.00
(1 row)

Ikkita UPDATE bo'ldi, lekin tarixga faqat bittasi tushdi - ikkinchisida maosh o'zgarmagan.

IS DISTINCT FROM ni yodda tuting

NEW.maosh <> OLD.maosh yozuvi ishlamaydi, agar qiymatlardan biri NULL bo'lsa.

YozuvNULL va 500NULL va NULL
<>NULL (shart bajarilmaydi)NULL
IS DISTINCT FROMtruefalse

Triggerlarda deyarli har doim IS DISTINCT FROM kerak - aks holda NULL dan songa o'zgarish sezilmay qoladi.

AFTER triggerda RETURN NULL yozish ham odat: qaytgan qiymat baribir e'tiborga olinmaydi, chunki qator allaqachon yozilgan.

Qachon trigger ishlatmaslik kerak #

Trigger kuchli, lekin u ko'rinmas ishlaydi - shuning uchun xatoni topish qiyinlashadi.

Trigger to'g'riTrigger noto'g'ri
yangilangan vaqtini qo'yishMurakkab biznes mantiq
Audit yozuviTashqi tizimga xabar yuborish
Hosilaviy ustunni to'ldirishBir necha jadvalga zanjir yozuv
Oddiy tekshiruvUzoq davom etadigan hisob

Asosiy xavf - zanjir: A jadvalidagi trigger B ga yozadi, B dagi trigger C ga yozadi. Bir kun kelib bitta UPDATE nima uchun 3 soniya ishlayotganini hech kim tushunmay qoladi.

Amaliy topshiriq
  1. LANGUAGE sql bilan oddiy funksiya yozing.
  2. Unga IMMUTABLE belgisini qo'ying va ma'nosini yozing.
  3. plpgsql da IF li funksiya yozing.
  4. DECLARE va FOR ... LOOP ishlatgan funksiya tuzing.
  5. RETURNS TABLE bilan jadval qaytaring.
  6. Ustun va o'zgaruvchi nomini ataylab bir xil qiling - xatoni ko'ring.
  7. Uni jadval taxallusi bilan to'g'rilang.
  8. BEFORE UPDATE trigger yozib NEW ni o'zgartiring.
  9. AFTER UPDATE trigger bilan audit jadvalini to'ldiring.
  10. <> o'rniga IS DISTINCT FROM nega kerakligini tushuntiring.

Xulosa #

  • Bitta SELECT uchun LANGUAGE sql, mantiq uchun LANGUAGE plpgsql.
  • $$ dollar qavsi tana ichidagi qo'shtirnoqlarni qochirishdan qutqaradi.
  • IMMUTABLE, STABLE, VOLATILE - bazaga funksiya tutimini aytadi.
  • DECLARE da o'zgaruvchi e'lon qilinadi, := qiymat beradi.
  • RETURNS TABLE funksiyani jadval kabi ishlatish imkonini beradi.
  • Ustun va o'zgaruvchi bir xil nomlansa - noaniqlik xatosi; taxallus bilan hal qiling.
  • Triggerda NEW yangi, OLD eski qator qiymati.
  • BEFORE NEW ni o'zgartira oladi, AFTER esa faqat yon ish uchun.
  • IS DISTINCT FROM NULL ni ham to'g'ri solishtiradi - triggerda shart.
  • Trigger ko'rinmas ishlaydi: uni oddiy vazifalar uchun saqlang, biznes mantiq uchun emas.

Keyingi bo'limda katta jadvallarni bo'laklash, VACUUM va zaxira nusxani ko'ramiz.

Xatolik topdingizmi?

Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.