---
title: "Mikro ERP SQL Server Performans Optimizasyonu: Yavaşlığın 7 Kaynağı ve Çözüm Rehberi"
description: "Mikro ERP veritabanınız yavaş mı çalışıyor? STOK_HAREKETLERI ve CARI_HESAP_HAREKETLERI tablolarında index stratejisi, TempDB optimizasyonu, Wait Stats analizi, Execution Plan okuma ve otomatik bakım planı ile SQL Server performansını 5-10x artırın."
date: 2026-06-28
category: mikro-erp
tags: ["mikro-erp", "sql-server", "performans", "optimizasyon", "index", "tempdb", "wait-stats", "astaflow"]
url: https://mikroerp.dev/blog/mikro-erp-sql-server-performans-optimizasyonu-rehberi/
---

Mikro ERP'de en sık duyduğumuz şikayet: **"Program çok yavaş."** Fatura kaydı 10 saniye sürüyor, stok raporu 2 dakikada açılıyor, cari ekstre sorgusu timeout veriyor.

Sorun genellikle Mikro yazılımının kendisinde değil, **SQL Server yapılandırmasında** ve **veritabanı bakımsızlığında.** Bu rehber, Mikro ERP veritabanınızdaki yavaşlığın gerçek kaynaklarını tespit etmenizi ve adım adım çözmenizi sağlayacak.

## Başlamadan Önce: Teşhis → Tedavi

Performans optimizasyonunda en büyük hata **körlemesine index eklemek** veya **donanım yükseltmek**tir. Doğru yaklaşım:

```
1. Ölç    → Yavaşlığın kaynağını tespit et (Wait Stats)
2. Analiz → Sorunlu sorguları bul (Execution Plan)
3. Uygula → Hedefe yönelik çözüm (Index, TempDB, Ayar)
4. Doğrula → İyileşmeyi ölç, yeni darboğaz var mı kontrol et
```

> ⚠️ **Uyarı:** Bu rehberdeki tüm SQL komutlarını önce **test ortamında** deneyin. Canlı veritabanında değişiklik yapmadan önce mutlaka **tam yedek** alın.

---

## 1. SQL Server Sürümü: Express Tuzağı

Mikro ERP kurulumlarının büyük çoğunluğu **SQL Server Express** ile gelir. Express sürümünün kritik sınırlamaları:

| Özellik | Express | Standard | Developer |
|---|---|---|---|
| **Maksimum RAM** | 1,4 GB | 128 GB | Sınırsız |
| **Maksimum CPU** | 4 çekirdek | 24 çekirdek | Sınırsız |
| **Veritabanı Boyutu** | 10 GB | 524 PB | Sınırsız |
| **Paralel Sorgu** | Yok | Var | Var |
| **Fiyat** | Ücretsiz | Lisanslı | Ücretsiz (üretim dışı) |

### Veritabanı Boyutunuzu Kontrol Edin

```sql
-- Mikro ERP veritabanı boyutunu kontrol et
SELECT
    DB_NAME(database_id)          AS Veritabani,
    type_desc                     AS Dosya_Tipi,
    name                          AS Dosya_Adi,
    size * 8 / 1024               AS Boyut_MB,
    CAST(size * 8.0 / 1024 / 1024 AS DECIMAL(10,2)) AS Boyut_GB
FROM sys.master_files
WHERE DB_NAME(database_id) NOT IN ('master', 'model', 'msdb', 'tempdb')
ORDER BY size DESC;
```

**Eğer veritabanınız 7-8 GB'ı aşıyorsa**, Express sürümünde ciddi yavaşlık yaşamaya başlarsınız. 10 GB sınırına yaklaştığınızda ise veritabanı büyüyemez ve hata verir.

### Ne Yapmalı?

- **Veritabanı < 5 GB:** Express yeterli, diğer optimizasyonlara geçin
- **Veritabanı 5-10 GB:** Acil Standard'a geçiş planlayın
- **Test/Geliştirme:** Developer Edition tamamen ücretsiz ve sınırsız (üretimde kullanılamaz)

---

## 2. Kritik Tabloları Tanıyın: Yavaşlık Nerede?

Mikro ERP'de tüm tablolar eşit yoğunlukta değildir. Yavaşlığın %80'i genellikle şu 4 tabloda yaşanır:

| Tablo | Prefix | Ortalama Kayıt | Tipik Sorun |
|---|---|---|---|
| `CARI_HESAP_HAREKETLERI` | `cha_` | 500K – 5M | Ekstre, yaşlandırma raporları |
| `STOK_HAREKETLERI` | `sth_` | 500K – 10M | Stok bakiye, maliyet raporları |
| `SIPARISLER` | `sip_` | 100K – 1M | Açık sipariş, teslimat raporları |
| `STOKLAR` | `sto_` | 10K – 100K | Fiyat listesi, stok arama |

### Tablo Boyutlarınızı Ölçün

```sql
-- En buyuk 15 tabloyu listele (satir sayisi + boyut)
SELECT TOP 15
    s.name + '.' + t.name                            AS Tablo,
    p.rows                                           AS Satir_Sayisi,
    CAST(SUM(a.total_pages) * 8.0 / 1024 AS DECIMAL(10,2)) AS Boyut_MB,
    CAST(SUM(a.used_pages) * 8.0 / 1024  AS DECIMAL(10,2)) AS Kullanilan_MB
FROM sys.tables t
INNER JOIN sys.schemas s ON t.schema_id = s.schema_id
INNER JOIN sys.indexes i ON t.object_id = i.object_id
INNER JOIN sys.partitions p ON i.object_id = p.object_id AND i.index_id = p.index_id
INNER JOIN sys.allocation_units a ON p.partition_id = a.container_id
WHERE t.is_ms_shipped = 0
GROUP BY s.name, t.name, p.rows
ORDER BY SUM(a.total_pages) DESC;
```

**Sonuçta `CARI_HESAP_HAREKETLERI` ve `STOK_HAREKETLERI` tablolarının boyutuna dikkat edin.** 1 GB'ı aşan tablolarda index stratejisi kritik hale gelir.

---

## 3. Wait Stats ile Yavaşlığın Kaynağını Bulun

SQL Server neden yavaş? Cevabı **Wait Statistics** verir. Her sorgu çalıştığında SQL Server bir kaynağı bekler — disk, bellek, kilit veya CPU. Wait Stats bu bekleme sürelerini kaydeder.

### Mevcut Wait Stats'ı Sorgulayın

```sql
-- En cok beklemeye neden olan ilk 10 wait type
SELECT TOP 10
    wait_type                        AS Bekleme_Tipi,
    wait_time_ms / 1000              AS Toplam_Bekleme_Saniye,
    signal_wait_time_ms / 1000       AS CPU_Bekleme_Saniye,
    (wait_time_ms - signal_wait_time_ms) / 1000 AS Kaynak_Bekleme_Saniye,
    waiting_tasks_count              AS Bekleme_Sayisi
FROM sys.dm_os_wait_stats
WHERE wait_type NOT IN (
    -- Arka plan ve bos bekleme tiplerini filtrele
    'SLEEP_TASK', 'BROKER_TO_FLUSH', 'SQLTRACE_BUFFER_FLUSH',
    'CLR_AUTO_EVENT', 'CLR_MANUAL_EVENT', 'LAZYWRITER_SLEEP',
    'CHECKPOINT_QUEUE', 'WAITFOR', 'XE_TIMER_EVENT',
    'REQUEST_FOR_DEADLOCK_SEARCH', 'HADR_FILESTREAM_IOMGR_IOCOMPLETION',
    'DIRTY_PAGE_POLL', 'XE_DISPATCHER_WAIT', 'BROKER_EVENTHANDLER',
    'FT_IFTS_SCHEDULER_IDLE_WAIT', 'LOGMGR_QUEUE',
    'ONDEMAND_TASK_QUEUE', 'SP_SERVER_DIAGNOSTICS_SLEEP',
    'BROKER_RECEIVE_WAITFOR', 'PREEMPTIVE_OS_AUTHENTICATIONOPS'
)
  AND wait_time_ms > 0
ORDER BY wait_time_ms DESC;
```

### Sonuçları Nasıl Yorumlayacaksınız?

| Wait Type | Anlam | Çözüm |
|---|---|---|
| `PAGEIOLATCH_SH / EX` | Veriler diskten okunuyor, bellekte bulunamıyor | 🔴 **RAM yetersiz** veya **index eksik** — en yaygın Mikro ERP sorunu |
| `CXPACKET` | Paralel sorgu thread'leri birbirini bekliyor | Index eksik veya istatistikler güncel değil |
| `LCK_M_S / LCK_M_X` | Kilitlenme — bir işlem diğerini bekliyor | Fatura kaydı sırasında rapor çekilmesi, uzun transaction'lar |
| `SOS_SCHEDULER_YIELD` | CPU yetersiz, sorgular sıra bekliyor | CPU dar boğaz veya çok karmaşık sorgular |
| `WRITELOG` | Transaction log yazma beklemesi | Log dosyası yavaş diskte |

**Mikro ERP'de en sık görülen:** `PAGEIOLATCH` — veriler bellekte tutulacak kadar RAM yok, her sorguda diskten okunuyor. Bu durumda index eklemek veya RAM artırmak en etkili çözümdür.

---

## 4. Eksik Index'leri Tespit Edin ve Akıllıca Ekleyin

SQL Server, çalıştırdığı her sorgunun "keşke şu index olsaydı daha hızlı olurdu" dediği yerleri kaydeder. Bu altın bilgiyi kullanarak doğru index'leri bulabilirsiniz.

### Eksik Index Önerileri

```sql
-- SQL Server'in onerdigi eksik index'ler (en etkili ilk 15)
SELECT TOP 15
    OBJECT_NAME(d.object_id)     AS Tablo,
    d.equality_columns           AS Esitlik_Kolonlari,
    d.inequality_columns         AS Aralik_Kolonlari,
    d.included_columns           AS Dahil_Kolonlar,
    CAST(s.avg_user_impact AS INT) AS Tahmini_Etki_Yuzde,
    s.user_seeks                 AS Kullanilacak_Sayisi,
    CAST(s.avg_total_user_cost AS DECIMAL(10,2)) AS Ortalama_Maliyet
FROM sys.dm_db_missing_index_details d
INNER JOIN sys.dm_db_missing_index_groups g ON d.index_handle = g.index_handle
INNER JOIN sys.dm_db_missing_index_group_stats s ON g.index_group_handle = s.group_handle
WHERE d.database_id = DB_ID()
ORDER BY s.avg_user_impact * s.user_seeks DESC;
```

### Mikro ERP İçin Önerilen Temel Index'ler

Aşağıdaki index'ler **çoğu Mikro ERP kurulumunda** fark yaratır. Bunlar hareket tablolarındaki en sık kullanılan filtreleme ve sıralama alanlarını kapsar:

```sql
-- =============================================
-- CARI_HESAP_HAREKETLERI: Tarih + Kod Filtresi
-- Fayda: Cari ekstre, yaslandirma, tahsilat raporlari
-- =============================================
IF NOT EXISTS (
    SELECT 1 FROM sys.indexes
    WHERE name = 'IX_CHA_Kod_Tarih'
      AND object_id = OBJECT_ID('dbo.CARI_HESAP_HAREKETLERI')
)
CREATE NONCLUSTERED INDEX IX_CHA_Kod_Tarih
ON dbo.CARI_HESAP_HAREKETLERI (cha_kod, cha_tarihi)
INCLUDE (cha_tip, cha_meblag, cha_evrak_tip, cha_cinsi, cha_iptal)
WITH (ONLINE = ON, FILLFACTOR = 90);
-- ONLINE=ON: Index olusturulurken tablo kilitlenmez
-- FILLFACTOR=90: Sayfalarda %10 bos alan birak (INSERT performansi icin)

-- =============================================
-- STOK_HAREKETLERI: Stok Kodu + Tarih Filtresi
-- Fayda: Stok bakiye, maliyet, hareket raporlari
-- =============================================
IF NOT EXISTS (
    SELECT 1 FROM sys.indexes
    WHERE name = 'IX_STH_StokKod_Tarih'
      AND object_id = OBJECT_ID('dbo.STOK_HAREKETLERI')
)
CREATE NONCLUSTERED INDEX IX_STH_StokKod_Tarih
ON dbo.STOK_HAREKETLERI (sth_stok_kod, sth_tarih)
INCLUDE (sth_tip, sth_miktar, sth_tutar, sth_cins, sth_giris_depo_no, sth_cikis_depo_no)
WITH (ONLINE = ON, FILLFACTOR = 90);

-- =============================================
-- STOK_HAREKETLERI: Cari Kodu Filtresi
-- Fayda: Cariye gore satis/alis raporlari
-- =============================================
IF NOT EXISTS (
    SELECT 1 FROM sys.indexes
    WHERE name = 'IX_STH_CariKod_Tarih'
      AND object_id = OBJECT_ID('dbo.STOK_HAREKETLERI')
)
CREATE NONCLUSTERED INDEX IX_STH_CariKod_Tarih
ON dbo.STOK_HAREKETLERI (sth_cari_kodu, sth_tarih)
INCLUDE (sth_stok_kod, sth_tip, sth_miktar, sth_tutar)
WITH (ONLINE = ON, FILLFACTOR = 90);

-- =============================================
-- SIPARISLER: Musteri Kodu + Tarih Filtresi
-- Fayda: Acik siparis, teslimat raporlari
-- =============================================
IF NOT EXISTS (
    SELECT 1 FROM sys.indexes
    WHERE name = 'IX_SIP_MusteriKod_Tarih'
      AND object_id = OBJECT_ID('dbo.SIPARISLER')
)
CREATE NONCLUSTERED INDEX IX_SIP_MusteriKod_Tarih
ON dbo.SIPARISLER (sip_musteri_kod, sip_tarih)
INCLUDE (sip_stok_kod, sip_miktar, sip_teslim_miktar, sip_tip, sip_cins)
WITH (ONLINE = ON, FILLFACTOR = 90);
```

### ⚡ Önemli: Index Ekleme Kuralları

1. **Körlemesine eklemeyin.** Önce eksik index sorgusunu çalıştırın, SQL Server'ın önerisini görün
2. **INCLUDE kolonlarını ayarlayın.** Raporda gösterdiğiniz alanları INCLUDE'a ekleyin — Key Lookup'ı önler
3. **Test edin.** Canlı ortamda index oluşturmadan önce test ortamında deneyin
4. **Gece çalıştırın.** Büyük tablolarda index oluşturma uzun sürebilir (ONLINE=ON kullanın)
5. **Fazla index = yavaş yazma.** Her `INSERT` ve `UPDATE` işleminde tüm index'ler güncellenir. Tablo başına 5-7 index idealdir

---

## 5. TempDB Optimizasyonu

TempDB, SQL Server'ın **geçici işlem alanı**dır. Sıralama, gruplama, JOIN, cursor ve geçici tablolar burayı kullanır. Mikro ERP'deki raporların çoğu karmaşık JOIN ve GROUP BY işlemleri yaptığı için TempDB performansı kritiktir.

### TempDB Durumunu Kontrol Edin

```sql
-- TempDB dosya durumu ve boyutlari
SELECT
    name                         AS Dosya_Adi,
    physical_name                AS Dosya_Yolu,
    size * 8 / 1024              AS Boyut_MB,
    growth                       AS Buyume_Ayari,
    CASE is_percent_growth
        WHEN 1 THEN CAST(growth AS VARCHAR) + '%'
        ELSE CAST(growth * 8 / 1024 AS VARCHAR) + ' MB'
    END                          AS Buyume_Miktari
FROM tempdb.sys.database_files;
```

### TempDB Optimizasyon Kuralları

**Kural 1: Dosya Sayısı = CPU Çekirdek Sayısı (max 8)**

```sql
-- Sunucunuzdaki CPU cekirdek sayisini kontrol edin
SELECT cpu_count AS Toplam_CPU, hyperthread_ratio AS HT_Orani
FROM sys.dm_os_sys_info;
```

Örneğin 4 çekirdekli bir sunucuda TempDB'de **4 data file** olmalıdır. Tüm dosyalar **aynı boyutta** olmalı:

```sql
-- TempDB'ye yeni dosya ekleme (ornek: 4 cekirdek icin 4 dosya, her biri 512 MB)
-- NOT: Bu komutu calistirmadan once mevcut dosya sayinizi kontrol edin
ALTER DATABASE tempdb
ADD FILE (
    NAME = 'tempdev2',
    FILENAME = 'D:\SQLData\tempdev2.ndf',  -- Hizli bir SSD yolu verin
    SIZE = 512MB,
    FILEGROWTH = 256MB
);
-- Diger dosyalar icin tekrarlayin (tempdev3, tempdev4)
```

**Kural 2: TempDB'yi Ayrı Diske Taşıyın**

TempDB **kesinlikle** ana veritabanı ile aynı diskte olmamalıdır. Mümkünse SSD/NVMe üzerine taşıyın.

**Kural 3: Otomatik Küçültme (Shrink) Kullanmayın**

`tempdb` için asla `DBCC SHRINKFILE` çalıştırmayın. SQL Server yeniden başladığında TempDB zaten başlangıç boyutuna döner.

---

## 6. En Yavaş Sorguları Bulun

Hangi sorgular en çok kaynak tüketiyor? SQL Server bunu sizin için kaydeder:

### Top 10 Kaynak Tüketen Sorgu

```sql
-- En cok CPU + IO tuketen 10 sorgu
SELECT TOP 10
    SUBSTRING(qt.text, (qs.statement_start_offset / 2) + 1,
        (CASE qs.statement_end_offset
            WHEN -1 THEN DATALENGTH(qt.text)
            ELSE qs.statement_end_offset
        END - qs.statement_start_offset) / 2 + 1) AS Sorgu_Metni,
    qs.execution_count                             AS Calisma_Sayisi,
    qs.total_elapsed_time / 1000                   AS Toplam_Sure_ms,
    qs.total_elapsed_time / qs.execution_count / 1000 AS Ortalama_Sure_ms,
    qs.total_logical_reads                         AS Toplam_Okuma,
    qs.total_logical_reads / qs.execution_count    AS Ortalama_Okuma,
    qs.total_worker_time / 1000                    AS Toplam_CPU_ms
FROM sys.dm_exec_query_stats qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) qt
WHERE qt.dbid = DB_ID()
ORDER BY qs.total_elapsed_time DESC;
```

### Execution Plan Okuma Rehberi

Yavaş bir sorguyu bulduktan sonra çalıştırma planını görüntüleyin:

```sql
-- Sorgunun execution plan'ini goruntule
SET STATISTICS IO ON;
SET STATISTICS TIME ON;

-- Yavaş sorgunuzu buraya yapistirin
-- Ornek: Cari ekstre raporu
SELECT cha_kod, cha_tarihi, cha_tip, cha_meblag
FROM CARI_HESAP_HAREKETLERI WITH (NOLOCK)
WHERE cha_kod = '120.001'
  AND cha_tarihi >= '2026-01-01'
  AND cha_iptal = 0
ORDER BY cha_tarihi;

SET STATISTICS IO OFF;
SET STATISTICS TIME OFF;
```

**SSMS'de çalıştırma planını görsel olarak görmek için:** `Ctrl + M` (Include Actual Execution Plan) tuşlayın, sorguyu çalıştırın, alt paneldeki "Execution Plan" sekmesine bakın.

### Execution Plan'da Neye Bakmalı?

| Operasyon | İyi mi? | Anlam |
|---|---|---|
| **Index Seek** | ✅ İyi | Index üzerinden doğrudan erişim — hızlı |
| **Index Scan** | ⚠️ Kötü olabilir | Tüm index taranıyor — çok kayıt okuyor |
| **Table Scan** | 🔴 Kötü | Tüm tablo satır satır taranıyor — index yok |
| **Key Lookup** | ⚠️ Kötü olabilir | Index buldu ama ek kolonlar için tabloya geri dönüyor — INCLUDE eksik |
| **Sort** | ⚠️ Dikkat | Bellek yetmezse TempDB'ye taşar — yavaşlatır |
| **Hash Match** | ⚠️ Dikkat | Büyük tablolarda JOIN — bellek yoğun |

**Hedef:** `Table Scan` → `Index Seek` dönüşümü. Bir tablo taramasını index aramasına çevirmek sorguyu **10-100x** hızlandırabilir.

---

## 7. Otomatik Bakım Planı Oluşturun

Veritabanı bakımı yapılmazsa zamanla yavaşlar. Index'ler parçalanır, istatistikler eskir, log dosyası şişer. Aşağıdaki bakım işlemlerini **haftalık** çalıştırın:

### Index Bakımı (Reorganize / Rebuild)

```sql
-- Index parcalanma oranlarini kontrol et
SELECT
    OBJECT_NAME(ips.object_id)   AS Tablo,
    i.name                       AS Index_Adi,
    ips.avg_fragmentation_in_percent AS Parcalanma_Yuzde,
    ips.page_count               AS Sayfa_Sayisi,
    CASE
        WHEN ips.avg_fragmentation_in_percent < 10 THEN 'Islem gereksiz'
        WHEN ips.avg_fragmentation_in_percent < 30 THEN 'REORGANIZE onerisi'
        ELSE 'REBUILD onerisi'
    END                          AS Oneri
FROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, 'LIMITED') ips
INNER JOIN sys.indexes i ON ips.object_id = i.object_id AND ips.index_id = i.index_id
WHERE ips.avg_fragmentation_in_percent > 5
  AND ips.page_count > 1000  -- Kucuk tablolari atla
ORDER BY ips.avg_fragmentation_in_percent DESC;
```

### Parçalanma Kuralları

| Parçalanma % | İşlem | Komut |
|---|---|---|
| < %10 | Bir şey yapma | — |
| %10 – %30 | Reorganize | `ALTER INDEX [IndexAdi] ON [Tablo] REORGANIZE;` |
| > %30 | Rebuild | `ALTER INDEX [IndexAdi] ON [Tablo] REBUILD WITH (ONLINE = ON);` |

### Tüm Index'leri Otomatik Bakım (Script)

```sql
-- Tum tablolardaki parcalanmis index'leri otomatik bakim yap
DECLARE @TabloDB NVARCHAR(256), @IndexDB NVARCHAR(256), @SqlDB NVARCHAR(MAX);
DECLARE @Frag FLOAT;

DECLARE IndexBakim CURSOR FOR
SELECT
    QUOTENAME(OBJECT_SCHEMA_NAME(ips.object_id)) + '.' + QUOTENAME(OBJECT_NAME(ips.object_id)),
    QUOTENAME(i.name),
    ips.avg_fragmentation_in_percent
FROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, 'LIMITED') ips
INNER JOIN sys.indexes i ON ips.object_id = i.object_id AND ips.index_id = i.index_id
WHERE ips.avg_fragmentation_in_percent > 10
  AND ips.page_count > 1000
  AND i.name IS NOT NULL;

OPEN IndexBakim;
FETCH NEXT FROM IndexBakim INTO @TabloDB, @IndexDB, @Frag;

WHILE @@FETCH_STATUS = 0
BEGIN
    IF @Frag < 30
        SET @SqlDB = N'ALTER INDEX ' + @IndexDB + N' ON ' + @TabloDB + N' REORGANIZE;';
    ELSE
        SET @SqlDB = N'ALTER INDEX ' + @IndexDB + N' ON ' + @TabloDB + N' REBUILD WITH (ONLINE = ON);';

    PRINT @SqlDB;
    EXEC sp_executesql @SqlDB;

    FETCH NEXT FROM IndexBakim INTO @TabloDB, @IndexDB, @Frag;
END

CLOSE IndexBakim;
DEALLOCATE IndexBakim;

PRINT 'Index bakim tamamlandi.';
```

### İstatistikleri Güncelle

```sql
-- Tum tablolarin istatistiklerini guncelle
-- Haftada 1 calistirin, tercihen gece
EXEC sp_updatestats;

-- Veya sadece kritik tablolar icin:
UPDATE STATISTICS dbo.CARI_HESAP_HAREKETLERI WITH FULLSCAN;
UPDATE STATISTICS dbo.STOK_HAREKETLERI WITH FULLSCAN;
UPDATE STATISTICS dbo.SIPARISLER WITH FULLSCAN;
```

`FULLSCAN` tablonun %100'ünü okuyarak en doğru istatistiği üretir. Tablo çok büyükse `SAMPLE 50 PERCENT` kullanabilirsiniz.

---

## Bonus: Mikro ERP'ye Özgü Hızlı Kazanımlar

### WITH (NOLOCK) Kullanımı

Raporlama sorgularınızda `WITH (NOLOCK)` kullanmak kilit bekleme süresini ortadan kaldırır. Mikro ERP'nin kendi raporları zaten bu tekniği kullanır:

```sql
-- Raporlama icin: Kilitsiz okuma
SELECT cha_kod, SUM(cha_meblag) AS Toplam
FROM CARI_HESAP_HAREKETLERI WITH (NOLOCK)
WHERE cha_tarihi >= '2026-01-01'
  AND cha_iptal = 0
GROUP BY cha_kod;
```

> ⚠️ `WITH (NOLOCK)` uncommitted (henüz onaylanmamış) verileri de okuyabilir. Rapor sorgularında kabul edilebilir ama **veri güncelleyen** sorgularda asla kullanmayın.

### Kullanılmayan Index'leri Tespit Edin

Çok fazla index yazma performansını düşürür. Hiç kullanılmayan index'leri tespit edin:

```sql
-- Son SQL Server restart'indan bu yana hic kullanilmamis index'ler
SELECT
    OBJECT_NAME(i.object_id) AS Tablo,
    i.name                   AS Index_Adi,
    i.type_desc              AS Index_Tipi,
    dm.user_seeks            AS Seek_Sayisi,
    dm.user_scans            AS Scan_Sayisi,
    dm.user_lookups          AS Lookup_Sayisi,
    dm.user_updates          AS Guncelleme_Sayisi
FROM sys.indexes i
LEFT JOIN sys.dm_db_index_usage_stats dm ON i.object_id = dm.object_id
    AND i.index_id = dm.index_id
    AND dm.database_id = DB_ID()
WHERE OBJECTPROPERTY(i.object_id, 'IsUserTable') = 1
  AND i.type_desc = 'NONCLUSTERED'
  AND ISNULL(dm.user_seeks, 0) + ISNULL(dm.user_scans, 0) + ISNULL(dm.user_lookups, 0) = 0
  AND dm.user_updates > 100  -- Sadece guncellenen ama hic okunmayan
ORDER BY dm.user_updates DESC;
```

### SQL Server Bellek Ayarı

SQL Server varsayılan olarak sunucudaki **tüm RAM'i** almaya çalışır. Mikro ERP sunucusunda başka uygulamalar da çalışıyorsa (Mikro istemci, Windows servisleri) SQL Server'a maksimum bellek limiti verin:

```sql
-- SQL Server'a maksimum bellek siniri ver
-- Toplam RAM: 16 GB ise → SQL Server'a 12 GB ver, 4 GB isletim sistemine birak
EXEC sp_configure 'show advanced options', 1;
RECONFIGURE;

EXEC sp_configure 'max server memory (MB)', 12288;  -- 12 GB = 12 * 1024
RECONFIGURE;

-- Ayari dogrula
EXEC sp_configure 'max server memory (MB)';
```

**Genel kural:** Toplam RAM'in %75-80'ini SQL Server'a verin. Kalan %20-25 işletim sistemi ve diğer servisler içindir.

---

## Kontrol Listesi: Hemen Yapılacaklar

| # | İşlem | Komut/Adım | Öncelik |
|---|---|---|---|
| 1 | SQL Server sürümünü kontrol et | `SELECT @@VERSION` | 🔴 |
| 2 | Veritabanı boyutunu kontrol et | Yukarıdaki sorguyu çalıştır | 🔴 |
| 3 | Wait Stats'ı oku | Yukarıdaki sorguyu çalıştır | 🔴 |
| 4 | Eksik index önerilerini al | `sys.dm_db_missing_index_details` | 🔴 |
| 5 | Kritik tablolara index ekle | 4 temel index'i oluştur | 🟡 |
| 6 | TempDB dosya sayısını kontrol et | CPU çekirdeğe eşitle | 🟡 |
| 7 | Index parçalanmasını kontrol et | Bakım scriptini çalıştır | 🟡 |
| 8 | İstatistikleri güncelle | `sp_updatestats` | 🟡 |
| 9 | Max Memory ayarla | `sp_configure` | 🟢 |
| 10 | Haftalık bakım planı kur | SQL Agent Job oluştur | 🟢 |

---

## Sonuç

Mikro ERP yavaşlığının çözümü çoğunlukla yazılım değişikliği değil, **veritabanı yapılandırması** ve **düzenli bakım**tır. Bu rehberdeki adımları sırayla uygularsanız:

- **Wait Stats** ile gerçek darboğazı tespit edersiniz (tahmin değil, veri)
- **Index stratejisi** ile `CARI_HESAP_HAREKETLERI` ve `STOK_HAREKETLERI` sorgularını 5-10x hızlandırırsınız
- **TempDB optimizasyonu** ile raporlardaki sıralama ve gruplama işlemlerini rahatlatırsınız
- **Otomatik bakım** ile performansın zamanla düşmesini engellersiniz

**İlgili yazılar:**
- [Mikro ERP Cari Yaşlandırma Performans Optimizasyonu](/blog/mikro-erp-cari-yaslandirma-performans-optimizasyon) — fn_Aysm darboğazı ve hızlandırma teknikleri
- [Mikro ERP CARI_HESAP_HAREKETLERI Tablo Rehberi](/blog/mikro-erp-cari-hesap-hareketleri-tablo-rehberi) — 185 alan, 137 evrak tipi
- [Mikro ERP Tablo İlişkileri ve JOIN Rehberi](/blog/mikro-erp-tablo-iliskileri-join-rehberi) — FK ilişkileri ve JOIN kalıpları
- [Mikro ERP Depo Bazlı Stok Bakiye SQL](/blog/mikro-erp-depo-bazli-stok-bakiye-sql) — Stok raporlama sorguları