---
title: "PostgreSQL Materialized View ile ERP Raporlarını 10x Hızlandırmak"
description: "Dashboard raporlarınız çok mu yavaş? Materialized View ile 100.000+ satırlık ERP verilerinden anlık rapor üretmeyi öğrenin. 12 saniye → 0.8 milisaniye."
date: 2026-04-09
category: react-supabase
tags: ["postgresql", "materialized-view", "performans", "supabase", "rapor", "erp"]
url: https://mikroerp.dev/blog/postgresql-materialized-view-erp-rapor-hizlandirma/
---

## Sorun: Rapor 12 Saniye Sürüyor

ERP verilerinizi PostgreSQL'e taşıdınız. İlk aylar güzel çalıştı. Sonra veri birikti. 100.000+ satır oldu. Ve artık basit bir rapor sorgusu 12 saniye sürüyor.

Kullanıcı ne yaşıyor? Sayfa açıldı → boş ekran → 12 sn bekleme... → veriler geldi. Kabul edilemez.

## Neden Bu Kadar Yavaş?

Her sayfa açılışında PostgreSQL şunu yapıyor:

```
100.000+ satırın HEPSİNİ tara
    ↓
Her satırı grupla (şube, ay)
    ↓
Toplamları hesapla
    ↓
96 satırlık özet tablo döndür
```

96 satırlık sonuç için 100.000 satırı taramak israf.

## Çözüm: Materialized View

**Materialized View** (MV), sorgunun sonucunu **önceden hesaplayıp saklıyor**. Sizden rapor istendiğinde tüm tabloyu taramak yerine, hazır sonucu anında döndürüyor.

```
Normal sorgu:  Her seferinde 100K satır tara → 12 saniye
MV sorgusu:    Hazır 96 satırı oku → 0.8 milisaniye
```

**150x hızlanma.**

## Nasıl Yapılır?

### 1. MV Oluşturma

```sql
CREATE MATERIALIZED VIEW mv_aylik_satis AS
SELECT 
    branch AS sube,
    DATE_TRUNC('month', sale_date)::date AS ay,
    SUM(amount) AS toplam_ciro,
    COUNT(*) AS islem_sayisi,
    COUNT(DISTINCT customer_code) AS musteri_sayisi,
    NOW() AS son_guncelleme
FROM sales
WHERE sale_date >= '2023-01-01'
GROUP BY branch, DATE_TRUNC('month', sale_date)::date
WITH DATA;

-- Hızlı sorgulama için index
CREATE INDEX idx_mv_satis ON mv_aylik_satis (sube, ay DESC);
```

Bu komut çalıştığında, PostgreSQL 100K satırı tarayıp sonucu saklıyor. Artık bu saklanan sonuçtan okuyor.

### 2. Otomatik Güncelleme

MV'ler kendiliğinden güncellenmez. Düzenli aralıklarla "yenile" demeniz gerekir:

```sql
-- Manuel yenileme
REFRESH MATERIALIZED VIEW mv_aylik_satis;

-- Supabase'de otomatik (pg_cron ile):
-- Her sabah 07:00'da yenile
SELECT cron.schedule(
    'satis-rapor-guncelle',
    '0 4 * * *',   -- UTC 04:00 = TR 07:00
    'REFRESH MATERIALIZED VIEW CONCURRENTLY mv_aylik_satis'
);
```

> **💡 İpucu**: `CONCURRENTLY` eklerseniz, yenileme sırasında kullanıcılar raporu okumaya devam edebilir. Bunun için unique index gerekir.

### 3. React'tan Kullanma

MV'yi normal tablo gibi sorgularsınız — hiçbir fark yok:

```tsx
const { data } = await supabase
  .from('mv_aylik_satis')   // MV'yi tablo gibi sorgula
  .select('*')
  .eq('sube', 'MERKEZ')
  .order('ay', { ascending: false });
```

## Ne Zaman MV Kullanmalı?

| Durum | MV Kullan? |
|-------|:---:|
| Dashboard özet tabloları | ✅ Evet |
| Aylık/dönemsel raporlar | ✅ Evet |
| Anlık stok durumu | ❌ Hayır (sürekli değişiyor) |
| Form verileri (kaydetme/silme) | ❌ Hayır (anında görünmeli) |

**Temel kural**: Sık okunan ama nadir değişen veriler için MV mükemmeldir.

## Son Güncelleme Bilgisi

Kullanıcıya "bu veri ne zaman güncellendi?" göstermek güven verir:

```
Aylık Satış Raporu
┌──────────┬──────┬──────────────┐
│ Şube     │ Ay   │ Toplam Ciro  │
│ Merkez   │ Mart │ ₺1.245.000   │
│ Adana    │ Mart │ ₺987.000     │
└──────────┴──────┴──────────────┘
Son güncelleme: Bugün 07:00
```

## Dikkat Edilecekler

- **MV disk alanı kullanır** — verileri fiziksel olarak saklar
- **Supabase'de MV'ler RLS desteklemez** — erişim kontrolünü RPC fonksiyonu ile sarmalayın
- **Yenileme süresi** — çok büyük tablolarda birkaç dakika sürebilir

## Bu Yaklaşım Temel Senaryolar İçin Yeter

Tek bir MV ile dashboard raporlarınız 10x-200x hızlanır. Ama birden fazla MV birbirine bağlıysa (birinin sonucu diğerinin girdisi), yenileme sıralaması ve hata yönetimi karmaşıklaşır.

Biz 6+ bağımlı MV'yi koordine eden bir altyapıyı production'da çalıştırıyoruz. İhtiyacınız olursa deneyimimizden faydalanabilirsiniz.

---

*Bu yazı [AstaFlow Case Study](/case-study) serisinin bir parçasıdır.*