Təbriklər, artıq həqiqətən maraqlı yerə gəldik! Bu gün müxtəlif subquery-ləri necə birləşdirib çətin məsələləri həll etməyi görəcəyik. EXISTS, IN, HAVING — bu trio ilə özünü database maq kimi hiss edəcəksən. Bir cədvəldən məlumat çıxaracağıq, başqa cədvəldəki məlumatlara görə filtrləyəcəyik, qruplaşdıracağıq, sonra isə qrupları filtrləyəcəyik. Bonus olaraq — sorğuları necə daha effektiv etmək olar, onu da müzakirə edəcəyik.
Gəlin, bir ümumi tapşırıq qoyaq və leksiya boyunca onu addım-addım həll edək.
Tapşırığın qoyuluşu
Tutaq ki, bizdə universitetin database-i var və orada üç cədvəl var:
students cədvəli
| id | name | group_id |
|---|---|---|
| 1 | Otto | 101 |
| 2 | Maria | 101 |
| 3 | Alex | 102 |
| 4 | Anna | 103 |
courses cədvəli
| id | name |
|---|---|
| 1 | Riyaziyyat |
| 2 | Proqramlaşdırma |
| 3 | Fəlsəfə |
enrollments cədvəli
| student_id | course_id | grade |
|---|---|---|
| 1 | 1 | 90 |
| 1 | 2 | NULL |
| 2 | 1 | 85 |
| 3 | 3 | 70 |
Aşağıdakı tələbələri seçmək lazımdır:
- Ən azı bir kursa yazılmış olanlar
EXISTS. - Yazıldığı kurslardan ən az birində qiyməti olmayanlar
IN. - Orta balı 80-dən yuxarı olan qruplara aid olanlar
HAVING.
EXISTS və IN ilə həll
Addım 1: Yazılmış tələbələri yoxlamaq (EXISTS). Ən sadə şərtdən başlayaq. Hansı tələbənin ən azı bir kursa yazıldığını tapmaq lazımdır. Bunun üçün EXISTS istifadə edə bilərik.
SELECT name
FROM students s
WHERE EXISTS (
SELECT 1
FROM enrollments e
WHERE e.student_id = s.id
);
- Xarici sorğu
studentscədvəlindən adları seçir. - Subquery-də yoxlayırıq ki,
enrollmentscədvəlində həmin tələbəyə uyğun qeyd var, ya yox (WHERE e.student_id = s.id). SELECT 1sadəcə qeyd olub-olmamasını yoxlamaq üçündür, məzmun vacib deyil.
Nəticə:
| name |
|---|
| Otto |
| Maria |
| Alex |
Artıq bilirik ki, hansı tələbələr kurslara yazılıb. Amma daha çox istəyirik. Qiyməti olmayanları filtrləmək istəyirik.
Addım 2: Qiymətin olmamasını yoxlamaq (IN + NULL). İndi filtr əlavə edirik: yalnız o tələbələr lazımdır ki, ən azı bir kursda qiyməti yoxdur. Burada IN və NULL kömək edəcək.
SELECT name
FROM students s
WHERE id IN (
SELECT e.student_id
FROM enrollments e
WHERE e.grade IS NULL
);
- Xarici sorğuda tələbələrin adlarını seçirik.
- Subquery
enrollmentscədvəlindənstudent_idsiyahısı qaytarır, haradagrade IS NULL.
Nəticə:
| name |
|---|
| Otto |
Deməli, Otto — qiyməti olmayan yeganə tələbədir. Nə dramatik! Amma işimiz hələ bitməyib: yalnız orta balı 80-dən yuxarı olan qrupları nəzərə almalıyıq.
HAVING ilə həll
Addım 3: Qruplaşdırma və HAVING ilə filtrasiya.
İndi isə hər şeyi birləşdirməyin vaxtıdır. Lazımdır ki:
- Hər qrup üçün orta balı hesablayaq.
- Orta balı 80-dən yuxarı olan qrupları filtrləyək.
- Bu qruplardan olan tələbələri, əvvəlki şərtləri də nəzərə alaraq çıxaraq.
SELECT name
FROM students s
WHERE s.group_id IN (
SELECT group_id
FROM students
JOIN enrollments ON students.id = enrollments.student_id
WHERE grade IS NOT NULL
GROUP BY group_id
HAVING AVG(grade) > 80
)
AND id IN (
SELECT e.student_id
FROM enrollments e
WHERE e.grade IS NULL
);
- Xarici sorğu bütün şərtlərə uyğun tələbələrin adlarını seçir.
WHERE-də birinci subquery orta balı 80-dən yuxarı olan qruplarıngroup_idsiyahısını qaytarır.studentsiləenrollmentsbirləşdiririk ki, qiymətləri alaq.- Yalnız
grade IS NOT NULLolanları filtrləyirik. - Məlumatları
group_idüzrə qruplaşdırırıq. - Qrupları
HAVINGilə filtrləyirik.
WHERE-də ikinci subquery tələbənin ən azı bir kursda qiyməti olmadığını yoxlayır.- Hər iki şərt
ANDilə birləşdirilib.
Nəticə:
| name |
|---|
| Otto |
Beləliklə, Otto — təkcə qiyməti olmayan tələbə deyil, həm də uğurlu qrupun üzvüdür.
Yanaşmaların müqayisəsi: EXISTS vs IN
EXISTS ən yaxşısı, sadəcə qeyd olub-olmadığını tez yoxlamaq istəyəndə işləyir. O, ilk qeyd tapılan kimi axtarışı dayandırır, bu da böyük cədvəllər üçün vacibdir.
Eyni zamanda IN məlumatın məzmununa fokuslananda faydalıdır. Məsələn, filtr üçün id siyahısı çıxaranda. Amma unutma ki, çoxlu dəyər qaytaran subquery-lər üçün IN yavaş ola bilər.
HAVING nə vaxt istifadə olunur
Aqreqasiya olunmuş məlumatlarda, nəticəyə görə filtr lazımdırsa, HAVING ən yaxşı seçimdir. Amma şərti WHERE-ə köçürmək mümkündürsə (məsələn, sütuna görə filtr), sorğunu sadələşdir və sürətləndir.
Tam nümunə
Pekişdirmək üçün başqa bir nümunə: elə qrupları seç ki, orada ən azı bir tələbənin qiyməti 75-dən aşağıdır, amma heç biri "Fəlsəfə" kursuna yazılmayıb.
Xatırladaq, cədvəllərimiz:
students cədvəli
| id | name | group_id |
|---|---|---|
| 1 | Otto | 101 |
| 2 | Maria | 101 |
| 3 | Alex | 102 |
| 4 | Anna | 103 |
courses cədvəli
| id | name |
|---|---|
| 1 | Riyaziyyat |
| 2 | Proqramlaşdırma |
| 3 | Fəlsəfə |
enrollments cədvəli
| student_id | course_id | grade |
|---|---|---|
| 1 | 1 | 90 |
| 1 | 2 | NULL |
| 2 | 1 | 85 |
| 3 | 3 | 70 |
SELECT DISTINCT group_id
FROM students s
WHERE group_id IN (
SELECT s.group_id
FROM students s
JOIN enrollments e ON s.id = e.student_id
WHERE e.grade < 75
)
AND group_id NOT IN (
SELECT s.group_id -- 1-ci səviyyəli iç-içə sorğu
FROM students s
JOIN enrollments e ON s.id = e.student_id
WHERE e.course_id = (
SELECT id FROM courses WHERE name = 'Fəlsəfə' -- 2-ci səviyyəli iç-içə sorğu :P
)
);
- Birinci subquery qiyməti 75-dən aşağı olan tələbə olan qrupları seçir.
- İkinci subquery "Fəlsəfə" kursu ilə əlaqəli qrupları çıxarır.
INvəNOT INilə şərtləri birləşdiririk və nəticəni alırıq.
Nəticə:
| group_id |
|---|
| 101 |
Bu nə qədər faydalıdır?
Real həyatda belə yanaşmalar, məlumatlar arasında mürəkkəb əlaqələri analiz edəndə çox kömək edir. Məsələn:
- Analitikada "xüsusi" müştəri qruplarını (VIP, problemli və s.) seçmək üçün.
- Tövsiyə sistemləri hazırlayanda, istifadəçini bir çox kriteriyaya görə filtrləyəndə.
- Müsahibədə, mürəkkəb SQL-sorğunu optimallaşdırmaq istəyəndə.
Çalış, məşq elə! Bu, ustalığa gedən yoldur.
GO TO FULL VERSION