---
title: 'Tablo Oluşturma'
source: 'https://academia.sh/tr/kurslar/sql-temelleri/tablo-olusturma'
course: 'SQL Temelleri'
language: tr
updated: '2026-08-17T18:09:02+00:00'
license: 'CC BY-SA 4.0'
---

# Tablo Oluşturma

Sütun tanımının parçaları, alan ve tablo düzeyi kısıtlar, anahtarların şemada ifadesi ve kısıt ihlallerinin motorda ürettiği hata iletileri.

Kursun buraya kadarki bölümü var olan veriyi okumakla geçti: sütun seçtik, koşul yazdık,
tabloları birleştirdik, grupladık ve küme işlemleriyle sonuç kümelerini birbirine ekledik.
Her sorgu, önceden kurulmuş bir şemayı veri olarak aldı. Bu ders o varsayımı kaldırır ve
sıradaki soruyu sorar: şemanın kendisi nasıl yazılır.

SQL Dil Aileleri dersinde ayrılan ailelerden ilki — veri tanımlama dili — bu konunun
alanıdır. Kütüphane şeması o derste kurulmuş ve üç kısıt türü tanıtılmıştı: `PRIMARY KEY`,
`NOT NULL` ve `REFERENCES`. Orada bu bildirimlerin **ne söylediği** anlatıldı; bu ders
neyi **engellediklerini** gösterir ve listeye üçün dışındakileri ekler — varsayılan
değerler, değer aralığı denetimleri, benzersizlik ve adlandırılmış kısıtlar.

Tablo tanımı, o tabloya girebilecek satırların **kuralını** belirler. Kural şemada
durursa motor onu her yazma denemesinde uygular; kural yalnızca uygulama kodunda durursa,
o koda uğramayan her yol kuralı deler. Bu dersin konusu bu farktır.

## Tablo Tanımının Parçaları

`CREATE TABLE` deyiminin gövdesi virgülle ayrılmış öge listesidir. Her öge ya bir sütun
tanımıdır ya da bir tablo düzeyi kısıttır. Sütun tanımı üç parçadan oluşur: ad, tip ve
sıfır veya daha çok **kısıt (constraint)**.

Şemanın en küçük tablosu şubelerdir.

```sh
sqlite3 :memory: <<'SQL'
.headers on
.mode column
CREATE TABLE sube (
  sube_id INTEGER PRIMARY KEY,
  ad      TEXT NOT NULL,
  sehir   TEXT NOT NULL
);

INSERT INTO sube VALUES (1,'Merkez','Ankara'),(2,'Bahçelievler','Ankara');
INSERT INTO sube (sube_id, ad, sehir) VALUES (3,'Kadıköy','İstanbul');

SELECT * FROM sube;
SQL
```

```
sube_id  ad            sehir   
-------  ------------  --------
1        Merkez        Ankara  
2        Bahçelievler  Ankara  
3        Kadıköy       İstanbul
```

Komut satırındaki `:memory:` argümanı, veritabanını diske değil belleğe kurar: örnek
biter bitmez ortadan kalkar. Bu dersin bütün blokları böyle çalışır ve geride dosya
bırakmaz. Sorgu derslerindeki `kutuphane.db` dosyasına dokunulmaz.

## Boşluğu Yasaklamak ve Varsayılan Değer

Veri Modelleme ve İlişkisel Kuram kursunda boş değerin "bilinmiyor" anlamına geldiği ve üç
değerli mantığı tetiklediği gösterilmişti. Bir sütun için boşluk anlamsızsa, bu şemada
söylenir: `NOT NULL`.

`DEFAULT` ise ekleme sırasında değer verilmeyen sütunun neyle doldurulacağını belirtir.
`UNIQUE` de sütundaki değerlerin birbirini tekrarlamamasını ister. Üyeler tablosu üçünü
birden kullanır. Sorgu derslerindeki tanıma göre iki değişiklik vardır: e-posta artık
tekil olmak zorundadır ve üyelik durumunu tutan bir sütun eklenmiştir.

```sh
sqlite3 :memory: <<'SQL'
.headers on
.mode column
CREATE TABLE uye (
  uye_id       INTEGER PRIMARY KEY,
  ad           TEXT NOT NULL,
  soyad        TEXT NOT NULL,
  eposta       TEXT UNIQUE,
  kayit_tarihi TEXT NOT NULL,
  durum        TEXT NOT NULL DEFAULT 'etkin'
);

INSERT INTO uye (uye_id, ad, soyad, eposta, kayit_tarihi)
VALUES (1,'Ayşe','Demir','ayse@ornek.test','2023-02-14');
INSERT INTO uye (uye_id, ad, soyad, eposta, kayit_tarihi)
VALUES (2,'Mehmet',NULL,'mehmet@ornek.test','2023-05-30');
INSERT INTO uye (uye_id, ad, soyad, eposta, kayit_tarihi)
VALUES (3,'Zeynep','Arslan','ayse@ornek.test','2024-01-09');

SELECT * FROM uye;
SQL
```

```
Runtime error near line 14: NOT NULL constraint failed: uye.soyad (19)
Runtime error near line 16: UNIQUE constraint failed: uye.eposta (19)
uye_id  ad    soyad  eposta           kayit_tarihi  durum
------  ----  -----  ---------------  ------------  -----
1       Ayşe  Demir  ayse@ornek.test  2023-02-14    etkin
```

Üç ekleme denendi, biri geçti. Birinci satır `durum` sütununu hiç yazmadığı hâlde `etkin`
değerini aldı: varsayılan değer devreye girdi. İkincisi soyadı boş bıraktı ve `NOT NULL`
kısıtına takıldı. Üçüncüsü zaten kullanılan bir e-posta adresini tekrarladı.

Hata iletisinin yapısı önemlidir: hangi kısıt türü, hangi tablo, hangi sütun. Kısıt
ihlalinde motor **satırı yazmaz**; deyim başarısız olur ve etkisi tümüyle geri alınır.
Kısmen yazılmış satır diye bir şey yoktur.

## Tekillik ve Boş Değer

`PRIMARY KEY`, İlişkisel Kuram kursunda tanımlanan birincil anahtarın şemadaki
karşılığıdır ve iki kısıtın bileşimidir: benzersizlik artı boş olmama. `UNIQUE` yalnız
birincisini ister.

Aradaki farkı boş değer belirginleştirir. Standart SQL'de benzersiz bir sütun birden çok
boş değer taşıyabilir, çünkü iki bilinmeyen değerin eşit olduğu söylenemez. Şemadaki
`eposta` sütunu bunun örneğidir: kimi üyelerin e-postası kayıtlı değildir.

```sh
sqlite3 :memory: <<'SQL'
.headers on
.mode column
CREATE TABLE uye (
  uye_id       INTEGER PRIMARY KEY,
  ad           TEXT NOT NULL,
  soyad        TEXT NOT NULL,
  eposta       TEXT UNIQUE,
  kayit_tarihi TEXT NOT NULL
);

INSERT INTO uye VALUES (3,'Zeynep','Arslan',NULL,'2024-01-09');
INSERT INTO uye VALUES (5,'Selin','Aydın',NULL,'2024-11-05');
INSERT INTO uye VALUES (6,'Burak','Şahin','burak@ornek.test','2025-01-18');

SELECT count(*) AS satir, count(eposta) AS epostasi_olan FROM uye;
SQL
```

```
satir  epostasi_olan
-----  -------------
3      1            
```

İki satır aynı anda boş e-postayla durabildi. Bu davranış motora göre değişebilen bir
noktadır: kimi motorlar birden çok boş değeri kabul eder, kimileri bu kararı tanımlayıcıya
bırakan ek yazımlar sunar. Bir sütunun hem tekil hem zorunlu olması isteniyorsa
`NOT NULL UNIQUE` ikilisi yazılır — ki bu, birincil anahtarın tanımıdır.

Anahtar birden çok sütundan da oluşabilir. Bir üyenin aynı kitabı iki kez sıraya
yazdırmasını engelleyen kural, tablo düzeyinde bileşik bir birincil anahtardır.

```sh
sqlite3 :memory: <<'SQL'
.headers on
.mode column
CREATE TABLE rezervasyon (
  uye_id   INTEGER NOT NULL,
  kitap_id INTEGER NOT NULL,
  tarih    TEXT    NOT NULL,
  PRIMARY KEY (uye_id, kitap_id)
);

INSERT INTO rezervasyon VALUES (1, 5, '2025-07-01');
INSERT INTO rezervasyon VALUES (2, 5, '2025-07-02');
INSERT INTO rezervasyon VALUES (1, 6, '2025-07-03');
INSERT INTO rezervasyon VALUES (1, 5, '2025-07-04');

SELECT * FROM rezervasyon;
SQL
```

```
Runtime error near line 13: UNIQUE constraint failed: rezervasyon.uye_id, rezervasyon.kitap_id (19)
uye_id  kitap_id  tarih     
------  --------  ----------
1       5         2025-07-01
2       5         2025-07-02
1       6         2025-07-03
```

Aynı kitabı farklı üyeler ve aynı üye farklı kitaplar için sıraya girebildi; tekrarlanan
tek şey **çiftin kendisiydi**. Bileşik anahtarda tekillik sütunların ayrı ayrı değil,
birlikte oluşturduğu değer üzerinde tanımlıdır.

## Değer Aralığını Daraltmak

`CHECK`, satır yazılırken doğrulanan bir koşul yazar. Koşul yanlış sonuç verirse satır
kabul edilmez. Üyelik durumunun üç değerden biri olması gibi bir kural, uygulama koduna
değil şemaya böyle taşınır.

```sh
sqlite3 :memory: <<'SQL'
.headers on
.mode column
CREATE TABLE uye (
  uye_id       INTEGER PRIMARY KEY,
  ad           TEXT NOT NULL,
  soyad        TEXT NOT NULL,
  kayit_tarihi TEXT NOT NULL,
  durum        TEXT NOT NULL DEFAULT 'etkin'
                 CHECK (durum IN ('etkin', 'askida', 'kapali'))
);

INSERT INTO uye (uye_id, ad, soyad, kayit_tarihi, durum)
VALUES (1,'Ayşe','Demir','2023-02-14','askida');
INSERT INTO uye (uye_id, ad, soyad, kayit_tarihi, durum)
VALUES (2,'Mehmet','Kaya','2023-05-30','pasif');

SELECT * FROM uye;
SQL
```

```
Runtime error near line 14: CHECK constraint failed: durum IN ('etkin', 'askida', 'kapali') (19)
uye_id  ad    soyad  kayit_tarihi  durum 
------  ----  -----  ------------  ------
1       Ayşe  Demir  2023-02-14    askida
```

Sütun tanımının içine yazılan bir denetim koşulu yalnız o sütuna bakabilir. İki sütunu
birlikte sınayan bir kural gerektiğinde kısıt, sütun listesinin sonuna **tablo düzeyi
kısıt** olarak yazılır. `CONSTRAINT ad` ön eki kısıta ad verir; ad, hata iletisinde
görünür ve hatanın hangi kurala ait olduğunu okunur kılar.

```sh
sqlite3 :memory: <<'SQL'
.headers on
.mode column
CREATE TABLE odunc (
  odunc_id    INTEGER PRIMARY KEY,
  kitap_id    INTEGER NOT NULL,
  uye_id      INTEGER NOT NULL,
  alis_tarihi TEXT    NOT NULL,
  iade_tarihi TEXT,
  CONSTRAINT iade_alistan_sonra
    CHECK (iade_tarihi IS NULL OR iade_tarihi >= alis_tarihi)
);

INSERT INTO odunc VALUES (1, 1, 1, '2025-01-10', '2025-01-24');
INSERT INTO odunc VALUES (3, 1, 2, '2025-02-11', NULL);
INSERT INTO odunc VALUES (4, 3, 3, '2025-03-01', '2025-02-15');

SELECT * FROM odunc;
SQL
```

```
Runtime error near line 15: CHECK constraint failed: iade_alistan_sonra (19)
odunc_id  kitap_id  uye_id  alis_tarihi  iade_tarihi
--------  --------  ------  -----------  -----------
1         1         1       2025-01-10   2025-01-24 
3         1         2       2025-02-11              
```

Koşuldaki `iade_tarihi IS NULL OR` parçası ihmal edilemez. Boş değerle yapılan
karşılaştırma doğru değil **bilinmeyen** sonuç verir; kısıt yalnızca ikinci koşuldan
oluşsaydı, iade edilmemiş ödünçler de reddedilirdi. Boş değerin üç değerli mantığı, kısıt
yazımında en sık tökezlenen yerdir.

## Tablolar Arası Bağ

`REFERENCES` yazımı, bir sütundaki değerin başka bir tablonun anahtarında bulunmasını
şart koşar. Bu, başvuru bütünlüğünün şemadaki ifadesidir: var olmayan bir şubeye kitap
yazılamaz.

Yabancı anahtar denetiminin **açık olup olmadığı motora göre değişir**. Kullanılan komut
satırı aracında denetim öntanımlı olarak kapalıdır ve bağlantı başına açılır.

```sh
sqlite3 :memory: <<'SQL'
.headers on
.mode column
CREATE TABLE sube (sube_id INTEGER PRIMARY KEY, ad TEXT NOT NULL, sehir TEXT NOT NULL);
CREATE TABLE kitap (
  kitap_id   INTEGER PRIMARY KEY,
  baslik     TEXT NOT NULL,
  yazar      TEXT NOT NULL,
  basim_yili INTEGER CHECK (basim_yili BETWEEN 1450 AND 2100),
  sube_id    INTEGER REFERENCES sube(sube_id)
);

INSERT INTO sube VALUES (1,'Merkez','Ankara');
INSERT INTO kitap VALUES (1,'Körlük','José Saramago',1995,9);

SELECT * FROM kitap;
SQL
```

```
kitap_id  baslik  yazar          basim_yili  sube_id
--------  ------  -------------  ----------  -------
1         Körlük  José Saramago  1995        9      
```

Şemada yazan kısıt uygulanmadı; olmayan şube numarasına sahip satır tabloya girdi. Aynı
blok `PRAGMA foreign_keys = ON;` satırıyla açıldığında sonuç değişir.

```sh
sqlite3 :memory: <<'SQL'
.headers on
.mode column
PRAGMA foreign_keys = ON;

CREATE TABLE sube (sube_id INTEGER PRIMARY KEY, ad TEXT NOT NULL, sehir TEXT NOT NULL);
CREATE TABLE kitap (
  kitap_id   INTEGER PRIMARY KEY,
  baslik     TEXT NOT NULL,
  yazar      TEXT NOT NULL,
  basim_yili INTEGER CHECK (basim_yili BETWEEN 1450 AND 2100),
  sube_id    INTEGER REFERENCES sube(sube_id)
);

INSERT INTO sube VALUES (1,'Merkez','Ankara');
INSERT INTO kitap VALUES (1,'Körlük','José Saramago',1995,9);
INSERT INTO kitap VALUES (2,'Tutunamayanlar','Oğuz Atay',1972,1);
INSERT INTO kitap VALUES (7,'Tehlikeli Oyunlar','Oğuz Atay',1973,NULL);

SELECT * FROM kitap;
SQL
```

```
Runtime error near line 15: FOREIGN KEY constraint failed (19)
kitap_id  baslik             yazar      basim_yili  sube_id
--------  -----------------  ---------  ----------  -------
2         Tutunamayanlar     Oğuz Atay  1972        1      
7         Tehlikeli Oyunlar  Oğuz Atay  1973               
```

Olmayan şubeye bağlanan kitap reddedildi, geçerli şubeye bağlanan kabul edildi. Üçüncü
satırın şube numarası boştur ve o da kabul edildi: yabancı anahtar, sütun boş olduğunda
denetim yapmaz — "hangi şubede olduğu bilinmiyor" ifadesi, "olmayan bir şubede" ifadesinden
farklıdır.

`PRAGMA` yazımı standart SQL'in parçası değildir, kullanılan motora aittir. Aktarılabilir
olan ders şudur: **bir kısıtın şemada yazılı olması, denetlendiği anlamına gelmez.** Yeni
bir veritabanıyla çalışmaya başlarken yabancı anahtar denetiminin durumu sınanır; kapalı
bir denetim, aylar sonra fark edilen öksüz satırlar demektir.

## Kütüphane Şeması

Dersteki parçalar bir araya geldiğinde konunun geri kalanında kullanılacak şema çıkar.

```sh
sqlite3 :memory: <<'SQL'
.headers on
.mode column
PRAGMA foreign_keys = ON;

CREATE TABLE sube (
  sube_id INTEGER PRIMARY KEY,
  ad      TEXT NOT NULL,
  sehir   TEXT NOT NULL
);

CREATE TABLE kitap (
  kitap_id   INTEGER PRIMARY KEY,
  baslik     TEXT    NOT NULL,
  yazar      TEXT    NOT NULL,
  basim_yili INTEGER CHECK (basim_yili BETWEEN 1450 AND 2100),
  sube_id    INTEGER REFERENCES sube(sube_id)
);

CREATE TABLE uye (
  uye_id       INTEGER PRIMARY KEY,
  ad           TEXT NOT NULL,
  soyad        TEXT NOT NULL,
  eposta       TEXT UNIQUE,
  kayit_tarihi TEXT NOT NULL,
  durum        TEXT NOT NULL DEFAULT 'etkin'
                 CHECK (durum IN ('etkin', 'askida', 'kapali'))
);

CREATE TABLE odunc (
  odunc_id    INTEGER PRIMARY KEY,
  kitap_id    INTEGER NOT NULL REFERENCES kitap(kitap_id),
  uye_id      INTEGER NOT NULL REFERENCES uye(uye_id),
  alis_tarihi TEXT    NOT NULL,
  iade_tarihi TEXT,
  CONSTRAINT iade_alistan_sonra
    CHECK (iade_tarihi IS NULL OR iade_tarihi >= alis_tarihi)
);

INSERT INTO sube VALUES (1,'Merkez','Ankara'),(3,'Kadıköy','İstanbul');
INSERT INTO kitap VALUES (1,'Körlük','José Saramago',1995,1),(5,'Sessiz Ev','Orhan Pamuk',1983,3);
INSERT INTO uye (uye_id, ad, soyad, eposta, kayit_tarihi)
VALUES (1,'Ayşe','Demir','ayse@ornek.test','2023-02-14'),(3,'Zeynep','Arslan',NULL,'2024-01-09');
INSERT INTO odunc VALUES (1,1,1,'2025-01-10','2025-01-24'),(4,5,3,'2025-03-01',NULL);

SELECT u.ad, u.soyad, k.baslik, s.ad AS sube, o.alis_tarihi
FROM odunc AS o
JOIN uye   AS u ON u.uye_id   = o.uye_id
JOIN kitap AS k ON k.kitap_id = o.kitap_id
JOIN sube  AS s ON s.sube_id  = k.sube_id
ORDER BY o.odunc_id;
SQL
```

```
ad      soyad   baslik     sube     alis_tarihi
------  ------  ---------  -------  -----------
Ayşe    Demir   Körlük     Merkez   2025-01-10 
Zeynep  Arslan  Sessiz Ev  Kadıköy  2025-03-01 
```

Dört tablonun da birincil anahtarı, veriden gelmeyen bir tam sayıdır. Kitaplar için doğal
bir aday vardı — ISBN — ama o, bir **baskıyı** adlandırır; aynı baskının iki nüshası aynı
numarayı taşır ve anahtar nüshaları ayırt edemez. Doğal bir alanın anahtar gibi görünüp
anahtar olamaması, şema tasarımında sık rastlanan bir durumdur ve genellikle bu dersteki
gibi üretilmiş bir anahtarla çözülür.

## Özet

- Sütun tanımı ad, tip ve kısıtlardan oluşur; kısıt şemada durursa motor onu her yazma
  yolunda uygular, uygulama kodunda durursa yalnızca o koddan geçen yolda uygulanır.
- `NOT NULL` boşluğu yasaklar, `DEFAULT` yazılmayan sütunu doldurur, `CHECK` değer
  aralığını daraltır; boş değer alabilen sütunlarda kısıt koşulu boşluğu ayrıca
  karşılamalıdır.
- `UNIQUE` tekilliği, `PRIMARY KEY` tekillik ile zorunluluğu birlikte ister; anahtar
  birden çok sütundan oluşabilir ve tekillik o sütunların birlikte ürettiği değer
  üzerindedir.
- `REFERENCES` başvuru bütünlüğünü şemaya yazar, boş değerli sütunda denetim yapmaz ve
  denetimin açık olup olmadığı motora göre değişir.
- Kısıt ihlalinde deyim tümüyle başarısız olur; hata iletisi kısıt türünü, tabloyu ve
  kısıt adını verdiği için tanı doğrudan okunabilir.

## Sonraki Adım

Şema ilk yazıldığı hâlde kalmaz: yeni bir sütun gerekir, bir sütunun adı yanlış
seçilmiştir, bir kısıt sonradan eklenmek istenir. Sonraki ders şema değiştirmeyi ele alır
ve asıl güçlüğü gösterir: `ALTER TABLE` ile tek deyimde yapılabilen değişiklikler
sınırlıdır ve bu sınır motorlar arasında farklıdır. Sınırın ötesine geçildiğinde tablo
yeniden kurulur; bu işlemin taşınabilir ve veriyi kaybetmeyen biçimi de o derste
kurulacak.
