6. SQL dilinin derinlemesine incelenmesi
6.1. Introduction
Bu bölümde
- SELECT komutunun, özellikle aynı anda birden fazla tabloyu sorgulamak için çok güçlü bir sorgu komutu haline getiren diğer sözdizimlerini tanıtacağız.
- Daha önce incelenen komutların genişletilmiş sözdizimlerini
Çeşitli komutları örneklemek amacıyla, bir kitap dağıtım PME'inde sipariş yönetimi için kullanılan aşağıdaki tablolarla çalışacağız:
6.1.1. CLIENTS tablosu
Bu tablo, PME sistemindeki müşterilerle ilgili bilgileri depolar:
![]() |

Müşteriyi benzersiz şekilde tanımlayan numara - birincil anahtar | |
müşterinin adı | |
I=Birey, E=Şirket, A=Kamu Kurumu | |
birey olması durumunda ad | |
Müşterideki irtibat kişisinin soyadı (şirket veya kamu kurumu olması durumunda) | |
Müşterinin adresi - cadde | |
şehir | |
posta kodu | |
Telefon | |
Ne zamandan beri müşterisiniz? | |
Müşterinin şirkete borcu varsa O (Evet), yoksa N (Hayır). |
6.1.2. ARTICLES tablosu
Satılan ürünlerle ilgili bilgileri (burada kitaplar) saklar. Yapısı şöyledir:

Bir kitabı benzersiz şekilde tanımlayan numara (ISBN = Uluslararası Standart Kitap Numarası) - birincil anahtar | |
Kitabın başlığı | |
Bir yayınevini benzersiz bir şekilde tanımlayan kod | |
Yazarın adı | |
Kitabın özeti | |
Yıl içinde satılan miktar | |
Bir önceki yıl satılan miktar | |
Son satış tarihi | |
Son teslimat miktarı | |
Son teslimat tarihi | |
Satış fiyatı | |
Satın alma maliyeti | |
Minimum sipariş miktarı | |
Minimum stok seviyesi | |
Stokta bulunan miktar |
İçeriği şu şekilde olabilir:

6.1.3. COMMANDES tablosu
Müşteriler tarafından verilen siparişlere ilişkin bilgileri kaydeder. Yapısı şu şekildedir:

Bir siparişi benzersiz şekilde tanımlayan numara - birincil anahtar | |
Siparişi veren müşterinin numarası - yabancı anahtar - referans numarası CLIENTS(ID) | |
Bu siparişin girildiği tarih | |
Sipariş iptal edilmişse O (Evet), aksi takdirde N (Hayır). |

6.1.4. DETAILS tablosu
Bu tablo, bir siparişin ayrıntılarını, yani sipariş edilen kitapların referans numaralarını ve miktarlarını içerir. Yapısı şu şekildedir:

Sipariş numarası - NOCMD tablosundaki COMMANDES sütununa referans veren yabancı anahtar | |
Sipariş edilen kitabın numarası - LIVRES tablosundaki ISBN sütununa referans veren yabancı anahtar | |
Sipariş edilen miktar |
İçeriği şu şekilde olabilir:

Yukarıda, 3 numaralı siparişin (NOCMD) üç kitapla ilgili olduğu görülmektedir. Bu, müşterinin aynı anda üç kitap sipariş ettiği anlamına gelir. Bu müşterinin referansları [COMMANDES] tablosunda bulunabilir; burada 3 numaralı siparişin 5 numaralı müşteri tarafından verildiği görülmektedir. [CLIENTS] tablosundan, 5 numaralı müşterinin Segré'deki NetLogos şirketi olduğu anlaşılmaktadır.
6.2. SELECT siparişi
Burada, SELECT siparişi hakkında bilgimizi derinleştirmek amacıyla, bu siparişin yeni sözdizimlerini sunacağız.
6.2.1. Çoklu tablo sorgusunun sözdizimi
SELECT sütun1, sütun2, ... FROM tablo1, tablo2, ..., tablop WHERE condition ORDER BY ... | |
Buradaki yenilik, colonne1, colonne2, ... sütunlarının table1, table2, ... gibi birden fazla tablodan gelmesinden kaynaklanmaktadır. İki tablonun aynı adlı sütunları varsa, tablei.colonnej notasyonu kullanılarak belirsizlik giderilir. condition, farklı tablolardaki sütunları ifade edebilir. |
Çalışma Prensibi
table1, table2, ..., tablep tablolarının kartezyen çarpımı oluşturulur. ni, tablei'in satır sayısı ise, oluşturulan tablo n1*n2*...*np satırdan oluşur ve farklı tabloların tüm sütunlarını içerir. | |
WHERE'in condition'i bu tabloya uygulanır. Böylece yeni bir tablo oluşturulur | |
Bu tablo, ORDER'te belirtilen sıralama yöntemine göre sıralanır. | |
SELECT'te talep edilen sütunlar görüntülenir. |
Örnekler
Daha önce sunulan tablolar kullanılır. 25 Eylül'den sonra verilen siparişlerin ayrıntılarını öğrenmek istiyoruz:
SQL>select details.nocmd,isbn,qte from commandes,details
where commandes.datecmd>'25-sep-91'
and details.nocmd=commandes.nocmd

FROM'in arkasına, sütunlarına başvurulan tüm tabloların adlarının yazıldığına dikkat edilmelidir. Önceki örnekte, seçilen sütunların tümü DETAILS tablosuna aittir. Ancak koşul, COMMANDES tablosuna atıfta bulunmaktadır. Bu nedenle, FROM'in arkasına bu tablonun adının yazılması gerekir. İki farklı tablonun sütunlarının eşitliğini test eden işleme genellikle eşleştirme (equijoin) denir.
SELECT sorgusu şu şekilde de yazılabilirdi:
SQL> select details.nocmd,isbn,qte from commandes
inner join details on details.nocmd=commandes.nocmd
where commandes.datecmd>'25-sep-91'
Örneklere devam edelim. Öncekiyle aynı sonucu istiyoruz, ancak sipariş edilen kitabın numarası yerine kitabın başlığını kullanmak istiyoruz: ISBN:
SQL>select commandes.nocmd, articles.titre, details.qte
from commandes,articles,details
where commandes.datecmd>'25-sep-91'
and details.nocmd=commandes.nocmd
and details.isbn=articles.isbn

Aşağıdaki, daha az okunaklı olan SQL sorgusuyla da aynı sonuç elde edilir:
SQL> select details.nocmd,articles.titre,details.qte from details
inner join commandes on details.nocmd=commandes.nocmd
inner join articles on details.isbn=articles.isbn
where commandes.datecmd>'25-sep-91'
Yukarıda, [DETAILS] tablosuyla iki iç birleştirme yapılmıştır:
- biri, bir kitabın sipariş tarihine erişmek için [COMMANDES] tablosuyla yapılmıştır
- Birincisi, sipariş edilen kitabın başlığına erişmek için [ARTICLES] tablosu
Ayrıca, siparişi veren müşterinin adını da öğrenmek istiyoruz:
SQL>select commandes.nocmd, articles.titre, qte ,clients.nom
from commandes,details,articles,clients
where commandes.datecmd>'25-sep-91'
and details.nocmd=commandes.nocmd
and details.isbn=articles.isbn
and commandes.idcli=clients.id

Ayrıca sipariş tarihlerini ve bu tarihlerin azalan sırayla görüntülenmesini istiyoruz:
SQL>select commandes.nocmd, commandes.datecmd, articles.titre, qte ,clients.nom
from commandes,details,articles,clients
where commandes.datecmd>'25-sep-91'
and details.nocmd=commandes.nocmd
and details.isbn=articles.isbn
and commandes.idcli=clients.id
order by commandes.datecmd descending

Birleştirme işlemlerinde uyulması gereken bazı kurallar şunlardır:
- SELECT'in arkasına, ekranda görüntülenmesini istediğiniz sütunları yazın. Sütun birden fazla tabloda mevcutsa, sütunun önüne tablonun adını yazın.
- FROM'in arkasına, SELECT tarafından taranacak tüm tablolar yazılır; yani, SELECT ve WHERE'in arkasında bulunan sütunlara sahip tablolar.
6.2.2. Otomatik birleştirme
"Using SQL" kitabından daha yüksek satış fiyatına sahip kitapları öğrenmek istiyoruz:
SQL>select a.titre from articles a, articles b
where b.titre='Using SQL'
and a.prixvente>b.prixvente
![]()
Burada birleştirme işleminin iki tablosu da aynıdır: articles tablosu. Bunları birbirinden ayırt etmek için, onlara bir takma ad verilir: from articles a, articles b. İlk tablonun takma adı a, ikincisinin ise b'tir. Bu sözdizimi, tablolar farklı olsa bile kullanılabilir. Bir takma ad kullanılırken, bu takma ad, SELECT komutunun her yerinde, işaret ettiği tablonun yerine kullanılmalıdır.
6.2.3. Dış birleştirme
Eylül ayında bir şey satın almış olan müşterileri, sipariş tarihiyle birlikte öğrenmek istiyoruz. Diğer müşteriler bu tarih olmadan görüntülenir:
SQL>select clients.nom,commandes.datecmd from clients
left outer join commandes on clients.id=commandes.idcli
where datecmd between '01-sep-91' and '30-sep-91'

Burada doğru sonucun çıkmaması şaşırtıcı. [CLIENTS] tablosundaki tüm müşterilerin listede yer alması gerekirken, durum böyle değil. Dış birleşimin işleyişini düşündüğümüzde, satın alma işlemi yapmamış müşterilerin COMMANDES tablosundaki boş bir satırla ve dolayısıyla boş bir tarihle (SQL terminolojisinde NULL değeri). Bu tarih, tarih koşulu ile eşleşmediğinden ilgili müşteri görüntülenmez. Başka bir şey deneyelim:
SQL>select clients.nom,commandes.datecmd from clients
left outer join commandes on clients.id=commandes.idcli
where (commandes.datecmd between '01-sep-91' and '30-sep-91')
or (commandes.datecmd is null)

Bu sefer sorumuzun doğru cevabını elde ediyoruz.
6.2.4. İç içe geçmiş sorgular
SELECT sütun[s] FROM tablo[s] WHERE sorgu operatörü ifadesi ORDER BY ... | |
requête, 0, 1 veya daha fazla değerden oluşan bir grup döndüren bir SELECT komutudur. Böylece, şu türde bir WHERE koşulu elde edilir ifade operatör (val1, val2, ..., vali) şeklinde bir koşul elde ederiz. expression ve vali aynı türde olmalıdır. Sorgu tek bir değer döndürürse, şu türde bir koşula geri dönülür: ifade operatör değer şeklindeki, bizim de iyi bildiğimiz bir koşula geri döneriz. Sorgu bir değerler listesi döndürürse, aşağıdaki operatörleri kullanabiliriz:
expression IN (val1, val2, ..., vali): expression'in değeri vali listesindeki öğelerden biri ise doğru.
IN'in tersi
önüne =, !=, >, >=, <, <= gelmelidir expression >= ANY (val1, val2, .., valn): expression, listedeki vali değerlerinden birine >= ise doğru
önüne =, !=, >, >=, <, <= işareti konulmalıdır expression >= ALL (val1, val2, .., valn): ifade, listedeki tüm vali değerlerine >= ise doğrudur
sorgu: requête en az bir satır döndürürse doğru. |
Örnekler
Daha önce bir eşleştirme ile çözülmüş olan soruyu tekrar ele alalım: 'Using SQL' kitabının satış fiyatından daha yüksek satış fiyatına sahip kitapların başlıklarını görüntülemek.
SQL>select titre from ARTICLES
where prixvente > (select prixvente from ARTICLES where titre='Using SQL')
![]()
Bu çözüm, eşleştirme çözümünden daha sezgisel görünüyor. İlk olarak SELECT ile bir filtreleme yapıyoruz, ardından elde edilen sonuç üzerinde ikinci bir filtreleme yapıyoruz. Bu şekilde arka arkaya birkaç filtreleme işlemi gerçekleştirebiliriz.
Ortalama satış fiyatından daha yüksek satış fiyatına sahip başlıkları öğrenmek istiyoruz:

Önceki sorgunun sonucu olarak ortaya çıkan menkul kıymetleri sipariş eden müşteriler kimlerdir?
SQL>select distinct idcli from COMMANDES,DETAILS
where DETAILS.isbn in
(select isbn from ARTICLES where prixvente
> (select avg(prixvente) from ARTICLES))
and COMMANDES.nocmd=DETAILS.nocmd

Açıklamalar
- DETAILS tablosundan, kitapların ortalama fiyatından daha yüksek fiyatlı kitaplar arasında bulunan ISBN kodları seçilir.
- Bir önceki adımda seçilen satırlarda IDCLI müşteri kodu bulunmamaktadır. Bu kod, COMMANDES tablosunda yer almaktadır. İki tablo arasındaki bağlantı, NOCMD sipariş numarasıyla kurulur; bu nedenle COMMANDES.nocmd=DETAILS.nocmd eşleştirme koşulu kullanılır.
- Aynı müşteri, söz konusu kitaplardan birini birden fazla kez satın almış olabilir; bu durumda, IDCLI kodu birden fazla kez karşımıza çıkar. Bunu önlemek için, DISTINCT anahtar kelimesini SELECT'in arkasına yerleştiririz. Genel olarak DISTINCT, bir SELECT sorgusunun sonuç satırlarındaki yinelenen kayıtları ortadan kaldırır.
- Müşterinin adını elde etmek için, aşağıdaki sorguda gösterildiği gibi COMMANDES ve CLIENTS tabloları arasında ek bir eşleştirme yapmamız gerekir.
SQL> select distinct CLIENTS.nom from COMMANDES,DETAILS,CLIENTS
where DETAILS.isbn in
(select isbn from ARTICLES where prixvente
> (select avg(prixvente) from ARTICLES))
and COMMANDES.nocmd=DETAILS.nocmd
and COMMANDES.IDCLI=CLIENTS.ID

24 Eylül'den beri sipariş vermemiş müşterileri bulmak:
SQL>select nom from CLIENTS
where clients.id not in
(select distinct commandes.idcli from commandes where datecmd>='24-sep-91')

WHERE cümlesi dışında satırları filtreleyebileceğimizi gördük: HAVING cümlesini GROUP ve BY cümleleriyle birlikte kullanarak. HAVING cümlesi satır gruplarını filtreler.
WHERE cümlesinde olduğu gibi, sözdizimi
HAVING expression opérateur requête
sözdizimi de kullanılabilir; ancak daha önce belirtildiği gibi, expression ifadesinin,
GROUP BY expr1, expr2, ...
Örnekler
200F'ten fazla olan kitapların satış miktarları nedir?
Öncelikle, kitap başlıklarına göre satılan miktarları görüntüleyelim:
SQL>select ARTICLES.titre,sum(qte) QTE from ARTICLES, DETAILS
where DETAILS.isbn=ARTICLES.isbn
group by titre

Şimdi, hisse senetlerini filtreleyelim:
SQL> select ARTICLES.titre,sum(qte) QTE from ARTICLES, DETAILS
where DETAILS.isbn=ARTICLES.isbn
group by titre
having titre in (select titre from ARTICLES where prixvente>200)

Belki daha açık bir şekilde şöyle yazılabilirdi:
SQL>select ARTICLES.titre,sum(qte) QTE from ARTICLES, DETAILS
where DETAILS.isbn=ARTICLES.isbn
and ARTICLES.prixvente>200
group by titre

6.2.5. İlişkili sorgular
İç içe geçmiş sorgularda, bir üst sorgu (en dıştaki sorgu) ve bir alt sorgu (en içteki sorgu) bulunur. Üst sorgu, alt sorgu tamamen değerlendirildikten sonra değerlendirilir.
İlişkili sorgular, aşağıdaki küçük ayrıntı dışında aynı sözdizimine sahiptir: alt sorgu, üst sorgunun tablosuyla birleştirme işlemi gerçekleştirir. Bu durumda, üst sorgu-alt sorgu kümesi, üst tablonun her satırı için tekrar tekrar değerlendirilir.
Örnek
24 Eylül'den bu yana sipariş vermemiş müşterilerin isimlerini almak istediğimiz örneği tekrar ele alalım:
SQL>
select nom from clients
where not exists
(select idcli from commandes
where datecmd>='24-sep-91'
and commandes.idcli=clients.id)

Ana sorgu, clients tablosu üzerinde gerçekleştirilir. Alt sorgu ise clients ve commandes tabloları arasında bir birleştirme yapar. Dolayısıyla, burada bir ilişkili sorgu söz konusudur. clients tablosundaki her satır için alt sorgu çalıştırılır: 24 Eylül'den sonra yapılan siparişlerde müşterinin id kodunu arar. Eğer bulamazsa (not exists), müşterinin adı görüntülenir. Ardından, clients tablosunun bir sonraki satırına geçilir.
6.2.6. SELECT sorgusunun yazımında seçim kriterleri
SELECT tablosuna farklı yazma işlemleriyle aynı sonuca ulaşılabileceğini defalarca gördük. Bir örnek verelim: Bir şey sipariş etmiş müşterileri görüntülemek:
Birleştirme

İç içe geçmiş sorgular
aynı sonucu verir.
İlişkili sorgular
SQL>
select nom from clients
where exists (select * from commandes where commandes.idcli=clients.id)
aynı sonucu verir.
Yazarlar Christian MAREE ve Guy LEDANT, 'SQL, Giriş, Programlama ve Uzmanlık' adlı kitaplarında bazı seçim kriterleri önermektedir:
Performans
Kullanıcı, SGBD'in istediği sonuçları bulmak için "nasıl bir yol izlediğini" bilmez. Dolayısıyla, yalnızca deneyim yoluyla, belirli bir yazım biçiminin diğerine göre daha verimli olduğunu keşfedecektir. MAREE ve LEDANT, deneyimlerine dayanarak, ilişkili sorguların genellikle iç içe geçmiş sorgulardan veya birleşimlerden daha yavaş göründüğünü belirtmektedir.
Formülasyon
İç içe geçmiş sorgularla formülasyon, birleştirme işlemine kıyasla genellikle daha okunaklı ve sezgiseldir. Ancak her zaman kullanılamaz. Özellikle iki noktaya dikkat edilmelidir:
- SELECT sorgusundaki argüman sütunlarına (SELECT col1, col2, ...) ait tablolar, FROM anahtar sözcüğünün ardından belirtilmelidir. Bu durumda bu tabloların kartezyen çarpımı gerçekleştirilir; buna birleştirme denir.
- Sorgu tek bir tablodan gelen sonuçları görüntülüyorsa ve bu tablodaki satırların filtrelenmesi başka bir tablonun sorgulanmasını gerektiriyorsa, iç içe geçmiş sorgular kullanılabilir.
6.3. Sözdizimi Genişletmeleri
Kolaylık olması açısından, çoğu zaman çeşitli komutların kısaltılmış sözdizimlerini sunduk. Bu bölümde ise genişletilmiş sözdizimlerini sunuyoruz. Bunlar, ayrıntılı olarak incelenen SELECT komutunun sözdizimlerine benzer oldukları için kendiliğinden anlaşılır.
INSERT
INSERT INTO table (col1, col2, ..) VALUES (val1, val2, ...) | |
INSERT INTO table (col1, col2, ..) (requête) | |
Bu iki sözdizimi sunulmuştur |
DELETE
DELETE FROM table WHERE condition | |
Bu sözdizimi bilinmektedir. Koşulun, WHERE ifade operatörü (sorgu) sözdizimine sahip bir sorgu içerebileceğini de ekleyelim. |
UPDATE
UPDATE table SET col1=expr1, col2=expr2, ... WHERE condition | |
Bu sözdizimi daha önce tanıtılmıştı. Şunu da ekleyelim: Koşul, WHERE ifade operatörü (sorgu) sözdizimine sahip bir sorgu içerebilir. |
UPDATE table SET (col1, col2, ..) = sorgu1, (cola, colb, ..) = sorgu2, ... WHERE condition | |
Farklı sütunlara atanan değerler bir sorgudan gelebilir. |
