Skip to content

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:

 

Image

ID
Müşteriyi benzersiz şekilde tanımlayan numara - birincil anahtar
NOM
müşterinin adı
STATUT
I=Birey, E=Şirket, A=Kamu Kurumu
PRENOM
birey olması durumunda ad
CONTACT
Müşterideki irtibat kişisinin soyadı (şirket veya kamu kurumu olması durumunda)
RUE
Müşterinin adresi - cadde
VILLE
şehir
CPOSTAL
posta kodu
TELEPH
Telefon
DEPUIS
Ne zamandan beri müşterisiniz?
DEBITEUR
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:

Image

ISBN
Bir kitabı benzersiz şekilde tanımlayan numara (ISBN = Uluslararası Standart Kitap Numarası) - birincil anahtar
TITRE
Kitabın başlığı
CODEDITEUR
Bir yayınevini benzersiz bir şekilde tanımlayan kod
AUTEUR
Yazarın adı
RESUME
Kitabın özeti
QTEANCOUR
Yıl içinde satılan miktar
QTEANPREC
Bir önceki yıl satılan miktar
DERNVENTE
Son satış tarihi
QTERECUE
Son teslimat miktarı
DERNLIV
Son teslimat tarihi
PRIXVENTE
Satış fiyatı
COUT
Satın alma maliyeti
MINCDE
Minimum sipariş miktarı
MINSTOCK
Minimum stok seviyesi
QTESTOCK
Stokta bulunan miktar

İçeriği şu şekilde olabilir:

Image

6.1.3. COMMANDES tablosu

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

Image

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

Image

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:

Image

NOCMD
Sipariş numarası - NOCMD tablosundaki COMMANDES sütununa referans veren yabancı anahtar
ISBN
Sipariş edilen kitabın numarası - LIVRES tablosundaki ISBN sütununa referans veren yabancı anahtar
QTE
Sipariş edilen miktar

İçeriği şu şekilde olabilir:

Image

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

syntaxe
SELECT sütun1, sütun2, ...
FROM tablo1, tablo2, ..., tablop
WHERE condition
ORDER BY ...
action
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

1
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.
2
WHERE'in condition'i bu tabloya uygulanır. Böylece yeni bir tablo oluşturulur
3
Bu tablo, ORDER'te belirtilen sıralama yöntemine göre sıralanır.
4
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

Image

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

Image

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

Image

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

Image

Birleştirme işlemlerinde uyulması gereken bazı kurallar şunlardır:

  1. 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.
  2. 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

Image

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'

Image

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)

Image

Bu sefer sorumuzun doğru cevabını elde ediyoruz.

6.2.4. İç içe geçmiş sorgular

syntaxe
SELECT sütun[s] FROM tablo[s]
WHERE sorgu operatörü ifadesi
ORDER BY ...
fonctionnement
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:
IN
expression IN (val1, val2, ..., vali): expression'in değeri vali listesindeki öğelerden biri ise doğru.
NOT IN
IN'in tersi
ANY
önüne =, !=, >, >=, <, <= gelmelidir
expression >= ANY (val1, val2, .., valn): expression, listedeki vali değerlerinden birine >= ise doğru
ALL
önüne =, !=, >, >=, <, <= işareti konulmalıdır
expression >= ALL (val1, val2, .., valn): ifade, listedeki tüm vali değerlerine >= ise doğrudur
EXISTS 
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')

Image

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:

SQL> select titre from ARTICLES
    where prixvente > (select avg(prixvente) from ARTICLES)

Image

Ö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

Image

Açıklamalar

  1. DETAILS tablosundan, kitapların ortalama fiyatından daha yüksek fiyatlı kitaplar arasında bulunan ISBN kodları seçilir.
  2. 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.
  3. 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.
  4. 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

Image

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')

Image

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

Image

Ş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)

Image

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

Image

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)

Image

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

SQL>
select distinct nom from clients,commandes
    where clients.id=commandes.idcli

Image

İç içe geçmiş sorgular

SQL> 
select nom from clients
    where id in (select idcli from commandes)

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

syntaxe1
INSERT INTO table (col1, col2, ..) VALUES (val1, val2, ...)
syntaxe2
INSERT INTO table (col1, col2, ..) (requête)
explication
Bu iki sözdizimi sunulmuştur

DELETE

syntaxe1
DELETE FROM table WHERE condition
explication
Bu sözdizimi bilinmektedir. Koşulun, WHERE ifade operatörü (sorgu) sözdizimine sahip bir sorgu içerebileceğini de ekleyelim.

UPDATE

syntaxe1
UPDATE table
SET col1=expr1, col2=expr2, ...
WHERE condition
explication
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.
syntaxe2
UPDATE table
SET (col1, col2, ..) = sorgu1, (cola, colb, ..) = sorgu2, ...
WHERE condition
explication
Farklı sütunlara atanan değerler bir sorgudan gelebilir.