Lang Azərbaycan
Ehim

12 fevral 2020 - 04:00

CTE (SQL SERVER)

SQL Server 2008-dən başlayaraq tətbiq olunmağa başlanmışdır. Bəzən mürəkkəb sorğuların yazılması zamanı verilənlərin müvəqqəti yadda saxlanılması üçün

CTE (SQL SERVER)

SQL Server 2008-dən başlayaraq tətbiq olunmağa başlanmışdır. Bəzən mürəkkəb sorğuların yazılması zamanı verilənlərin müvəqqəti yadda saxlanılması üçün

12 fevral 2020 - 04:00

CTE(Common table expression)

Azərbaycan dilində ümumi cədvəl ifadəsi kimi tərcümə olunur. SQL Server 2008-dən başlayaraq tətbiq olunmağa başlanmışdır. Bəzən mürəkkəb sorğuların yazılması zamanı verilənlərin müvəqqəti yadda saxlanılması üçün cədvələ ehityac duyulur. Bunun üçün “temporary table” anlayışından istifadə edərkən onu ilk öncə yaratmaq, sonra sorğunun nəticəsini həmin cədvələ insert etmək, sonra isə həmin cədvəli silməyə ehtiyac yarana bilər. Yaxud cədvəl tipli dəyişən anlayışından istifadə oluna bilər. Bu proseslərin hər ikisi zamanı cədvələ sorğunun nəticəsini insert etmək lazım gələcək. Lakin CTE istifadə edən zaman buna lüzum yoxdur. Select,insert,update,delete,create view əmrləri üçün istifadə etmək mümkündür. CTE bir obyekt kimi yadda saxlanılmır və yalnız sorğunun icra müddətində istifadə olunur. Bundan əlavə CTE öz – özünə müraciət edə bilir və eyni zamanda bir sorğu zamanı bir neçə dəfə öz -özünə müraciət edə bilər. Digər tərəfdən sorğuda CTE-in istifadə olunması skriptin daha rahat oxunaqlığını təmin edir.

CTE-in strukturu

---------------------- KOD ----------------------
WITH ifadənin adı [ ( sütün adları [,...n] ) ]
 
AS
 
( İstifadə edəcəyimiz sorğu )
 
Select * from ifadənin adı

Nümunə:Belə bir sorğumuz var

---------------------- KOD ----------------------
select   distinct top 10 name,place from pass_in_trip pt join passenger p on pt.id_psg=p.id_psg

Şəkil 1

Sorğunun nəticəsinidə sərnişinlər və onların təyyarədə əyləşdiyi oturacaqlar göstərilir.

Biz eyni oturacaqda əyləşən sərnişinlərin  qarşılaşmasını çıxardaq.

---------------------- KOD ----------------------
with cte
 
as
 
(select   distinct top 10 name,place from pass_in_trip pt join passenger p on pt.id_psg=p.id_psg)
 
select a.name as aname,b.name as bname from cte a join cte b on a.place=b.place and a.name>b.name

Şəkil 2

Bundan əlavə bir neçə CTE bir sorğuda istifadə oluna bilər. Bir neçə CTE-nin istifadə olunmasının strukturu aşağıdakı kimidir.

---------------------- KOD ----------------------
WITH ifadənin adı1 [ ( sütün adları [,...n] ) ]
 
AS
 
( İstifadə edəcəyimiz sorğu )
 
,ifadə adı2 [ ( sütün adları [,...n] ) ]
 
As
 
(select * from ifadə adı1 where …)
 
Select * from ifadə adı2

Nəzərinizə çatdırım ki eyni anda bir neçə cte-dən istifadə etdikdə yalnız ilk CTE ifadəsinin adının  əvvəlində “with” açar sözü işlənir . Yuxarıdakı sorğunun nəticəsinidə eyni oturacaqda əyləşmiş sərnişinlərin siyahısını göstərdik. Qarşımıza belə bir məsələ qoyulub ki, iki və daha artıq müxtəlif sərnişinlərlə eyni oturacaqda əyləşmiş ( yerin iki sərnişin qarşılaşmasında eyni olmağı mütləqdir, digər qarşılaşma üçün isə eyni olmağı mütləq deyil. Yəni ki A və B sərnişini d oturacağında, B və C sərnişini isə f oturacağında oturmuşsa onda sorğumuzun nəticəsində B sərnişini göstərilməlidir ) sərnişinlərin siyahısın çıxardaq. Aşağıdakı sorğuya nəzər yetirək.

---------------------- KOD ----------------------
with cte
 
as
 
(select   distinct top 10 name,place from pass_in_trip pt join passenger p on pt.id_psg=p.id_psg)
 
,cte1
 
as(
 
select a.name as aname,b.name as bname from cte a join cte b on a.place=b.place and a.name>b.name
 
union
 
select 'Harrison Ford','X'
 
union
 
select 'Jennifer Lopez','Y'
 
)
 
select distinct
 
case when a.aname=b.bname then a.aname
 
when a.aname=b.aname then a.aname
 
end as Passenger
 
from cte1 a,cte1 b
 
where (a.aname=b.bname and a.bname<>b.aname) or (a.aname=b.aname and a.bname<>b.bname)

Qeyd:

---------------------- KOD ----------------------
“union
 
select 'Harrison Ford','X'
 
union
 
select 'Jennifer Lopez','Y' “

bu skripti sorğumuzun nəticəsinin boş qayıtmaması üçün əlavə etdim.

Gördüyünüz kimi biz burda birdən daha artıq CTE ifadəsindən istifadə etdik. Sorğunu çalışdırdıqda aşağıdakı nəticəni alırıq.

Şəkil3

Rekursiya

Müasir VBİS əksəriyyəti – münasibətlidir (relatioonal), yəni verilənlər cədvəllər və sətirlər şəklində təsvir olunub. Buna ikiölçülü təsvir deyilir. Lakin praktikada biz verilənlərin digər təşkilat növləri ilə də rast gəlirik, o cümlədən iyerarxik.

Kompüterimizdəki faylların siyahısına baxsaq onlar ağacvari şəkildə təşkil olnunasını görərik. Digər bir nümunə üçün kitabxananadakı kitabları göstərmək olar. Kitabxana- zal – Şkaf- rəf- kitab. Belə nümünələr çox gətirmək olar. Biz yuxarıdakı təsvirimiz üçün ayrı-ayrı bir neçə cədvəl yarada bilərik. Kitabxana cədvəli, zallar cədvələ, şkaflar cədvələ və s. Lakin bu iyerarxiyanı dərilnliyini biz dəqiq bilməsək yeganə çıxış yolu rekursiyadır.

Problem ondadır ki, iyerarxik şəklində təşkil olunmuş verilənlər relational verilənlər modelinə uyğun gəlmir. SQL -1999 stabdartında rekursiv sorğular dəstəklənməyə başlandı. Oracle VBİS –də bunlar bir qədər əvvəl 8-ci versiyasında istifadə edilməyə başlandı. MS SqL serverdə bir qədər gec SQL 2005 ci versiyasından başlayaraq istifadə edildi. Buna görə də terminologiya və sintaksisdə bir qədər fərqlər var. Məsələn Oracle da bunun adı iyerarxik sorğular gedir, digər VBİS-lərdə isə rekursiv sorğular lakin hər ikisi eyni şeydir.

Cədvəllərdə belə verilənlərin saxlanılması iki üsulu var

  1. Ən çox istifadə olunan üsul. Hər bir cədvəl üçün əcdad obyektləri təyin etmək üçün əlavə sütun yaradılır. Beləliklə biz həmişə əcdad obyektləri asanlıqla təyin edə bilərik.
  2. SQL Server 2008 –ci versiyada verilənləri ağacvari iyerarxiya şəklində saxlamaq üçün yeni verilənlər tipi hierarchyid tipi yaradılmışdır.

Yuxarıda sadaladığımız hallardan başqa CTE-ni rekursiv proseslər zamanı istifadə etmək olar. Tutaq ki, bizə son 45 günün siyahısını çıxartmaq lazımdır( bu gün də daxil olmaqla) . Təbii ki, alqoritm olaraq biz 44 gün əvvəli götürüb üzərinə hər dəfəsində 1 gün gəlməliyik. Bunu CTE ifadəsi ilə aşağıdakı şəkildə yazmaq olar (CTE-nin bu şəkildə yazılması bizi skriptdə “while” və ya “cursor” istifadə etməkdən azad edə bilər)

---------------------- KOD ----------------------
with cte
 
as
 
(select  dateadd(dd,-44,cast(getdate() as date)) date_
 
union all
 
select dateadd(dd,1,date_) d from cte where date_
 
select * from cte

Sorğu necə çalışır? İlkin olaraq select  dateadd(dd,-44,cast(getdate() as date)) date_ bu skiripti işlətməklə 44 gün əvvəlki tarixi alırıq. Daha sonra isə

Union all

select dateadd(dd,1,date_) d from cte

skriptini çalışdırmaqla ilk tarix günündən başlayaraq hər dəfəsində yeni tarixin üzərinə 1 gün əlavə edəcək. Lakin burda bir detala diqqət yetirmək lazımdır – rekursiya nə vaxt tamamlanacaq. Bunun üçün isə where hissəsində şərtimizi göstəririk.

Bu çox sadə bir nümunə idi. Siz də özünüzdə olan rekursiya proseslərini bu şəkildə yaza bilərsiz.

CTE –dən istifadə edərkən aşağıdakı halları nəzərə almaq lazımdır.

  • CTE ifadənin daxilində hər bir atributa (sütuna) ad verilməlidir. Əgər ad verməmisinizsə aşağıdakı xətanı alacaqsız.

Şəkil 4

  • CTE ifadədən dərhal sonra ona müraciət olunmalıdır. Dərhal sonra müraciət olunmazsa aşağıdakı xətanı alacaqsız

Şəkil 5

  • CTE ifadəyə iki və daha artıq ayrı ayrı sorğuda müraciət edə bilərsiniz lakin, nəticə olaraq yalnız ilkin müraciətin nəticəsini qaytaracaq. Digər müraciətlər isə xəta ilə nəticələnəcək.

Şəkil 6

  • CTE-ni rekursiv məqsədlərlə istifadə etdikdə mütləq limit qoyulmalıdır. Yuxarıda istifadə etdiyimiz sorğuda limit qoymadıqda aşağıdakı xətanı almış oluruq. Susmaya görə limit qoyulmadıqda rekursiya sayı 100 götürülür.

Şəkil 7

Şəkil 8

 

100 dəfə müraciət etməklə nəticəni qaytarır (Şəkil 7) və xətanı (Şəkil 8) çıxardır. Rekursiyanın sayını manual olaraq təyin etmək mümkündür. Bunun üçün MAXRECURSION açar sözündən istifadə olunur. Nümunə üçün aşağıdakı skripti göstərmək olar.

---------------------- KOD ----------------------
with cte
 
as
 
(select  dateadd(dd,-44,cast(getdate() as date)) date_
 
union all
 
select dateadd(dd,1,date_) d from cte
 
)
 
select * from cte option (maxrecursion 0)

Burada rekursiyanı 0 seçməklə rekursiyaların sayının limitsiz təyin edirik. Onu da qeyd edim ki, maksimum  rekursiyanın sayını 32767 təyin etmək olar.  Bu zaman xəta çıxsa da sorğunun nəticəsini göstərəcək. Əgər rekursiya sayını 32768 etsək  yalnız aşağıdakı xətanı alacağıq .

Şəkil 9

Sual oluna bilər ki, biz rekursiyanı sıfır təyin etməklə həmin sorğunu dövrə (loop-a) salmış oluruqsa, niyə 0 anlayışı mövcud olsun. Cavab olaraq onu deyim ki, bizim elə proseslərimiz ola bilər ki 32767 dəfədən çox rekursiya gedə bilər. O zaman biz where hissəsində şərtimizi təyin edərək və rekursiyanı 0 seçərək istədiyimiz nəticəni xətasız əldə edə bilərik.

Bu məqaləmdə CTE haqqında bildiklərimi sizinlə bölüşdüm. Oxuduğunuz üçün təşəkkürlər.

Müəllif : Natiq Rzazadə

0 Comments