Mikro Aylık Ciro Karşılaştırma SQL: Bu Yıl vs Geçen Yıl
Mikro'da ay bazında bu yıl ve geçen yıl cirosunu yan yana alın: evraktip, iade ve iptal filtreleri, NULLIF korumalı büyüme yüzdesi ve hazır T-SQL.
Erp Asistanı
Editör ekibi
9 dk okuma
İçindekiler
Aylık ciro karşılaştırması ilk bakışta kolay görünür: iki yılın tutarlarını CASE WHEN YEAR(...) ile yan yana koyarsınız, iş biter. Pratikte bitmiyor. Aynı raporda üç ayrı tuzak var: satışı hangi sth_evraktip kümesinden alacağınız, iadeyi nasıl düşeceğiniz ve bölen sıfır olduğunda yüzdenin ne olacağı. Aşağıdaki sorgular Mikro v16/v17 şemasına göre yazıldı, her biri tek başına çalışır. Tablo ilişkilerini tazelemek isterseniz Mikro tablo yapısı rehberi iyi bir başlangıç.
Önce satış tanımını netleştirin
Ciro raporunun tamamı tek bir filtreden geçer. Yanlış filtre sorguyu patlatmaz; sayı sessizce şişer veya eksilir. Ölçülmüş farklar şöyle:
| Filtre | Ne yapar | Sonuç |
|---|---|---|
Yalnız sth_evraktip = 4 |
Faturalanmış irsaliye satışlarını kaçırır (mal hareketi evraktip = 1 satırında kalır) |
Ciro eksik; bir veritabanında ölçülen kayıp %13,7 |
Yalnız sth_tip = 1 |
Depo çıkış fişini (evraktip = 0) ve alış iadesini ciroya katar |
Ciro şişer, hata vermez |
sth_evraktip = 4 + sth_normal_iade = 1 satırlar |
Alış iadesini satış sanar | Ciro şişer |
sth_tip = 1 AND sth_evraktip IN (1,4) AND sth_normal_iade = 0 |
Kanonik satış kalemi | Doğru |
Kanonik satış kalemi: sth_iptal = 0 AND sth_tip = 1 AND sth_evraktip IN (1, 4) AND sth_normal_iade = 0. Satış iadesi ise mal geri geldiği için giriş tarafındadır: sth_tip = 0 AND sth_evraktip IN (3, 13) AND sth_normal_iade = 1. Net ciro = satış eksi satış iadesi. sth_iptal = 0 her sorguda var; iptal edilmiş evrak hiçbir toplama girmiyor. Fatura tarafının mantığını bilmiyorsanız Mikro'da fatura hangi tabloda yazısı bu ayrımı örneklerle anlatıyor.
1. Ay bazında iki yıl yan yana
En temel kalıp: tarih aralığını iki yılla sınırla, YEAR() ile yılları ayır, MONTH() ile grupla. Tarih filtresi olmadan tüm geçmiş taranır ve sorgu gereksiz yavaşlar.
SELECT
MONTH(sth_tarih) AS Ay,
SUM(CASE WHEN YEAR(sth_tarih) = 2025 THEN sth_tutar ELSE 0 END) AS Ciro2025,
SUM(CASE WHEN YEAR(sth_tarih) = 2026 THEN sth_tutar ELSE 0 END) AS Ciro2026
FROM STOK_HAREKETLERI
WHERE sth_iptal = 0
AND sth_tip = 1
AND sth_evraktip IN (1, 4)
AND sth_normal_iade = 0
AND sth_tarih >= '20250101' AND sth_tarih < '20270101'
GROUP BY MONTH(sth_tarih)
ORDER BY Ay;
Bu raporu SQL yazmadan, Türkçe sorarak alın: Mikro raporlama
Burada YEAR() kullanmak güvenlidir çünkü sth_tarih datetime. sth_evrakno_sira, sth_firmano, sth_subeno gibi int kolonları tarih fonksiyonuna sokarsanız örtük dönüşüm 8115 taşması alırsınız. Tarih işlemi yalnızca tarih kolonlarıyla yapılır.
2. İadeyi düşen net ciro
Yukarıdaki sorgu satış iadesini hiç görmüyor; çünkü satış iadesi giriş tarafında. Net rakam istiyorsanız iki filtre kümesini birleştirip işareti sth_tip ile belirleyin.
SELECT
MONTH(t.sth_tarih) AS Ay,
SUM(CASE WHEN YEAR(t.sth_tarih) = 2025 THEN t.NetTutar ELSE 0 END) AS Net2025,
SUM(CASE WHEN YEAR(t.sth_tarih) = 2026 THEN t.NetTutar ELSE 0 END) AS Net2026
FROM (
SELECT
sth_tarih,
CASE WHEN sth_tip = 1 THEN sth_tutar ELSE -sth_tutar END AS NetTutar
FROM STOK_HAREKETLERI
WHERE sth_iptal = 0
AND (
(sth_tip = 1 AND sth_evraktip IN (1, 4) AND sth_normal_iade = 0)
OR (sth_tip = 0 AND sth_evraktip IN (3, 13) AND sth_normal_iade = 1)
)
AND sth_tarih >= '20250101' AND sth_tarih < '20270101'
) t
GROUP BY MONTH(t.sth_tarih)
ORDER BY Ay;
Satış iadesi işareti negatife çevirdiği için SUM kendiliğinden net ciro verir. İade tutarını ayrı satırda görmek isterseniz aynı alt sorguya SUM(CASE WHEN sth_tip = 0 THEN sth_tutar ELSE 0 END) AS IadeTutar ekleyin.
3. Büyüme yüzdesi: NULLIF olmadan olmaz
Geçen yıl o ay hiç satış yoksa düz bölme Msg 8134 Divide by zero hatası verir. NULLIF(Ciro2025, 0) sıfırı NULL'a çevirir, yüzde NULL döner ve sorgu ayakta kalır. NULL değeri rapor katmanında yeni açılan ay, sıfır ise değişim yok diye okunmalı.
SELECT
a.Ay,
a.Ciro2025,
a.Ciro2026,
CAST((a.Ciro2026 - a.Ciro2025) * 100.0 / NULLIF(a.Ciro2025, 0) AS decimal(10,2)) AS BuyumeYuzde
FROM (
SELECT
MONTH(sth_tarih) AS Ay,
SUM(CASE WHEN YEAR(sth_tarih) = 2025 THEN sth_tutar ELSE 0 END) AS Ciro2025,
SUM(CASE WHEN YEAR(sth_tarih) = 2026 THEN sth_tutar ELSE 0 END) AS Ciro2026
FROM STOK_HAREKETLERI
WHERE sth_iptal = 0
AND sth_tip = 1
AND sth_evraktip IN (1, 4)
AND sth_normal_iade = 0
AND sth_tarih >= '20250101' AND sth_tarih < '20270101'
GROUP BY MONTH(sth_tarih)
) a
ORDER BY a.Ay;
Örnek çıktı:
| Ay | Ciro2025 | Ciro2026 | BuyumeYuzde |
|---|---|---|---|
| 1 | 412500.00 | 508900.00 | 23.37 |
| 2 | 388150.00 | 361400.00 | -6.89 |
| 3 | 0.00 | 96200.00 | NULL |
4. Eksik ayları sıfırla göster (12 satır garantisi)
GROUP BY MONTH() yalnızca veri olan ayları döndürür. Şubat'ta satış yoksa tablo 11 satır çıkar, Excel grafiği kayar, ay isimleri birbirine kaymaz ama sütun hizası bozulur. 12 ayı sabitlemek için VALUES ile takvim üretip LEFT JOIN yapın.
WITH Aylar AS (
SELECT * FROM (VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)) v(Ay)
),
Ciro AS (
SELECT YEAR(sth_tarih) AS Yil, MONTH(sth_tarih) AS Ay, SUM(sth_tutar) AS Tutar
FROM STOK_HAREKETLERI
WHERE sth_iptal = 0
AND sth_tip = 1 AND sth_evraktip IN (1, 4) AND sth_normal_iade = 0
AND sth_tarih >= '20250101' AND sth_tarih < '20270101'
GROUP BY YEAR(sth_tarih), MONTH(sth_tarih)
)
SELECT a.Ay,
ISNULL(MAX(CASE WHEN c.Yil = 2025 THEN c.Tutar END), 0) AS Ciro2025,
ISNULL(MAX(CASE WHEN c.Yil = 2026 THEN c.Tutar END), 0) AS Ciro2026
FROM Aylar a
LEFT JOIN Ciro c ON c.Ay = a.Ay
GROUP BY a.Ay
ORDER BY a.Ay;
MAX(CASE ...) kalıbı, aynı ay için iki yıl satırı gelirse pivot görevi görür. ISNULL de boş ayı sıfıra çevirir; böylece yüzde hesabında NULLIF devreye girer ve sıfıra bölme olmaz.
5. Kümülatif (YTD) karşılaştırma
Yönetim raporlarında ay ay bakmak yetmez, yıl başından bugüne birikim istenir. Pencere fonksiyonu ile aynı sonuç kümesi üzerinden kümülatif toplam alın.
WITH AylikCiro AS (
SELECT
MONTH(sth_tarih) AS Ay,
SUM(CASE WHEN YEAR(sth_tarih) = 2025
THEN CASE WHEN sth_tip = 1 THEN sth_tutar ELSE -sth_tutar END ELSE 0 END) AS Net2025,
SUM(CASE WHEN YEAR(sth_tarih) = 2026
THEN CASE WHEN sth_tip = 1 THEN sth_tutar ELSE -sth_tutar END ELSE 0 END) AS Net2026
FROM STOK_HAREKETLERI
WHERE sth_iptal = 0
AND (
(sth_tip = 1 AND sth_evraktip IN (1, 4) AND sth_normal_iade = 0)
OR (sth_tip = 0 AND sth_evraktip IN (3, 13) AND sth_normal_iade = 1)
)
AND sth_tarih >= '20250101' AND sth_tarih < '20270101'
GROUP BY MONTH(sth_tarih)
)
SELECT Ay, Net2025, Net2026,
SUM(Net2025) OVER (ORDER BY Ay ROWS UNBOUNDED PRECEDING) AS Kumulatif2025,
SUM(Net2026) OVER (ORDER BY Ay ROWS UNBOUNDED PRECEDING) AS Kumulatif2026
FROM AylikCiro
ORDER BY Ay;
Dikkat: pencere fonksiyonunu yıl kırılımlı satırlar üzerinde kullanırsanız aynı ay iki kez toplanır. Bu yüzden önce pivot edip tek satıra indiriyorum, sonra kümülatif alıyorum.
6. Sadece fatura bazlı hızlı varyant
Muhasebe ile konuşuyorsanız ve irsaliyeden faturaya dönmüş satışlar zaten fatura altında toplanıyorsa sth_evraktip = 4 yeterli olabilir. Bu sürüm operasyonel ciro için eksiktir, fatura cirosu için doğrudur.
SELECT
MONTH(sth_tarih) AS Ay,
SUM(CASE WHEN YEAR(sth_tarih) = 2025 THEN sth_tutar ELSE 0 END) AS Fatura2025,
SUM(CASE WHEN YEAR(sth_tarih) = 2026 THEN sth_tutar ELSE 0 END) AS Fatura2026
FROM STOK_HAREKETLERI
WHERE sth_iptal = 0
AND sth_tip = 1
AND sth_evraktip = 4
AND sth_normal_iade = 0
AND sth_tarih >= '20250101' AND sth_tarih < '20270101'
GROUP BY MONTH(sth_tarih)
ORDER BY Ay;
sth_normal_iade = 0 şartını burada da bırakıyorum: alış iadesi evraktip = 4 ve normal_iade = 1 olarak çıkış tarafında durur; filtresiz bırakırsanız ciroyu yukarı çeker. Yılları sabitlemek yerine YEAR(GETDATE()) ve YEAR(GETDATE()) - 1 yazarak raporu kalıcı hale getirebilirsiniz.
Sık yapılan hatalar
sth_iptal = 0filtresini unutmak; iptal edilmiş irsaliye ve faturalar toplama girer, rakam yukarı sapar.- Sadece
sth_evraktip = 4demek; faturalanmış irsaliye satışları kaybolur, ciro sessizce düşük çıkar. - Sadece
sth_tip = 1demek; depo çıkış fişi ve alış iadesi ciroya karışır. - Satış iadesini hiç düşmemek; iade giriş tarafında olduğu için
sth_normal_iadekontrolü tek başına yetmez,sth_tipile birlikte okunmalı. - Büyüme yüzdesini
NULLIFolmadan yazmak; geçen yıl boş olan ayda divide by zero hatası alınır. GROUP BYsonucunu 12 ay sanmak; satışsız ay satırı hiç gelmez, grafik kayar.- Aynı rapor için
STOK_HAREKETLERIveCARI_HESAP_HAREKETLERItutarlarını tek toplamda birleştirmek; aynı fatura iki kez sayılır. - Tarih fonksiyonlarını int kolonlara uygulamak; örtük dönüşüm 8115 hatası verir. Raporlamadaki benzer tuzaklar için ERP raporlamada en sık 10 hata yazısına bakın.
Sık sorulanlar
Neden BETWEEN yerine >= ve < kullanıyorsunuz? sth_tarih datetime olduğu için BETWEEN '20261231' AND ... yazıldığında gün sonu saatli kayıtlar sınırda kalabilir. Bitiş tarihini dışlayan >= ilk AND < sonraki ilk gün kalıbı her zaman güvenlidir.
Ciro neden muhasebedeki satış hesabıyla birebir tutmuyor? STOK_HAREKETLERI mal hareketini, CARI_HESAP_HAREKETLERI parasal ayağı tutar. KDV, masraf, kur farkı ve dönem sonu işlemleri cari tarafta farklılaşır. Ürün bazlı ciro için stok tarafını, cari ekstre ve bakiye için cari tarafı kullanın; ikisini aynı toplamda birleştirmeyin. Ayrıntılı kalıplar Mikro satış ve fatura raporu SQL yazısında.
İadeyi ayrı sütunda göstermek istiyorum, nasıl yaparım? Aynı alt sorguya SUM(CASE WHEN sth_tip = 0 AND sth_normal_iade = 1 THEN sth_tutar ELSE 0 END) ekleyin; böylece brüt satış, iade ve net ciro üç sütun olarak yan yana gelir.
Bu sorgular v16'da da çalışır mı? Kullanılan kolonların tamamı iki sürümde de aynı addadır. Pencere fonksiyonu ve VALUES takvimi SQL Server 2008 ve üzerinde çalışır. Kurulumunuza özel ek alanlar varsa sorguyu kendi şemanızda doğrulayın.
Ay bazında iki yılı yan yana görmek başlangıçtır; asıl fark, filtreleri bilerek kurmaktan geliyor. Hangi ayın neden düştüğünü, hangi müşterinin kaybettiğini tek tek SQL yazmadan görmek istiyorsanız sorunuzu Türkçe yazmanız yeterli: v16/v17 şemanıza uygun sorguyu saniyeler içinde alırsınız.
Ücretsiz üye olun, ciro karşılaştırmanızı doğal dille alın ->
Bu raporu SQL yazmadan, Türkçe sorarak alın
Türkçe sorunuzu yazın, şemanıza uygun çalışan SQL'i ya da hazır uygulamanızı saniyeler içinde alın.




