Mikro ERP Tablo İlişkileri ve JOIN Rehberi: Hangi Tablo Hangisiyle Birleşir?

Mikro ERP veritabanında 490+ tablonun ilişkilerini ve doğru JOIN kullanımını öğrenin. 10 temel tablo, 12 JOIN kalıbı ve 3 hazır çoklu JOIN sorgusu.

Mikro ERP veritabanı 490’dan fazla tablo içerir. İlk kez bakan birisi bu tablolar arasında kaybolabilir: hangi tablo ne işe yarıyor, hangi alanla hangisi birleşiyor, LEFT JOININNER JOIN mi kullanmalı?

Bu rehberde Mikro ERP’nin en çok kullanılan 10 tablosunu, aralarındaki ilişkileri ve doğru JOIN kalıplarını tek bir yerde topladık. Blog’daki 20+ SQL yazımızın tamamı bu tablolar üzerine kurulu.

Ana Tablolar — Mikro ERP’nin Omurgası

TabloPrefixAçıklamaAlan Sayısı
CARI_HESAPLARcari_Müşteri, satıcı ve diğer cari kart bilgileri196
CARI_HESAP_HAREKETLERIcha_Tüm cari hareketler (fatura, tahsilat, çek, senet, dekont…)185
STOKLARsto_Stok kart bilgileri (ürün tanımları)233
STOK_HAREKETLERIsth_Tüm stok giriş/çıkış hareketleri148+
SIPARISLERsip_Alış ve satış siparişleri139
DEPOLARdep_Depo tanımları ve bilgileri81
BANKALARban_Banka hesap bilgileri98
KASALARkas_Kasa tanımları33
STOK_SATIS_FIYAT_LISTELERIsfiyat_Stok satış fiyat listeleri36
E_FATURA_ISLEMLERIefi_e-Fatura ve e-Arşiv işlemleri85

Prefix kuralı: Mikro ERP’de her tablonun alanları bir prefix ile başlar. cha_kodcha_ prefixi → CARI_HESAP_HAREKETLERI tablosu. Bu kural, tablonun hangi alana ait olduğunu anında anlamanızı sağlar.

İlişki Haritası — 12 Temel JOIN Kalıbı

1. Cari Kart ↔ Cari Hareket

CARI_HESAPLAR.cari_kod = CARI_HESAP_HAREKETLERI.cha_kod

Ne yapar: Cari hareketlere müşteri/satıcı ad ve ünvan bilgisini ekler.

Ne zaman kullanılır: Her zaman. Cari ekstre, tahsilat raporu, yaşlandırma — hemen her cari raporda bu JOIN gerekir.

SELECT
    CHA.cha_tarihi  AS [Tarih],
    CHA.cha_kod     AS [Cari Kodu],
    LTRIM(RTRIM(
        ISNULL(CH.cari_unvan1, '') + ' ' + ISNULL(CH.cari_unvan2, '')
    )) AS [Cari Ünvan],
    CHA.cha_meblag  AS [Tutar]
FROM dbo.CARI_HESAP_HAREKETLERI CHA WITH (NOLOCK)
LEFT JOIN dbo.CARI_HESAPLAR CH WITH (NOLOCK)
    ON CH.cari_kod = CHA.cha_kod

Neden LEFT JOIN? Bazı hareketlerde cari kod hatalı girilmiş veya silinmiş olabilir. INNER JOIN kullanırsanız bu satırlar kaybolur. Cari ünvan gelmese bile hareketi görmek önemlidir.


2. Stok Kart ↔ Stok Hareket

STOKLAR.sto_kod = STOK_HAREKETLERI.sth_stok_kod

Ne yapar: Stok hareketlerine ürün adını ekler.

Ne zaman kullanılır: Stok bakiye raporu, satış raporu, maliyet analizi — stok hareketiyle çalışan her sorguda.

SELECT
    STH.sth_tarih       AS [Tarih],
    S.sto_isim           AS [Ürün Adı],
    STH.sth_miktar       AS [Miktar],
    STH.sth_tutar        AS [Tutar]
FROM dbo.STOK_HAREKETLERI STH WITH (NOLOCK)
INNER JOIN dbo.STOKLAR S WITH (NOLOCK)
    ON S.sto_kod = STH.sth_stok_kod

Burada INNER JOIN: Stok kodu olmayan hareket normalde olamaz. Eğer varsa zaten hatalı veridir ve raporda gösterilmemesi mantıklıdır.


3. Stok Hareket ↔ Cari Kart

STOK_HAREKETLERI.sth_cari_kodu = CARI_HESAPLAR.cari_kod

Ne yapar: Stok çıkışında hangi müşteriye verildiğini, girişte hangi satıcıdan alındığını gösterir.

Ne zaman kullanılır: Satış raporu, alış-satış kâr analizi, müşteri bazlı stok hareketi.

SELECT
    S.sto_isim          AS [Ürün],
    CH.cari_unvan1      AS [Müşteri/Satıcı],
    STH.sth_miktar      AS [Miktar],
    STH.sth_tutar       AS [Tutar]
FROM dbo.STOK_HAREKETLERI STH WITH (NOLOCK)
INNER JOIN dbo.STOKLAR S WITH (NOLOCK)
    ON S.sto_kod = STH.sth_stok_kod
LEFT JOIN dbo.CARI_HESAPLAR CH WITH (NOLOCK)
    ON CH.cari_kod = STH.sth_cari_kodu

LEFT JOIN nedeni: Depo transfer, sarf, fire gibi hareketlerde cari kodu boş olabilir.


4. Stok Hareket ↔ Depo (Giriş)

STOK_HAREKETLERI.sth_giris_depo_no = DEPOLAR.dep_no

Ne yapar: Stok girişinin hangi depoya yapıldığını gösterir.


5. Stok Hareket ↔ Depo (Çıkış)

STOK_HAREKETLERI.sth_cikis_depo_no = DEPOLAR.dep_no

Ne yapar: Stok çıkışının hangi depodan yapıldığını gösterir.

⚠️ Dikkat: Giriş ve çıkış deposu farklı alanlardır. Transfer hareketlerinde her ikisi de dolu olabilir. Aynı sorguya her iki depoyu da eklemek isterseniz:

LEFT JOIN dbo.DEPOLAR DG WITH (NOLOCK)
    ON DG.dep_no = STH.sth_giris_depo_no    -- Giriş deposu
LEFT JOIN dbo.DEPOLAR DC WITH (NOLOCK)
    ON DC.dep_no = STH.sth_cikis_depo_no    -- Çıkış deposu

Her iki depo da aynı tablodur, farklı alias ile ayrılır. Bu çok yapılan bir hatadır.


6. Sipariş ↔ Stok Kart

SIPARISLER.sip_stok_kod = STOKLAR.sto_kod

Ne yapar: Siparişlerdeki ürün koduna stok adını ekler.


7. Sipariş ↔ Cari (Müşteri)

SIPARISLER.sip_musteri_kod = CARI_HESAPLAR.cari_kod

Ne yapar: Siparişe müşteri bilgisini ekler. Satış siparişlerinde (sip_tip = 0) müşteri kodunu gösterir.


8. Sipariş ↔ Cari (Satıcı/Tedarikçi)

SIPARISLER.sip_satici_kod = CARI_HESAPLAR.cari_kod

Ne yapar: Alış siparişlerinde (sip_tip = 1) tedarikçi bilgisini ekler.

Aynı tablo, farklı alan: Müşteri ve satıcı kodları ayrı alanlardır ama her ikisi de CARI_HESAPLAR tablosuna JOIN edilir:

LEFT JOIN dbo.CARI_HESAPLAR CM WITH (NOLOCK)
    ON CM.cari_kod = SIP.sip_musteri_kod      -- Müşteri
LEFT JOIN dbo.CARI_HESAPLAR CS WITH (NOLOCK)
    ON CS.cari_kod = SIP.sip_satici_kod       -- Satıcı/Tedarikçi

9. Fiyat Listesi ↔ Stok

STOK_SATIS_FIYAT_LISTELERI.sfiyat_stokkod = STOKLAR.sto_kod

Ne yapar: Fiyat listesindeki stok koduna ürün adını ekler. Birden fazla fiyat listesi olduğunda, listeleme ve karşılaştırma için kullanılır.


10. Sipariş ↔ Stok Hareket (GUID ile)

SIPARISLER.sip_Guid = STOK_HAREKETLERI.sth_siparis_uid

Ne yapar: Siparişin hangi stok hareketi (irsaliye/fatura) ile karşılandığını eşleştirir.

Önemli: Bu ilişki Uniqueidentifier (GUID) tipindedir, string bazlı değildir. Performans açısından GUID JOIN’leri integer JOIN’lere göre daha yavaş olabilir, ancak Mikro’nun yapısında bu şekilde tasarlanmıştır.

İlgili yazı: Fatura-Sipariş Eşleştirme SQL


11. Cari Hareket ↔ Stok Hareket (Evrak Bazında)

CARI_HESAP_HAREKETLERI.cha_evrakno_seri = STOK_HAREKETLERI.sth_evrakno_seri
AND CARI_HESAP_HAREKETLERI.cha_evrakno_sira = STOK_HAREKETLERI.sth_evrakno_sira

Ne yapar: Aynı faturanın cari tarafı (borç/alacak) ile stok tarafı (giriş/çıkış) eşleştirilir.

⚠️ Dikkat: Bu JOIN, seri+sıra numarası bazlıdır ve farklı evrak tipleri aynı seri/sıra numarasını kullanabilir. Güvenli kullanım için cha_evrak_tip filtresini de eklemeniz gerekir.


12. Cari Hareket ↔ Banka / Kasa

-- Karşı taraf banka ise:
CARI_HESAP_HAREKETLERI.cha_kasa_hizkod = BANKALAR.ban_kod
-- (cha_kasa_hizmet = 2 olduğunda)

-- Karşı taraf kasa ise:
CARI_HESAP_HAREKETLERI.cha_kasa_hizkod = KASALAR.kas_kod
-- (cha_kasa_hizmet = 4 olduğunda)

Ne yapar: Tahsilat veya ödeme hareketinde karşı tarafın hangi banka/kasa olduğunu gösterir.

cha_kasa_hizmet alanı: 0=Cari, 2=Banka, 4=Kasa. Bu alana göre JOIN hedefi değişir. Doğrudan tek JOIN ile çözmek zordur, en pratik yolu CASE WHEN ile isim çekmektir.


Sık Yapılan JOIN Hataları

❌ Hata 1: INNER JOIN ile Veri Kaybı

-- ❌ Cari kodu boş olan hareketler kaybolur:
INNER JOIN dbo.CARI_HESAPLAR CH ON CH.cari_kod = CHA.cha_kod

-- ✅ Cari kodu boş olan hareketler de görünür:
LEFT JOIN dbo.CARI_HESAPLAR CH ON CH.cari_kod = CHA.cha_kod

Kural: Ana tablonuzdaki (FROM’daki) her satırı görmek istiyorsanız LEFT JOIN kullanın. Sadece eşleşen satırları istiyorsanız INNER JOIN kullanın.

❌ Hata 2: Giriş-Çıkış Depo Karışıklığı

-- ❌ Satış raporunda giriş deposunu JOIN ettik — yanlış depo adı çıkar:
LEFT JOIN dbo.DEPOLAR D ON D.dep_no = STH.sth_giris_depo_no

-- ✅ Satış (çıkış) raporunda çıkış deposunu kullanmalıyız:
LEFT JOIN dbo.DEPOLAR D ON D.dep_no = STH.sth_cikis_depo_no

❌ Hata 3: String Alanlarda Boşluk Sorunu

Mikro ERP’de birçok string alanı sabit uzunlukta (NVARCHAR) olduğu için sağ tarafta boşluk kalabilir. Karşılaştırma ve birleştirme yaparken LTRIM(RTRIM(...)) kullanın:

-- ❌ Sondaki boşluklar dahil:
SELECT cari_unvan1 + ' ' + cari_unvan2

-- ✅ Temizlenmiş:
LTRIM(RTRIM(ISNULL(cari_unvan1, '') + ' ' + ISNULL(cari_unvan2, '')))

❌ Hata 4: NOLOCK Kullanmamak

-- ❌ Canlı sistemde kilitleme riski:
FROM dbo.STOK_HAREKETLERI STH
INNER JOIN dbo.STOKLAR S ON S.sto_kod = STH.sth_stok_kod

-- ✅ Rapor sorgularında NOLOCK:
FROM dbo.STOK_HAREKETLERI STH WITH (NOLOCK)
INNER JOIN dbo.STOKLAR S WITH (NOLOCK) ON S.sto_kod = STH.sth_stok_kod

3 Hazır Çoklu JOIN Sorgusu

Sorgu 1: Satış Faturası Detay Raporu (4 Tablo JOIN)

Stok hareket + stok kart + cari kart + çıkış deposu:

-- =============================================
-- Satış Faturası Detay Raporu
-- ⚙️ SSMS: Tarihleri değiştirin
-- ✅ Mikro: DECLARE satırlarını kaldırın
-- =============================================
DECLARE @P1 DATE = '2026-01-01';
DECLARE @P2 DATE = '2026-12-31';

SELECT
    STH.sth_tarih                AS [Fatura Tarihi],
    STH.sth_evrakno_seri         AS [Seri],
    STH.sth_evrakno_sira         AS [Sıra No],
    LTRIM(RTRIM(
        ISNULL(CH.cari_unvan1, '') + ' ' + ISNULL(CH.cari_unvan2, '')
    ))                           AS [Müşteri],
    S.sto_kod                    AS [Stok Kodu],
    S.sto_isim                   AS [Ürün Adı],
    DC.dep_adi                   AS [Çıkış Deposu],
    STH.sth_miktar               AS [Miktar],
    STH.sth_birim_pntr           AS [Birim],
    STH.sth_tutar                AS [Tutar],
    STH.sth_vergi                AS [KDV]
FROM dbo.STOK_HAREKETLERI STH WITH (NOLOCK)
INNER JOIN dbo.STOKLAR S WITH (NOLOCK)
    ON S.sto_kod = STH.sth_stok_kod
LEFT JOIN dbo.CARI_HESAPLAR CH WITH (NOLOCK)
    ON CH.cari_kod = STH.sth_cari_kodu
LEFT JOIN dbo.DEPOLAR DC WITH (NOLOCK)
    ON DC.dep_no = STH.sth_cikis_depo_no
WHERE STH.sth_tarih >= @P1
  AND STH.sth_tarih <= @P2
  AND STH.sth_evraktip = 4      -- Çıkış faturası
  AND STH.sth_tip = 1           -- Çıkış
  AND STH.sth_normal_iade = 0   -- Normal (iade değil)
ORDER BY STH.sth_tarih, STH.sth_evrakno_sira;

Sorgu 2: Cari Ekstre Özet (2 Tablo JOIN)

Cari kart + cari hareket toplamı:

-- =============================================
-- Cari Bazlı Borç/Alacak Özet
-- =============================================
DECLARE @P1 DATE = '2026-01-01';
DECLARE @P2 DATE = '2026-12-31';

SELECT
    CHA.cha_kod                  AS [Cari Kodu],
    LTRIM(RTRIM(
        ISNULL(CH.cari_unvan1, '') + ' ' + ISNULL(CH.cari_unvan2, '')
    ))                           AS [Cari Ünvan],
    SUM(CASE WHEN CHA.cha_tip = 0 THEN CHA.cha_meblag ELSE 0 END) AS [Borç],
    SUM(CASE WHEN CHA.cha_tip = 1 THEN CHA.cha_meblag ELSE 0 END) AS [Alacak],
    SUM(CASE WHEN CHA.cha_tip = 0 THEN CHA.cha_meblag ELSE 0 END)
  - SUM(CASE WHEN CHA.cha_tip = 1 THEN CHA.cha_meblag ELSE 0 END) AS [Bakiye]
FROM dbo.CARI_HESAP_HAREKETLERI CHA WITH (NOLOCK)
LEFT JOIN dbo.CARI_HESAPLAR CH WITH (NOLOCK)
    ON CH.cari_kod = CHA.cha_kod
WHERE CHA.cha_tarihi >= @P1
  AND CHA.cha_tarihi <= @P2
GROUP BY CHA.cha_kod, CH.cari_unvan1, CH.cari_unvan2
HAVING SUM(CASE WHEN CHA.cha_tip = 0 THEN CHA.cha_meblag ELSE 0 END)
     - SUM(CASE WHEN CHA.cha_tip = 1 THEN CHA.cha_meblag ELSE 0 END) <> 0
ORDER BY [Bakiye] DESC;

Sorgu 3: Sipariş Teslimat Durumu (3 Tablo JOIN)

Sipariş + stok kart + müşteri:

-- =============================================
-- Açık Sipariş ve Teslimat Durumu
-- =============================================
SELECT
    SIP.sip_tarih                AS [Sipariş Tarihi],
    SIP.sip_evrakno_seri         AS [Seri],
    SIP.sip_evrakno_sira         AS [Sıra No],
    LTRIM(RTRIM(
        ISNULL(CH.cari_unvan1, '') + ' ' + ISNULL(CH.cari_unvan2, '')
    ))                           AS [Müşteri],
    S.sto_isim                   AS [Ürün],
    SIP.sip_miktar               AS [Sipariş Miktarı],
    SIP.sip_teslim_miktar        AS [Teslim Edilen],
    SIP.sip_miktar - SIP.sip_teslim_miktar AS [Kalan],
    CASE
        WHEN SIP.sip_teslim_miktar >= SIP.sip_miktar THEN 'Tamamlandı'
        WHEN SIP.sip_teslim_miktar > 0 THEN 'Kısmi Teslim'
        ELSE 'Bekliyor'
    END AS [Durum]
FROM dbo.SIPARISLER SIP WITH (NOLOCK)
INNER JOIN dbo.STOKLAR S WITH (NOLOCK)
    ON S.sto_kod = SIP.sip_stok_kod
LEFT JOIN dbo.CARI_HESAPLAR CH WITH (NOLOCK)
    ON CH.cari_kod = SIP.sip_musteri_kod
WHERE SIP.sip_tip = 0              -- Satış siparişi (talep)
  AND SIP.sip_teslim_miktar < SIP.sip_miktar  -- Henüz tamamlanmamış
ORDER BY SIP.sip_tarih;

Bu Bilgiyi Nereden Biliyoruz? (Kaynaklar)

📚 İlgili Yazılar