Daxili dövrlər — başqa dövrlərin içində işləyən dövrlərdir. Təsəvvür elə, müsahibədə sual-cavab bölməsi var və bir sual yeni dəqiqləşdirici suallara gətirib çıxarır. Daxili dövrlərin məntiqi belədir: xarici dövr iterasiya edir, daxili dövr isə hər xarici dövr üçün keçidlər edir.
Nümunə: çarpma cədvəli
Gəlin 1-dən 5-ə qədər ədədlər üçün çarpma cədvəli yaradan funksiya yazaq. Bu klassik nümunədir, burada daxili dövrlərdən istifadə olunur:
CREATE OR REPLACE FUNCTION generate_multiplication_table()
RETURNS VOID AS $$
BEGIN
FOR i IN 1..5 LOOP -- Xarici dövr
FOR j IN 1..5 LOOP -- Daxili dövr
RAISE NOTICE '% x % = %', i, j, i * j; -- Nəticəni loglayırıq
END LOOP;
END LOOP;
END;
$$ LANGUAGE plpgsql;
-- Funksiyanı çağırmaq:
SELECT generate_multiplication_table();
İş prinsipi:
- Xarici dövr
iüçün 1-dən 5-ə qədər olan dəyərləri tutur. - Hər
iüçün daxili dövrjüçün 1-dən 5-ə qədər olan dəyərləri tutur. - Hər addımda
ivəjdəyərləri birləşdirilir və vurma nəticəsi hesablanır. - Çıxış mini-cədvəl kimi görünür:
1 x 1 = 1
1 x 2 = 2
...
5 x 5 = 25
Praktika: iki cədvəldə kəsişmələri tapmaq
İndi isə daha "real" bir nümunə yaradaq. Təsəvvür elə ki, bizdə iki cədvəl var:
students(tələbələr, onların adları),courses(onların yazıldığı kurslar).
Biz istəyirik ki, bir neçə kursa yazılmış tələbələri tapaq. Daxili dövrlərdən istifadə edirik:
CREATE OR REPLACE FUNCTION find_students_with_multiple_courses()
RETURNS TABLE(student_name TEXT, course_name TEXT) AS $$
BEGIN
FOR student IN SELECT DISTINCT student_name FROM students LOOP
FOR course IN SELECT DISTINCT course_name FROM courses WHERE student_id = student.student_id LOOP
RETURN QUERY SELECT student.student_name, course.course_name;
END LOOP;
END LOOP;
END;
$$ LANGUAGE plpgsql;
-- Funksiyanı çağırmaq:
SELECT * FROM find_students_with_multiple_courses();
Rekursiya
Rekursiya — bu, funksiya özünü çağıranda baş verir. Elə bil dostundan SQL izah etməyi xahiş edirsən, o isə sənə bu dərsə istinad edən sənədi oxumağı məsləhət görür... Rekursiyanı sonsuz dövrlə qarışdırmaq lazım deyil. Rekursiyada həmişə "dayanma şərti" olur (funksiya özünü çağırmağı dayandırdığı nöqtə).
Nümunə: ədədin faktorialını hesablamaq
n ədədinin faktorialı — 1-dən n-ə qədər bütün ədədlərin hasilidir. Məsələn, 5-in faktorialı (5!) 5 * 4 * 3 * 2 * 1 = 120-yə bərabərdir. Bunu rekursiya ilə belə yazmaq olar:
CREATE OR REPLACE FUNCTION calculate_factorial(n INTEGER)
RETURNS INTEGER AS $$
BEGIN
-- Dayanma şərti: 0 və ya 1-in faktorialı 1-dir
IF n = 0 OR n = 1 THEN
RETURN 1;
END IF;
-- Funksiyanın rekursiv çağırılması
RETURN n * calculate_factorial(n - 1);
END;
$$ LANGUAGE plpgsql;
-- Funksiyanı çağırmaq:
SELECT calculate_factorial(5); -- Nəticə: 120
İş prinsipi:
- Əgər
n0 və ya 1-ə bərabərdirsə, 1 qaytarırıq. - Əgər
n > 1isə, funksiya özünün - 1arqumenti ilə çağırır və bu dəyərin-ə vurur. - Beləliklə, çağırışlar "toplanır", sonra bir istiqamətdə açılır.
Praktik nümunə: Fibonacci ədədləri
Fibonacci ədədləri — bu, hər bir ədədin əvvəlki iki ədədin cəmi olduğu ardıcıllıqdır. Ardıcıllıq belə başlayır: 0, 1, 1, 2, 3, 5, 8....
Gəlin ardıcıllıqda n-ci ədədi hesablamaq üçün funksiya yazaq:
CREATE OR REPLACE FUNCTION fibonacci(n INTEGER)
RETURNS INTEGER AS $$
BEGIN
-- Dayanma şərti: ilk iki ədəd məlumdur
IF n = 0 THEN
RETURN 0;
ELSIF n = 1 THEN
RETURN 1;
END IF;
-- Funksiyanın rekursiv çağırılması
RETURN fibonacci(n - 1) + fibonacci(n - 2);
END;
$$ LANGUAGE plpgsql;
-- Funksiyanı çağırmaq:
SELECT fibonacci(6); -- Nəticə: 8
Daxili dövrlər və rekursiyanı nə vaxt istifadə etməli?
Daxili dövrlər cədvəllərlə işləmək üçün əladır:
- İki cədvəl arasında dəyərləri müqayisə etmək.
- Mürəkkəb məlumat kombinasiyaları qurmaq.
Rekursiya daha yaxşıdır:
- Ardıcıllıqları hesablamaq üçün (məsələn, faktorial, Fibonacci).
- İyerarxik strukturlarla işləmək üçün (məsələn, məhsul kateqoriyaları ağacı).
Tipik səhvlər
Daxili dövrlər bəzən performans baxımından "bahalı" olur, xüsusilə böyük cədvəllərlə işləyəndə. Onlardan yalnız adi SQL vasitələri ilə nəticə əldə etmək mümkün olmadıqda istifadə et.
Rekursiyadan istifadə edəndə mütləq aydın "dayanma şərtin" olsun. Onsuz sonsuz funksiya çağırışı və çox güman ki, stack overflow xətası alacaqsan.
Mürəkkəb daxili konstruksiyalar debug-u çətinləşdirə bilər. Aralıq nəticələri çıxarmaq üçün özünə RAISE NOTICE ilə kömək et.
GO TO FULL VERSION