SQL Server’da FIFO Maliyetinin T-SQL ile Uygulanması

Herkese merhaba,

Bu yazıda SQL Server’da fifo maliyetinin t-sql ile uygulanması hakkında bilgi vereceğim.

Aslında bu bilgiyi veren ve kodları hazırlayan kişi Vedat Özer Bey. Sorguyu hazırlamış. Gerekli açıklamaları eklemiş. Bana da paylaşması kaldı.

Kendisine bu çalışması için ayrıca teşekkürler.

Aşağıdaki kod bloğunu yeni oluşturduğunuz veritabanı üzerinde deneyebilirsiniz.

/* Bu kısmı yorumdan çıkarıp çalıştırabilirsiniz. Bu sayede yeni database oluşup sonrasında sorgu çalışacaktır. CREATE DATABASE YeniDB USE YeniDB */ SET NOCOUNT ON; /********************************************************************************************************************** Hesaplama Türü : Fifo Maliyet Fifo Yöntemi (İlk Giren İlk Çıkar): Fifo değerleme yöntemi, üretime verilecek olan veya satılacak malların stoklara ilk önce giren mallardan olması gerektiği varsayımına dayanır. Stoktaki malların kullanılma sırası ilk alınan mallardan başlanarak sırasyla devam eder. Yani stoklara giren malların yine giriş sırasıyla stoktan çıkarlar. Sorguyu Yazan : VEDAT ÖZER Tablo İsimleri Stok Hareketler Tablosu : STOK_DETAYLI Stok Giriş Çıkış Eşlemesi : FIFOESLEME Envanter Tablosu : FIFOENVANTER Negatif Bilgiler : STOKNEGATIF Stok Negatife düştüğünde maliyet hesabı yapılmıyacak. Eksiğe düştüğü tarihten itibaren aşağıdaki tabloya yazacaz. **********************************************************************************************************************/
IF
(
    SELECT COUNT(*)
    FROM dbo.sysobjects
    WHERE id = OBJECT_ID(N'STOKNEGATIF')
          AND OBJECTPROPERTY(id, 'IsUserTable') = 1
) > 0
BEGIN
    DROP TABLE [dbo].STOKNEGATIF;
END;
CREATE TABLE STOKNEGATIF
(
    [KODU] VARCHAR(150) NOT NULL,
    [ADI] VARCHAR(250) NOT NULL,
    [TARIH] DATETIME NOT NULL,
    [AMBAR] VARCHAR(150) NOT NULL,
    [GIRISCIKIS] VARCHAR(150),
    [FISTURU] VARCHAR(200),
    [MIKTAR] FLOAT NOT NULL,
    KALAN DECIMAL(38, 2)
);
CREATE CLUSTERED INDEX TANIMLAMA ON STOKNEGATIF (KODU, AMBAR, TARIH);
CREATE INDEX DENEME ON STOKNEGATIF (KODU);
/********************************************************************************************************************** Maliyet hesaplamaların doğru bir şekilde yapmamız için Giriş ve Çıkış hareketlerin ilişkilendirmemiz lazım. Aşağıdaki tabloya giriş ve çıkış bilgilerini yazacaz. **********************************************************************************************************************/
IF
(
    SELECT COUNT(*)
    FROM dbo.sysobjects
    WHERE id = OBJECT_ID(N'FIFOESLEME')
          AND OBJECTPROPERTY(id, 'IsUserTable') = 1
) > 0
BEGIN
    DROP TABLE [dbo].FIFOESLEME;
END;
CREATE TABLE [dbo].FIFOESLEME
(
    [KODU] VARCHAR(150),
    [AMBAR] VARCHAR(50),
    GIRISID INT,
    CIKISID INT,
    MIKTAR FLOAT,
    GIRISFIYAT FLOAT,
    CIKISFIYAT FLOAT,
    ALIMTARIHI DATE,
    SATISTARIHI DATE
);
CREATE CLUSTERED INDEX ITANIMLAMADX11
ON FIFOESLEME (
                  [KODU],
                  AMBAR,
                  ALIMTARIHI,
                  GIRISID
              );
CREATE INDEX KART ON FIFOESLEME ([KODU]);
/********************************************************************************************************************** Ambarlara göre elimizdeki stokğun bilgilerini yazacağımız tabloyu oluşturuyoruz. **********************************************************************************************************************/
IF
(
    SELECT COUNT(*)
    FROM dbo.sysobjects
    WHERE id = OBJECT_ID(N'FIFOENVANTER')
          AND OBJECTPROPERTY(id, 'IsUserTable') = 1
) > 0
BEGIN
    DROP TABLE [dbo].FIFOENVANTER;
END;
CREATE TABLE [dbo].FIFOENVANTER
(
    [KODU] VARCHAR(150),
    [AMBAR] VARCHAR(100),
    GIRISID INT,
    MIKTAR FLOAT,
    GIRISFIYAT FLOAT,
    ALIMTARIHI DATE
);
CREATE CLUSTERED INDEX IDX11
ON FIFOENVANTER (
                    [KODU],
                    AMBAR,
                    ALIMTARIHI,
                    GIRISID
                );
CREATE INDEX DENEME ON FIFOENVANTER ([KODU]);
/********************************************************************************************************************** Aşağıdaki alanlarda maliyet hesabını yapacağımız satırları ekliyoruz. **********************************************************************************************************************/
IF
(
    SELECT COUNT(*)FROM sys.tables WHERE name = 'STOK_DETAYLI'
) > 0
BEGIN
    DROP TABLE STOK_DETAYLI;
END;
CREATE TABLE [dbo].[STOK_DETAYLI]
(
    [ID] [INT] IDENTITY(1, 1) NOT NULL,
    [KODU] VARCHAR(150) NOT NULL,
    [ADI] VARCHAR(250) NOT NULL,
    [TARIH] DATETIME NOT NULL,
    [AMBAR] VARCHAR(100) NOT NULL,
    [GIRISCIKIS] VARCHAR(100),
    [FISTURU] VARCHAR(200),
    [MIKTAR] FLOAT NOT NULL,
    [BIRIMFIYAT] FLOAT,
    [TUTAR] FLOAT,
    [MALIYET] FLOAT
);
CREATE CLUSTERED INDEX STOK ON [STOK_DETAYLI] (KODU, TARIH, AMBAR);
CREATE INDEX SIRA ON [STOK_DETAYLI] (KODU);
INSERT INTO [STOK_DETAYLI]
SELECT 'STOK.001',
       'Deneme Stok',
       '2018-01-01 09:34:00.000',
       'Merkez',
       'GIRIS',
       'Devir',
       '15',
       '1.25',
       '18.75',
       '0'
UNION ALL
SELECT 'STOK.001',
       'Deneme Stok',
       '2018-01-02 09:35:00.000',
       'Merkez',
       'GIRIS',
       'SatınAlma',
       '15',
       '2',
       '30',
       '0'
UNION ALL
SELECT 'STOK.001',
       'Deneme Stok',
       '2018-01-02 09:36:00.000',
       'Merkez',
       'CIKIS',
       'Satış',
       '22',
       '3',
       '66',
       '0'
UNION ALL
SELECT 'STOK.001',
       'Deneme Stok',
       '2018-01-02 09:38:00.000',
       'Merkez',
       'GIRIS',
       'SatınAlma',
       '20',
       '1.75',
       '35',
       '0'
UNION ALL
SELECT 'STOK.001',
       'Deneme Stok',
       '2018-01-03 10:35:00.000',
       'Antalya',
       'GIRIS',
       'SatınAlma',
       '15',
       '2.50',
       '37.50',
       '0'
UNION ALL
SELECT 'STOK.001',
       'Deneme Stok',
       '2018-01-03 10:36:00.000',
       'Antalya',
       'CIKIS',
       'Satış',
       '12',
       '3',
       '36',
       '0'
UNION ALL
SELECT 'STOK.001',
       'Deneme Stok',
       '2018-01-04 11:35:00.000',
       'Merkez',
       'GIRIS',
       'SatınAlma',
       '20',
       '1.75',
       '35',
       '0'
UNION ALL
SELECT 'STOK.001',
       'Deneme Stok',
       '2018-01-04 11:40:00.000',
       'Merkez',
       'CIKIS',
       'Satış',
       '15',
       '3',
       '45',
       '0'
UNION ALL
SELECT 'STOK.001',
       'Deneme Stok',
       '2018-01-05 11:41:00.000',
       'Merkez',
       'GIRIS',
       'SatınAlma',
       '8',
       '2.50',
       '20',
       '0'
UNION ALL
SELECT 'STOK.002',
       'Deneme',
       '2018-02-01 09:34:00.000',
       'Merkez',
       'GIRIS',
       'Devir',
       '15',
       '1.25',
       '18.75',
       '0'
UNION ALL
SELECT 'STOK.002',
       'Deneme',
       '2018-02-02 09:35:00.000',
       'Merkez',
       'GIRIS',
       'SatınAlma',
       '15',
       '2',
       '30',
       '0'
UNION ALL
SELECT 'STOK.002',
       'Deneme',
       '2018-02-02 09:36:00.000',
       'Merkez',
       'CIKIS',
       'Satış',
       '22',
       '3',
       '66',
       '0'
UNION ALL
SELECT 'STOK.002',
       'Deneme',
       '2018-02-02 09:38:00.000',
       'Merkez',
       'GIRIS',
       'SatınAlma',
       '20',
       '1.75',
       '35',
       '0'
UNION ALL
SELECT 'STOK.002',
       'Deneme',
       '2018-02-03 10:35:00.000',
       'Antalya',
       'GIRIS',
       'SatınAlma',
       '15',
       '2.50',
       '37.50',
       '0'
UNION ALL
SELECT 'STOK.002',
       'Deneme',
       '2018-02-03 10:36:00.000',
       'Antalya',
       'CIKIS',
       'Satış',
       '12',
       '3',
       '36',
       '0'
UNION ALL
SELECT 'STOK.002',
       'Deneme',
       '2018-02-04 11:35:00.000',
       'Merkez',
       'GIRIS',
       'SatınAlma',
       '20',
       '1.75',
       '35',
       '0'
UNION ALL
SELECT 'STOK.002',
       'Deneme',
       '2018-02-04 11:40:00.000',
       'Merkez',
       'CIKIS',
       'Satış',
       '15',
       '3',
       '45',
       '0'
UNION ALL
SELECT 'STOK.002',
       'Deneme',
       '2018-02-05 11:41:00.000',
       'Merkez',
       'GIRIS',
       'SatınAlma',
       '8',
       '2.50',
       '20',
       '0';
                                                                                                                                                                                                                                                                                                                               /********************************************************************************************************************** Artık Gerekli maliyet hesaplamalarına başlıyacağız. Numaralı şekilde gideceğiz **********************************************************************************************************************/ /********************************************************************************************************************** 1) Gerçek tabloda olan bütün verileri Geçiçi tabloya aktarıyoruz. ********************************************************************************************************************/
IF (OBJECT_ID('tempdb..##VERIDATA') IS NOT NULL)
BEGIN
    DROP TABLE ##VERIDATA;
END;
SELECT *,
       CAST(0 AS DECIMAL(38, 2)) AS KALAN
INTO ##VERIDATA
FROM [STOK_DETAYLI] WITH (NOLOCK, INDEX(STOK));
CREATE INDEX STOK ON ##VERIDATA (KODU);
IF (OBJECT_ID('tempdb..##FIFOESLEME') IS NOT NULL)
BEGIN
    DROP TABLE ##FIFOESLEME;
END;
CREATE TABLE ##FIFOESLEME
(
    [KODU] VARCHAR(150),
    [AMBAR] VARCHAR(50),
    GIRISID INT,
    CIKISID INT,
    MIKTAR FLOAT,
    GIRISFIYAT FLOAT,
    CIKISFIYAT FLOAT,
    ALIMTARIHI DATE,
    SATISTARIHI DATE
);
CREATE CLUSTERED INDEX ITANIMLAMADX11
ON ##FIFOESLEME (
                    [KODU],
                    AMBAR,
                    ALIMTARIHI,
                    GIRISID
                );
CREATE INDEX KART ON ##FIFOESLEME (GIRISID);
/********************************************************************************************************************** 2) Geçiçi tabloda Yürüyen bakiye işlemini yapıyoruz. Eksi değer veren varmı diye Normal şartlarda 2012 versiyonda Partıtıon By fonksiyonu ile gidilebilir. **********************************************************************************************************************/
DECLARE @STOKKODU VARCHAR(150),
        @AMBAR VARCHAR(50),
        @KALAN DECIMAL(38, 2);
SET @STOKKODU = '';
SET @AMBAR = '';
SET @KALAN = 0;
UPDATE a
SET @KALAN=KALAN = CASE
                       WHEN GIRISCIKIS = 'GIRIS' THEN
                           MIKTAR
                       ELSE
                           -MIKTAR
                   END + CASE
                             WHEN KODU = @STOKKODU
                                  AND AMBAR = @AMBAR THEN
                                 @KALAN
                             ELSE
                                 0
                         END,
    @STOKKODU = KODU,
    @AMBAR = AMBAR
FROM ##VERIDATA a WITH (TABLOCKX, INDEX(STOK))
OPTION (MAXDOP 5);
/********************************************************************************************************************** 3) Döngü ile verileri yazacağımız geçiçi tabloyu oluşturuyoruz. **********************************************************************************************************************/
IF (OBJECT_ID('tempdb..#GECICI') IS NOT NULL)
BEGIN
    DROP TABLE #GECICI;
END;
CREATE TABLE #GECICI
(
    ID INT,
    TARIH DATETIME,
    [KODU] VARCHAR(150),
    [AMBAR] VARCHAR(50),
    GIRISCIKIS VARCHAR(35),
    BIRIMFIYAT FLOAT,
    MIKTAR DECIMAL(38, 2)
);
CREATE CLUSTERED INDEX SIRALAMA ON #GECICI ([KODU], AMBAR, TARIH);
CREATE INDEX VEDAT ON #GECICI ([KODU]);
CREATE INDEX SATIR
ON #GECICI (ID)
INCLUDE (
            [KODU],
            AMBAR,
            TARIH,
            MIKTAR
        );
/********************************************************************************************************************** 4) Negatif olan stokları toplu olarak tabloya aktarıyoruz. **********************************************************************************************************************/
TRUNCATE TABLE STOKNEGATIF;
INSERT INTO STOKNEGATIF
SELECT KODU,
       ADI,
       TARIH,
       AMBAR,
       GIRISCIKIS,
       FISTURU,
       MIKTAR,
       KALAN
FROM ##VERIDATA
WHERE KALAN < 0;
/********************************************************************************************************************** 5) Malıyet Hesabını yapıyoruz. **********************************************************************************************************************/
DECLARE @KODU VARCHAR(150),
        @AMBARR VARCHAR(50);
DECLARE STOK CURSOR FOR
SELECT DISTINCT
       KODU,
       AMBAR
FROM ##VERIDATA
ORDER BY KODU,
         AMBAR;
OPEN STOK;
FETCH NEXT FROM STOK
INTO @KODU,
     @AMBAR;
WHILE @@FETCH_STATUS = 0
BEGIN
    DELETE #GECICI WITH (TABLOCK)
    WHERE KODU = @KODU
          AND AMBAR = @AMBAR;
    IF
    (
        SELECT COUNT(*)
        FROM STOKNEGATIF WITH (NOLOCK, INDEX(DENEME))
        WHERE KODU = @KODU
              AND AMBAR = @AMBAR
              AND KALAN < 0
    ) = 0
    BEGIN
        DECLARE @ID INT,
                @TARIH DATETIME,
                @GIRISCIKIS VARCHAR(10),
                @FISTUR VARCHAR(60),
                @MIKTAR DECIMAL(38, 2),
                @BIRIMFIYAT DECIMAL(38, 2);
        DECLARE @GIRENID INT,
                @GIRENTARIH DATETIME,
                @GIRENFISTUR VARCHAR(60),
                @GIRENMIKTAR DECIMAL(38, 2),
                @GIRENBIRIMFIYAT DECIMAL(38, 2);
        DECLARE FIFOMALIYET CURSOR FOR
        SELECT ID,
               TARIH,
               GIRISCIKIS,
               FISTURU,
               MIKTAR,
               BIRIMFIYAT
        FROM ##VERIDATA
        WHERE KODU = @KODU
              AND AMBAR = @AMBAR;
        OPEN FIFOMALIYET;
        FETCH NEXT FROM FIFOMALIYET
        INTO @ID,
             @TARIH,
             @GIRISCIKIS,
             @FISTUR,
             @MIKTAR,
             @BIRIMFIYAT;
        WHILE @@FETCH_STATUS = 0
        BEGIN
            IF @GIRISCIKIS = 'GIRIS'
            BEGIN
                INSERT INTO #GECICI
                VALUES
                (@ID, @TARIH, @KODU, @AMBAR, @GIRISCIKIS, @BIRIMFIYAT, @MIKTAR);
            END;
            IF @GIRISCIKIS = 'CIKIS'
            BEGIN
                WHILE (1 = 1)
                BEGIN
                    SELECT TOP 1
                           @GIRENID = ID,
                           @GIRENTARIH = TARIH,
                           @GIRENMIKTAR = MIKTAR,
                           @GIRENBIRIMFIYAT = BIRIMFIYAT
                    FROM #GECICI
                    WHERE KODU = @KODU
                          AND AMBAR = @AMBAR
                          AND GIRISCIKIS = 'GIRIS'
                          AND MIKTAR > 0
                    ORDER BY TARIH ASC;
                    IF @GIRENMIKTAR >= @MIKTAR
                       AND @MIKTAR <> 0
                    BEGIN
                        INSERT INTO ##FIFOESLEME
                        (
                            KODU,
                            AMBAR,
                            GIRISID,
                            CIKISID,
                            MIKTAR,
                            GIRISFIYAT,
                            CIKISFIYAT,
                            ALIMTARIHI,
                            SATISTARIHI
                        )
                        VALUES
                        (@KODU, @AMBAR, @GIRENID, @ID, @MIKTAR, @GIRENBIRIMFIYAT, @BIRIMFIYAT, @GIRENTARIH, @TARIH);
                        UPDATE #GECICI WITH (TABLOCK)
                        SET MIKTAR = COALESCE(MIKTAR, 0) - COALESCE(@MIKTAR, 0)
                        WHERE ID = @GIRENID;
                        BREAK;
                    END;
                    ELSE
                    BEGIN
                        INSERT INTO ##FIFOESLEME
                        (
                            KODU,
                            AMBAR,
                            GIRISID,
                            CIKISID,
                            MIKTAR,
                            GIRISFIYAT,
                            CIKISFIYAT,
                            ALIMTARIHI,
                            SATISTARIHI
                        )
                        VALUES
                        (@KODU, @AMBAR, @GIRENID, @ID, @GIRENMIKTAR, @GIRENBIRIMFIYAT, @BIRIMFIYAT, @GIRENTARIH, @TARIH);
                        UPDATE #GECICI WITH (TABLOCK)
                        SET MIKTAR = COALESCE(MIKTAR, 0) - COALESCE(@GIRENMIKTAR, 0)
                        WHERE ID = @GIRENID;
                        SET @MIKTAR = @MIKTAR - @GIRENMIKTAR;
                    END;
                END;
            END;
            FETCH NEXT FROM FIFOMALIYET
            INTO @ID,
                 @TARIH,
                 @GIRISCIKIS,
                 @FISTUR,
                 @MIKTAR,
                 @BIRIMFIYAT;
        END;
        /********************************************************************************************************************** 5) Malıyet Hesabını bitti. Elimizde kalan malzemeleri envanter bölümüne aktarıyoruz. **********************************************************************************************************************/
        DELETE FIFOENVANTER WITH (TABLOCK)
        WHERE KODU = @KODU
              AND AMBAR = @AMBAR;
        INSERT INTO FIFOENVANTER
        (
            KODU,
            AMBAR,
            GIRISID,
            MIKTAR,
            GIRISFIYAT,
            ALIMTARIHI
        )
        SELECT KODU,
               AMBAR,
               ID,
               MIKTAR,
               BIRIMFIYAT,
               TARIH
        FROM #GECICI
        WHERE ISNULL(MIKTAR, 0) > 0
              AND GIRISCIKIS = 'GIRIS'
              AND KODU = @KODU
              AND AMBAR = @AMBAR;

        /********************************************************************************************************************** 6) Çıkışların maliyetini hesaplaması aşağıdaki şekilde yapılmaktadır. İlk İşlemde ; (MIKTAR * GIRISFIYAT) TUTAR Sonrasında ; (TUTAR / MIKTAR) **********************************************************************************************************************/
        UPDATE V
        SET V.MALIYET = CAST(F.MALIYET AS DECIMAL(38, 8))
        FROM STOK_DETAYLI V
            LEFT JOIN
            (
                SELECT CIKISID,
                       AMBAR,
                       KODU,
                       ISNULL(SUM(TUTAR) / NULLIF(SUM(MIKTAR), 0), 0) MALIYET
                FROM
                (
                    SELECT CIKISID,
                           AMBAR,
                           KODU,
                           CAST(MIKTAR * GIRISFIYAT AS DECIMAL(38, 8)) TUTAR,
                           (MIKTAR) AS MIKTAR
                    FROM ##FIFOESLEME
                    WHERE KODU = @KODU
                          AND AMBAR = @AMBAR
                ) AS YHP
                GROUP BY CIKISID,
                         KODU,
                         AMBAR
            ) AS F
                ON F.CIKISID = V.ID
        WHERE GIRISCIKIS = 'CIKIS'
              AND V.KODU = @KODU
              AND V.AMBAR = @AMBAR;
        /********************************************************************************************************************** 7) Geçiçi tabloda olan Giriş ve Çıkış eşleşmelerini gerçek tabloya yazıyoruz. **********************************************************************************************************************/
        DELETE FIFOESLEME WITH (TABLOCK)
        WHERE KODU = @KODU
              AND AMBAR = @AMBAR;
        INSERT INTO FIFOESLEME
        SELECT *
        FROM ##FIFOESLEME
        WHERE KODU = @KODU
              AND AMBAR = @AMBAR;
        DELETE ##FIFOESLEME WITH (TABLOCK)
        WHERE KODU = @KODU
              AND AMBAR = @AMBAR;
        CLOSE FIFOMALIYET;
        DEALLOCATE FIFOMALIYET;
    END;
    FETCH NEXT FROM STOK
    INTO @KODU,
         @AMBAR;
END;
CLOSE STOK;
DEALLOCATE STOK;
SELECT *
FROM FIFOENVANTER;
SELECT *
FROM FIFOESLEME;
SELECT *
FROM STOK_DETAYLI;

Sorguyu çalıştırdığınızda aşağıdaki gibi bir sonuç alacaksınız.

SQL Server’da FIFO Maliyetinin T-SQL ile Uygulanması

Sorgu ile ilgili detaylı bilgi almak isterseniz Vedat Özer Beye ulaşabilirsiniz.

Herkese çalışma hayatında ve yaşamında başarılar kolaylıklar.

671 Kez Okundu

Yavuz Selim Kart

Yazılım ve veri tabanında bildiklerimi anlatmaya gayret ediyorum. Halen birçok programlama dili üzerinde araştırmalar yaparak kendimi geliştiriyorum. Bunlar haricinde Grafik Tasarım ve Wordpress konularına da ilgim bulunmakta. SEO ve Sosyal medya yönetimleri hakkında da bilgim mevcuttur. Kısaca çok çalışmayı seven azimli biriyim.

You may also like...

Yazı kopyalamak yasaktır!