---
title: 'Boyutsal Modelleme'
source: 'https://academia.sh/tr/kurslar/veri-depolama-mimarileri/boyutsal-modelleme'
course: 'Veri Depolama Mimarileri'
language: tr
updated: '2026-08-17T18:08:50+00:00'
license: 'CC BY-SA 4.0'
---

# Boyutsal Modelleme

Olgu ile boyutun ayrılmasının sakladığı hücreyi ve sorguya ödettiği birleştirmeyi ölçme: aynı 2200 satır tek düz tabloda 15400 hücre tutarken olgu artı iki boyut düzeninde 7236 hücre tutuyor, 8164 hücre eksiliyor ve iki düzenin ürettiği tip başına toplam birebir aynı kalıyor; karşılığında sorgu tek dosyaya dokunup maliyet 4520 öderken olgu artı boyut iki dosyaya dokunup 4800 hücre okuyor, maliyet 5040 oluyor ve sıfır birleştirme 2200'e çıkıyor; bir bölgenin adı düzeltildiğinde düz tabloda 506 satır, boyutta 46 satır güncelleniyor.

Altı derste verinin **nerede** durduğu ayrıldı: şema dayatan katman, ham katman, ikisini birleştiren
kurulum, alan odaklı alt kümeler, sahipliğin dağıtıldığı düzenler ve varlığı bulmayı sağlayan
katalog. Hepsinin ortak varsayımı vardı: satırlar bir yerde durur, biri onları arar. Hiçbir ölçüm
satırların **hangi biçimde** durduğunu sormadı.

Bu ders o soruyu sorar ve en eski karardan başlar: ölçülen değer ile ölçümü niteleyen bilgi aynı
tabloda mı durmalı. Sayaç okumasından çıkan tüketim bir sayıdır; abonenin bölgesi ve tipi, dönemin
yılı ve ayı o sayıyı niteler. Tek tablo ikisini yan yana taşır. Boyutsal modelleme onları ayırır.
Ayrımın saklanan hücreye, sorgunun maliyetine ve **çıkan sonuca** ne yaptığı sayılabilir bir şeydir.

- **AM1.** Kaynak, hat ve depo **kurgudur**; gerçek bir ambar, göl, dosya biçimi ya da sorgu motoru
  çağrılmaz. Tohum **20260218**, ölçü koşumdan bağımsızdır.
- **AM2.** Hattan çıkan satır **2200**'dür: 200 abone ile on iki dönemin çarpımı 2400, ilk dönemin
  tüketimi hesaplanamadığı için 200 satır düşer.
- **AM3.** **Olgu tablosu (fact table)** ölçülen değeri taşır; **boyut bağıntısı (dimension table)** o
  değeri niteleyen bilgiyi taşır. Olgunun **taneciği (grain)** abone ile dönemin çiftidir.
- **AM4.** Saklanan hücre satır ile sütunun çarpımıdır. Bir alan bir hücredir; alanın genişliği
  ölçülmez.
- **AM5.** Maliyet `dosya x 120 + hücre` biçiminde okunur. **Dosya sabiti 120 bir seçimdir**; dosya
  sayısına duyarlı her sonuç sabitin değeriyle birlikte yazılır.
- **AM6.** **Birleştirme (join)** sayısı, sorgunun boyut bağıntısına kaç kez baktığıdır. Ham süre
  yazılmaz.
- **AM7.** Taban çizgisi **tek düz tablodur**: bölümlemesiz, tek dosya, birleştirme sayısı **0**.
- **AM8.** Çözünürlük: satır oranının adımı **0,0005**. **Toplamlar oran olarak değil, değerin
  kendisi olarak yazılır.**

## Tek Tablonun Taşıdığı

Hattın beşinci aşamasından önceki satır yedi alan taşır: abone, dönem, bölge, tip, endeks, birim ve
hesaplanmış tüketim. Bu satırı olduğu gibi diske indirmek en az karar gerektiren düzendir; sorgu tek
bir yere bakar, hiçbir tabloyu bir başkasıyla eşlemek zorunda kalmaz. Karşılığında abonenin bölgesi
o abonenin her satırında yeniden yazılır. Bir abonenin on bir satırı varsa bölge adı on bir kez
saklanır.

İlk blok ortak tanımı kurar: kurgu kaynak, hattın satır üreten üç aşaması, düzen yazma ve maliyet
ölçen sorgu. Boyutsal ayrım `yildiz` işlevindedir: olguya yalnızca abone, dönem ve tüketim kalır,
niteleyen alanlar iki boyut bağıntısına çıkar.

```python
# KURGUDUR. Sayac okumalari, hat ve depo ders icinde tanimlanir; gercek bir
# ambar, gol, dosya bicimi ya da sorgu motoru cagrilmaz.
TOHUM, M32 = 20260218, 0xFFFFFFFF


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

    def sonraki():
        s[0] = (s[0] * 1664525 + 1013904223) & M32
        return s[0] / 4294967296
    return sonraki


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def kaynak_uret():
    kayit = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            kayit.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                          "endeks": e, "birim": "m3"})
    return kayit


def a1_ayikla(kayit):
    ZOR = ("abone", "donem", "bolge", "endeks", "birim")
    tut = [k for k in kayit if all(a in k for a in ZOR)]
    return tut, len(kayit) - len(tut)


def a2_tekille(kayit):
    gor, tut = set(), []
    for k in kayit:
        ad = (k["abone"], k["donem"])
        if ad in gor:
            continue
        gor.add(ad)
        tut.append(k)
    return tut, len(kayit) - len(tut)


def a3_tuketim(kayit):
    tablo = {(k["abone"], k["donem"]): k for k in kayit}
    cikti, dusen = [], 0
    for k in kayit:
        i = DONEM.index(k["donem"])
        if i == 0:
            continue
        onceki = tablo.get((k["abone"], DONEM[i - 1]))
        if onceki is None:
            dusen += 1
            continue
        try:
            f = int(k["endeks"]) - int(onceki["endeks"])
        except (TypeError, ValueError):
            dusen += 1
            continue
        cikti.append(dict(k, m3=f))
    return cikti, dusen


SUTUN = ("abone", "donem", "bolge", "tip", "endeks", "birim", "m3")
DOSYA_SABIT = 120          # her dosyaya dokunmanin sabit bedeli , hucre cinsinden


def satirlar():
    v, _ = a1_ayikla(kaynak_uret())
    v, _ = a2_tekille(v)
    v, _ = a3_tuketim(v)
    return v


def yaz(kayitlar, bolumleme=()):
    duzen = {}
    for k in kayitlar:
        anahtar = tuple(k[a] for a in bolumleme)
        duzen.setdefault(anahtar, []).append(k)
    return duzen


def sorgu(duzen, bolumleme, secilen, suzgec=None, sutunlu=True):
    suzgec = {} if suzgec is None else suzgec
    budayan = {a: v for a, v in suzgec.items() if a in bolumleme}
    kalan = {a: v for a, v in suzgec.items() if a not in bolumleme}
    dosya, hucre, satir = 0, 0, 0
    okunan_sutun = len(secilen) if sutunlu else len(SUTUN)
    for anahtar, kayit in duzen.items():
        if budayan:
            eslesme = dict(zip(bolumleme, anahtar))
            if any(eslesme[a] != v for a, v in budayan.items()):
                continue
        dosya += 1
        hucre += len(kayit) * okunan_sutun
        satir += sum(1 for k in kayit if all(k[a] == v for a, v in kalan.items()))
    return {"dosya": dosya, "hucre": hucre, "eslesen_satir": satir,
            "maliyet": dosya * DOSYA_SABIT + hucre}


def yildiz(kayitlar):
    """Olgu tablosu artı iki boyut. Niteleyen alanlar olgudan cikarilir."""
    olgu = [{"abone": k["abone"], "donem": k["donem"], "m3": k["m3"]} for k in kayitlar]
    d_abone = {k["abone"]: {"bolge": k["bolge"], "tip": k["tip"]} for k in kayitlar}
    d_donem = {d: {"yil": d[:4], "ay": d[5:]} for d in DONEM}
    return olgu, {"abone": d_abone, "donem": d_donem}
```

## Ayrımın Sakladığı Hücre

İkinci blok iki düzeni yan yana kurar ve saklanan hücreyi sayar. Ölçümün dürüst olması için bir ara
satır gerekir. `yildiz` işlevi olguyu kurarken endeks ile birim alanlarını hiç taşımaz, çünkü
tüketim hesaplandıktan sonra ikisine de gerek kalmaz. Bu, boyutsal ayrımın değil **alan düşürmenin**
kazancıdır ve ayrı sayılmazsa ayrıma yazılır. Tablo bu yüzden düz tabloyu iki kez listeler: yedi
alanla ve olgunun taşıdığı beş alanla. Ardından aynı soru iki düzene de sorulur: tip başına toplam
tüketim ne kadar.

```python
SATIR = satirlar()
OLGU, BOYUT = yildiz(SATIR)
TASINAN = ("abone", "donem", "bolge", "tip", "m3")
print(f"{'tablo':<22}{'satir':>7}{'sutun':>7}{'saklanan hucre':>16}")
for ad, n, s in (("duz tablo , yedi alan", len(SATIR), len(SUTUN)),
                 ("duz tablo , bes alan", len(SATIR), len(TASINAN)),
                 ("olgu", len(OLGU), 3),
                 ("boyut abone", len(BOYUT["abone"]), 3),
                 ("boyut donem", len(BOYUT["donem"]), 3)):
    print(f"{ad:<22}{n:>7}{s:>7}{n * s:>16}")
duz7 = len(SATIR) * len(SUTUN)
duz5 = len(SATIR) * len(TASINAN)
yil = len(OLGU) * 3 + len(BOYUT["abone"]) * 3 + len(BOYUT["donem"]) * 3
print(f"{'yildiz toplam':<22}{'':>7}{'':>7}{yil:>16}")
print("alan dusurmenin kazandirdigi:", duz7 - duz5)
print("ayrimin kazandirdigi:", duz5 - yil, " boyut satiri:",
      len(BOYUT["abone"]) + len(BOYUT["donem"]))


def duz_tip_toplam(kayitlar):
    top = {}
    for k in kayitlar:
        top[k["tip"]] = top.get(k["tip"], 0) + k["m3"]
    return top, 0


def yildiz_tip_toplam(olgu, boyut):
    top, birlestirme = {}, 0
    for o in olgu:
        t = boyut["abone"][o["abone"]]["tip"]
        birlestirme += 1
        top[t] = top.get(t, 0) + o["m3"]
    return top, birlestirme


t_duz, b_duz = duz_tip_toplam(SATIR)
t_yil, b_yil = yildiz_tip_toplam(OLGU, BOYUT)
print()
print("duz sonuc   :", t_duz, " birlestirme:", b_duz)
print("yildiz sonuc:", t_yil, " birlestirme:", b_yil)
print("sonuc birebir ayni:", t_duz == t_yil)
```

```
tablo                   satir  sutun  saklanan hucre
duz tablo , yedi alan    2200      7           15400
duz tablo , bes alan     2200      5           11000
olgu                     2200      3            6600
boyut abone               200      3             600
boyut donem                12      3              36
yildiz toplam                                   7236
alan dusurmenin kazandirdigi: 4400
ayrimin kazandirdigi: 3764  boyut satiri: 212

duz sonuc   : {'isyeri': 29932, 'mesken': 28522}  birlestirme: 0
yildiz sonuc: {'isyeri': 29932, 'mesken': 28522}  birlestirme: 2200
sonuc birebir ayni: True
```

Toplam düşüş **15400**'den **7236**'ya, yani **8164 hücre**. Bu sayının **4400**'ü ayrıma ait
değildir: endeks ile birim alanlarının artık taşınmaması iki sütunu, yani 2200 satırda 4400 hücreyi
düşürüyor. Aynı işlem düz tabloda da yapılabilirdi ve yapıldığında düz tablo **11000** hücreye
inerdi. Boyutsal ayrımın kendi kazancı bu satırla yıldız toplamının farkıdır: **3764 hücre**.

Bu 3764 hücre tekrarın kendisidir. Bölge ve tip alanları 2200 satırda değil 200 satırda duruyor,
dönemin yılı ve ayı 2200 satırda değil on iki satırda. Boyut tablolarının tamamı **212 satır**
tutuyor. Kazanç, olgu satırının boyut satırına oranından gelir: her abonenin on bir dönemi olduğu
için niteleyen her alan on bir kez değil bir kez yazılıyor. Bu oran birebire yaklaşsaydı, yani her
abonenin tek dönemi olsaydı, ayrımın saklama tarafında kazandıracağı bir şey kalmazdı.

Sonuç satırı ayrımın ilk sınavıdır ve geçer: iki düzen aynı sözlüğü üretiyor. Boyutsal modelleme
veriyi özetlemez, sıkıştırmaz, örneklemez; yalnızca **nerede yazıldığını** değiştirir. Sonuç
tarafında hiçbir borç bırakmaması, bundan sonraki bütün ölçümlerin tek bir eksene indirgenmesini
sağlar: aynı cevap, farklı bedel.

## Sorgunun Ödediği Birleştirme

Bedel sonuçta değil, sonuca ulaşma yolundadır. Düz tabloda tip ile tüketim aynı satırda durduğu için
sorgu tek dosyayı okur ve hiçbir eşleme yapmaz. Olgu tablosunda tip yoktur; sorgu her olgu satırı
için boyut bağıntısına bakmak zorundadır. Üçüncü blok iki yolun dokunduğu dosyayı, okuduğu hücreyi ve
ödediği maliyeti sayar.

```python
d_abone = [{"abone": a, **v} for a, v in BOYUT["abone"].items()]
q_duz = sorgu(yaz(SATIR, ()), (), ("tip", "m3"))
q_olgu = sorgu(yaz(OLGU, ()), (), ("abone", "m3"))
q_boyut = sorgu(yaz(d_abone, ()), (), ("abone", "tip"))
print(f"{'okuma':<22}{'dosya':>7}{'hucre':>7}{'maliyet':>9}{'birlestirme':>13}")
print(f"{'duz tablo':<22}{q_duz['dosya']:>7}{q_duz['hucre']:>7}"
      f"{q_duz['maliyet']:>9}{0:>13}")
print(f"{'olgu':<22}{q_olgu['dosya']:>7}{q_olgu['hucre']:>7}{q_olgu['maliyet']:>9}{'':>13}")
print(f"{'boyut abone':<22}{q_boyut['dosya']:>7}{q_boyut['hucre']:>7}"
      f"{q_boyut['maliyet']:>9}{'':>13}")
print(f"{'olgu + boyut':<22}{q_olgu['dosya'] + q_boyut['dosya']:>7}"
      f"{q_olgu['hucre'] + q_boyut['hucre']:>7}"
      f"{q_olgu['maliyet'] + q_boyut['maliyet']:>9}{len(OLGU):>13}")
print("dosya sabiti:", DOSYA_SABIT, "hucre")
print()
print(f"{'degisiklik':<26}{'duz tablo':>11}{'boyut':>7}")
print(f"{'A007 tipi duzeltiliyor':<26}"
      f"{sum(1 for k in SATIR if k['abone'] == 'A007'):>11}{1:>7}")
print(f"{'kuzey adi degisiyor':<26}"
      f"{sum(1 for k in SATIR if k['bolge'] == 'kuzey'):>11}"
      f"{sum(1 for v in BOYUT['abone'].values() if v['bolge'] == 'kuzey'):>7}")
print()
BOYUT_SABIT = len(BOYUT["abone"]) * 3 + len(BOYUT["donem"]) * 3
print("boyutun sabit hucresi:", BOYUT_SABIT)
print(f"{'olgu satiri':>12}{'duz bes alan':>14}{'yildiz':>9}{'kazanc':>9}")
for n in (550, 1100, 2200, 4400):
    print(f"{n:>12}{5 * n:>14}{3 * n + BOYUT_SABIT:>9}{2 * n - BOYUT_SABIT:>9}")
```

```
okuma                   dosya  hucre  maliyet  birlestirme
duz tablo                   1   4400     4520            0
olgu                        1   4400     4520             
boyut abone                 1    400      520             
olgu + boyut                2   4800     5040         2200
dosya sabiti: 120 hucre

degisiklik                  duz tablo  boyut
A007 tipi duzeltiliyor             11      1
kuzey adi degisiyor               506     46

boyutun sabit hucresi: 636
 olgu satiri  duz bes alan   yildiz   kazanc
         550          2750     2286      464
        1100          5500     3936     1564
        2200         11000     7236     3764
        4400         22000    13836     8164
```

Tablonun ilk yarısı kursun kuralını bu derse indirir. Saklamada kazanılan **8164 hücre** okumada
geri ödenmiyor, üstüne konuyor: aynı soruyu yanıtlamak düz tabloda **4520**, olgu artı boyutta
**5040** maliyet istiyor. Fark **520**'dir ve iki parçadan oluşur: boyut bağıntısının 400 hücresi ve
ikinci dosyaya dokunmanın **120** hücrelik sabit bedeli. Dosya sabiti 120 seçildiği için ikinci
dosya bu kadar tutuyor; sabit daha büyük seçilseydi ayrımın okuma tarafındaki bedeli de büyürdü.

Birleştirme sütunu farkın nereden geldiğini söyler. Düz tabloda **0**, olgu artı boyutta **2200**:
her olgu satırı için boyuta bir kez bakılıyor. Bu, hattan çıkan satır sayısıyla birebir aynı sayıdır
ve tesadüf değildir; boyutsal düzenin okuma tarafındaki bedeli olgu bağıntısının satır sayısına
bağlıdır. Düzenin sayısı sakladığı satır değil, **bir sorgunun ona dokunmak için ödediğidir**; bu
sorgu için ödenen 5040'tır ve hangi sorgu olduğu yazılmadan bu sayının anlamı yoktur.

## Değişikliğin Dokunduğu Satır

Tablonun ikinci yarısı ayrımın okuma dışındaki karşılığını verir. Bir abonenin tipi yanlış
kaydedilmişse düz tabloda o abonenin **11** satırı düzeltilir, boyutta **1** satır. Bir bölgenin adı
değişirse düz tabloda **506** satır güncellenir, boyutta **46**. İkisinde de değişen bilgi tektir;
değişen satır sayısı, o bilginin kaç kez yazıldığına eşittir.

Bu, ayrımın en somut kazancıdır ve tekrarın doğrudan sonucudur. Aynı bilgi 506 yerde duruyorsa
güncellemenin 506 yerin hepsine ulaşması gerekir; ulaşmadığı yer kalırsa aynı bölge iki adla
görünür. Boyut tablosu güncellemenin dokunduğu yüzeyi küçültür, yani tutarsızlığın oluşabileceği
yeri küçültür. Karşılığı sorgu başına 2200 birleştirmedir ve bu takas, boyutsal modellemenin
tamamıdır.

Son tablo iki bedelin ölçekle ilişkisini verir. Boyut tablolarının hücresi **636**'dır ve olgu
büyüdükçe büyümez: abone sayısı sabit kaldığı sürece 200 satır 200 satır kalır. Beş alanlı düz
tablo `n` satırda `5n` hücre tutarken yıldız `3n + 636` tutar, yani ayrımın kazancı `2n - 636`'dır.
Ölçülen 2200 satırda bu **3764** eder ve blok bunu doğrular. Sayı 550 satırda **464**'e iner, 4400
satırda **8164**'e çıkar. Okuma tarafındaki bedel ise ölçekten bağımsızdır: boyut hangi sorguda
olursa olsun bir kez okunur ve **520** tutar. Ayrımın küçük tabloda tartışmalı, büyük tabloda
kaçınılmaz görünmesinin nedeni budur; iki durumda da ölçülen şey aynı iki sayıdır.

## Özet

- Olgu tablosu ölçülen değeri, boyut bağıntısı o değeri niteleyen bilgiyi taşır; taneciği abone ile
  dönemin çifti olan 2200 satır iki düzende de aynı veriyi anlatır.
- Saklanan hücre **15400**'den **7236**'ya iner, ama düşüşün **4400**'ü artık taşınmayan iki alana
  aittir; ayrımın kendi kazancı **3764 hücredir** ve boyut bağıntılarının tamamı **212 satır** tutar.
- İki düzenin ürettiği tip başına toplam **birebir aynıdır**; boyutsal modelleme sonucu değil,
  sonuca ulaşma yolunu değiştirir.
- Bedel okumada ödenir: aynı sorgu düz tabloda **4520**, olgu artı boyutta **5040** maliyet ister ve
  birleştirme **0**'dan **2200**'e çıkar; farkın 120'si dosya sabitidir.
- Ayrımın okuma dışındaki karşılığı güncellemedir: bir bölge adı düz tabloda **506**, boyutta **46**
  satıra dokunur, yani tutarsızlığın oluşabileceği yüzey on bir kat küçülür.

## Sonraki Adım

Bu derste boyut bağıntıları düzleştirilmiş kuruldu: abonenin bölgesi bölge adının kendisi olarak, 200
satırın her birinde yazıldı. Bölge kümesi beş elemanlıdır, yani aynı beş metin boyut bağıntısında
kırk kez tekrarlanıyor. Düz tabloda tekrarı kaldırmak için uygulanan işlem burada da uygulanabilir:
bölgeyi ayrı bir tabloya çıkarıp boyutta yalnızca bir başvuru bırakmak. Bu, olgu ile boyut arasında
bir değil iki adımlık bir zincir kurar. Sonraki ders bu zinciri sayar: normalleştirme derecesi
arttıkça kaç satır kazanılıyor, karşılığında kaç birleştirme ödeniyor ve sonuç kımıldıyor mu.
