none
Aufteilen des Umsatzes auf einzelne Monate des Projektzeitraumes RRS feed

  • Frage

  • Hallo an alle

    Ich muss ein interessantes Problem lösen, aber mir fällt keine Vorgehensweise ein. Vielleicht könnt ihr mir helfen.

    Eine Kundendatenbank verwaltet Projekte, die unter anderem einen Umsatz und einen Zeitraum beinhalten. Z.B. Projekt A geht vom 16.04.2019 bis zum 31.08.2019 und bringt einen Umsatz von 1000 Euro. Die Projektzeiten können nur wenige Tage dauern oder auch jahrübergreifend (10.12.2018 bis 31.01.2020).

    Meine Aufgabe besteht jetzt darin, dass ich den Projektumsatz auf die einzelnen Monate des Projekts umrechnen muss. Um beim obigen Beispiel zu bleiben:

    So soll das ganze dann aussehen.

    16.04.2019 - 31.08.2019 = 1000 Euro

    16.04.2019 - 30.04.2019 = 111,08 Euro

    01.05.2019 - 31.05.2019 = 222,23 Euro

    01.06.2019 - 30.06.2019 = 222,23 Euro

    01.07.2019 - 31.07.2019 = 222,23 Euro

    01.08.2019 - 31.08.2019 = 222,23 Euro

    Nur habe ich keinen Plan, wie ich das in einer View bzw. Storedproc umsetzen soll. Kann mir einer von euch einen Tipp geben?


    Thorsten Schröer Dipl-Inform (FH) - EDV, Beratung & Service - double-D-IT -Office- / Information system support Bismarckstraße 18a Germany-12169 Berlin Steglitz phone: +49-(0)30-303286770 fax: +49-(0)30-303286779

    Donnerstag, 12. Dezember 2019 11:59

Antworten

  • Hallo Thorsten,

    hier ein Beispiel für eine tageweise Umrechnung der Ausgaben auf Monate.

    Schönen Abend.

    IF OBJECT_ID(N'tempdb..#Calendar') IS NOT NULL
        DROP TABLE #Calendar;
    
    DECLARE @DateMax DATETIME, @DateMin DATETIME;
    DECLARE @Periode VARCHAR(MAX);
    DECLARE @Zeitachse CHAR(1);
    
    
    SET @DateMin = '01.01.2019';
    SET @DateMax = '31.12.2020';
    SET @Zeitachse = 'm';  /* Schalter: Y = Jahr; Q = Quartal; M = Monat */
    
    DECLARE @Projekte TABLE ( BeginnProjekt DATETIME, EndeProjekt DATETIME, Ausgaben MONEY );
    
    INSERT INTO @Projekte VALUES 
    	( '16.04.2019', '31.08.2019', 1000 );
    
    CREATE TABLE #Calendar(
       Jahr int NULL,
       Quartal int NULL,
       Monat CHAR (2) NULL,
       Beginn_Periode DATETIME,
       Ende_Periode DATETIME,
       Periode varchar(10) NULL,
       Periodentage INT);
    
    WITH Calendar_func AS 
    (
       SELECT @DateMin AS DateDay
       UNION ALL
       SELECT DATEADD(DAY, 1, DateDay)
       FROM Calendar_func
       WHERE DateDay < @DateMax
    )
    
    INSERT INTO #Calendar (Jahr, Quartal, Monat, Beginn_Periode, Ende_Periode, Periode, Periodentage)
    SELECT DISTINCT
       DATEPART(yy, DateDay) AS [Jahr],
       
    	CASE WHEN @Zeitachse = 'Y' 
             THEN NULL
             ELSE CASE WHEN @Zeitachse = 'Q' 
    			       THEN DATEPART(qq, DateDay)
                       ELSE CASE WHEN @Zeitachse = 'M' 
    						     THEN DATEPART(qq, DateDay)
    						     ELSE NULL
    					     END 
                  END 
       END AS [Quartal],
        
    	CASE WHEN @Zeitachse = 'Y' 
             THEN NULL
             ELSE CASE WHEN @Zeitachse = 'Q' 
    			       THEN NULL
                       ELSE DATEPART(mm, DateDay)
                   END 
        END AS [Monat],
    
    
    	CASE WHEN @Zeitachse = 'Y' 
             THEN DATEADD(yy,DATEDIFF(yy,0,DateDay),0)
             ELSE CASE WHEN @Zeitachse = 'Q'
    				   THEN DATEADD(qq,DATEDIFF(qq,0,DateDay),0)
    				   ELSE CASE WHEN @Zeitachse = 'M'
    						     THEN DATEADD(mm,DATEDIFF(mm,0,DateDay),0) 
    						     ELSE NULL
    					    END
    			  END 
    	END AS [Beginn_Periode],
    	
    	
    
    	CASE WHEN @Zeitachse = 'Y' 
             THEN DATEADD(yy,DATEDIFF(yy,0,DateDay)+1,-1)
             ELSE CASE WHEN @Zeitachse = 'Q'
    				   THEN DATEADD(qq,DATEDIFF(qq,0,DateDay)+1,-1)
    				   ELSE CASE WHEN @Zeitachse = 'M'
    						     THEN DATEADD(mm,DATEDIFF(mm,0,DateDay)+1,-1)
    						     ELSE NULL
    					    END
    			  END 
    	END AS [Ende_Periode],
    
     
    	CASE WHEN @Zeitachse = 'Y' 
             THEN CAST (DATEPART(yy, DateDay) AS CHAR (4))
             ELSE CASE WHEN @Zeitachse = 'Q'
    				   THEN CAST (DATEPART(yy, DateDay) AS CHAR (4)) 
    						+ ' - Q' 
    						+ CAST (DATEPART(qq, DateDay) AS CHAR (1))
    				   ELSE CASE WHEN @Zeitachse = 'M'
    						     THEN CAST (DATEPART(yy, DateDay) AS CHAR (4)) 
    							 + ' - ' 
    						     + CASE 
    									WHEN DATEPART(mm, DateDay) < 10 
    									THEN RIGHT('0' + CONVERT( varchar, DATEPART(mm, DateDay) ), 2) 
    									ELSE CONVERT(VARCHAR, DATEPART(mm, DateDay))
    							 END 
    						     ELSE NULL
    					    END
    			  END 
    	END AS [Periode],
    
    	CASE WHEN @Zeitachse = 'Y' 
             THEN DATEDIFF (d, DATEADD(yy,DATEDIFF(yy,0,DateDay),0), DATEADD(yy,DATEDIFF(yy,0,DateDay)+1,-0))
             ELSE CASE WHEN @Zeitachse = 'Q'
    				   THEN DATEDIFF (d, DATEADD(qq,DATEDIFF(qq,0,DateDay),0), DATEADD(qq,DATEDIFF(qq,0,DateDay)+1,-0))
    				   ELSE CASE WHEN @Zeitachse = 'M'
    						     THEN DATEDIFF (d, DATEADD(mm,DATEDIFF(mm,0,DateDay),0), DATEADD(mm,DATEDIFF(mm,0,DateDay)+1,-0))
    					    END
    			  END 
    	END AS [Periodentage]
     
    FROM Calendar_func
    OPTION ( MAXRECURSION 0 )
    
    SELECT * FROM @Projekte
    
    SELECT Periode,
        CASE WHEN BeginnProjekt > c.Beginn_Periode THEN BeginnProjekt ELSE c.Beginn_Periode END as  von,
        CASE WHEN EndeProjekt < c.Ende_Periode THEN EndeProjekt ELSE c.Ende_Periode END AS bis,
        DATEDIFF (dd, CASE WHEN BeginnProjekt > c.Beginn_Periode THEN BeginnProjekt ELSE c.Beginn_Periode END, CASE WHEN EndeProjekt < c.Ende_Periode THEN EndeProjekt ELSE c.Ende_Periode END) + 1 AS Tage,
        Ausgaben * (DATEDIFF (dd, CASE WHEN BeginnProjekt > c.Beginn_Periode THEN BeginnProjekt ELSE c.Beginn_Periode END, CASE WHEN EndeProjekt < c.Ende_Periode THEN EndeProjekt ELSE c.Ende_Periode END) + 1) / (DATEDIFF (dd, BeginnProjekt, EndeProjekt) + 1)  AS Ausgaben_Periode,
        DATEDIFF (dd, BeginnProjekt, EndeProjekt) + 1 AS Projektdauer,
        Ausgaben
    
    FROM #Calendar c
       RIGHT OUTER JOIN @Projekte p ON (c.Ende_Periode >= p.BeginnProjekt AND c.Beginn_Periode <=p.EndeProjekt) 


    Freitag, 13. Dezember 2019 20:53
  • Hallo Thorsten,

    vermutlich hast Du andere Spracheinstellungen.

    Ergänze bitte den Code als erste Zeile:

    SET dateformat dmy;

    Schönen Tag.

    Dienstag, 17. Dezember 2019 08:39

Alle Antworten

  • Hallo Thorsten,

    gibt es immer nur Start, Ende und Umsatz? Soll also die Berechnung immer mit einem zusammenhängendem Zeitraum erfolgen?

    Falls nicht, poste bitte mal deine Tabellenstruktur und Beispieldaten, die exakt zu deinem gewünschten Ergebnis passen.


    Gruß, Stefan
    Microsoft MVP - Visual Developer ASP/ASP.NET (2001-2018)
    https://www.asp-solutions.de/ - IT Beratung, Softwareentwicklung, Remotesupport

    Donnerstag, 12. Dezember 2019 13:58
    Moderator
  • Die einfachste Variante ist, die Anzahl Tage jeder Periode zu berechnen:
    https://docs.microsoft.com/de-de/sql/t-sql/functions/datediff-transact-sql?view=sql-server-ver15
    Dann kannst du den Tageswert ausrechnen und mit den jeweiligen Tagen je Monat wieder ausmultiplizieren.

    Sind allerdings Lücken in den Einzelperioden, so ist die Summe vorher zu berechnen:

    sum(datediff(day, FromDate, ToDate)) over()

    Möchtest du halbe/Ganze Monate geht das am Besten über Wochen:

    ceil((Datediff(...) / 7)

    Möglichkeiten gibt es da mehrere.

    Donnerstag, 12. Dezember 2019 14:25
  • Hallo Thorsten,

    hier ein Beispiel für eine tageweise Umrechnung der Ausgaben auf Monate.

    Schönen Abend.

    IF OBJECT_ID(N'tempdb..#Calendar') IS NOT NULL
        DROP TABLE #Calendar;
    
    DECLARE @DateMax DATETIME, @DateMin DATETIME;
    DECLARE @Periode VARCHAR(MAX);
    DECLARE @Zeitachse CHAR(1);
    
    
    SET @DateMin = '01.01.2019';
    SET @DateMax = '31.12.2020';
    SET @Zeitachse = 'm';  /* Schalter: Y = Jahr; Q = Quartal; M = Monat */
    
    DECLARE @Projekte TABLE ( BeginnProjekt DATETIME, EndeProjekt DATETIME, Ausgaben MONEY );
    
    INSERT INTO @Projekte VALUES 
    	( '16.04.2019', '31.08.2019', 1000 );
    
    CREATE TABLE #Calendar(
       Jahr int NULL,
       Quartal int NULL,
       Monat CHAR (2) NULL,
       Beginn_Periode DATETIME,
       Ende_Periode DATETIME,
       Periode varchar(10) NULL,
       Periodentage INT);
    
    WITH Calendar_func AS 
    (
       SELECT @DateMin AS DateDay
       UNION ALL
       SELECT DATEADD(DAY, 1, DateDay)
       FROM Calendar_func
       WHERE DateDay < @DateMax
    )
    
    INSERT INTO #Calendar (Jahr, Quartal, Monat, Beginn_Periode, Ende_Periode, Periode, Periodentage)
    SELECT DISTINCT
       DATEPART(yy, DateDay) AS [Jahr],
       
    	CASE WHEN @Zeitachse = 'Y' 
             THEN NULL
             ELSE CASE WHEN @Zeitachse = 'Q' 
    			       THEN DATEPART(qq, DateDay)
                       ELSE CASE WHEN @Zeitachse = 'M' 
    						     THEN DATEPART(qq, DateDay)
    						     ELSE NULL
    					     END 
                  END 
       END AS [Quartal],
        
    	CASE WHEN @Zeitachse = 'Y' 
             THEN NULL
             ELSE CASE WHEN @Zeitachse = 'Q' 
    			       THEN NULL
                       ELSE DATEPART(mm, DateDay)
                   END 
        END AS [Monat],
    
    
    	CASE WHEN @Zeitachse = 'Y' 
             THEN DATEADD(yy,DATEDIFF(yy,0,DateDay),0)
             ELSE CASE WHEN @Zeitachse = 'Q'
    				   THEN DATEADD(qq,DATEDIFF(qq,0,DateDay),0)
    				   ELSE CASE WHEN @Zeitachse = 'M'
    						     THEN DATEADD(mm,DATEDIFF(mm,0,DateDay),0) 
    						     ELSE NULL
    					    END
    			  END 
    	END AS [Beginn_Periode],
    	
    	
    
    	CASE WHEN @Zeitachse = 'Y' 
             THEN DATEADD(yy,DATEDIFF(yy,0,DateDay)+1,-1)
             ELSE CASE WHEN @Zeitachse = 'Q'
    				   THEN DATEADD(qq,DATEDIFF(qq,0,DateDay)+1,-1)
    				   ELSE CASE WHEN @Zeitachse = 'M'
    						     THEN DATEADD(mm,DATEDIFF(mm,0,DateDay)+1,-1)
    						     ELSE NULL
    					    END
    			  END 
    	END AS [Ende_Periode],
    
     
    	CASE WHEN @Zeitachse = 'Y' 
             THEN CAST (DATEPART(yy, DateDay) AS CHAR (4))
             ELSE CASE WHEN @Zeitachse = 'Q'
    				   THEN CAST (DATEPART(yy, DateDay) AS CHAR (4)) 
    						+ ' - Q' 
    						+ CAST (DATEPART(qq, DateDay) AS CHAR (1))
    				   ELSE CASE WHEN @Zeitachse = 'M'
    						     THEN CAST (DATEPART(yy, DateDay) AS CHAR (4)) 
    							 + ' - ' 
    						     + CASE 
    									WHEN DATEPART(mm, DateDay) < 10 
    									THEN RIGHT('0' + CONVERT( varchar, DATEPART(mm, DateDay) ), 2) 
    									ELSE CONVERT(VARCHAR, DATEPART(mm, DateDay))
    							 END 
    						     ELSE NULL
    					    END
    			  END 
    	END AS [Periode],
    
    	CASE WHEN @Zeitachse = 'Y' 
             THEN DATEDIFF (d, DATEADD(yy,DATEDIFF(yy,0,DateDay),0), DATEADD(yy,DATEDIFF(yy,0,DateDay)+1,-0))
             ELSE CASE WHEN @Zeitachse = 'Q'
    				   THEN DATEDIFF (d, DATEADD(qq,DATEDIFF(qq,0,DateDay),0), DATEADD(qq,DATEDIFF(qq,0,DateDay)+1,-0))
    				   ELSE CASE WHEN @Zeitachse = 'M'
    						     THEN DATEDIFF (d, DATEADD(mm,DATEDIFF(mm,0,DateDay),0), DATEADD(mm,DATEDIFF(mm,0,DateDay)+1,-0))
    					    END
    			  END 
    	END AS [Periodentage]
     
    FROM Calendar_func
    OPTION ( MAXRECURSION 0 )
    
    SELECT * FROM @Projekte
    
    SELECT Periode,
        CASE WHEN BeginnProjekt > c.Beginn_Periode THEN BeginnProjekt ELSE c.Beginn_Periode END as  von,
        CASE WHEN EndeProjekt < c.Ende_Periode THEN EndeProjekt ELSE c.Ende_Periode END AS bis,
        DATEDIFF (dd, CASE WHEN BeginnProjekt > c.Beginn_Periode THEN BeginnProjekt ELSE c.Beginn_Periode END, CASE WHEN EndeProjekt < c.Ende_Periode THEN EndeProjekt ELSE c.Ende_Periode END) + 1 AS Tage,
        Ausgaben * (DATEDIFF (dd, CASE WHEN BeginnProjekt > c.Beginn_Periode THEN BeginnProjekt ELSE c.Beginn_Periode END, CASE WHEN EndeProjekt < c.Ende_Periode THEN EndeProjekt ELSE c.Ende_Periode END) + 1) / (DATEDIFF (dd, BeginnProjekt, EndeProjekt) + 1)  AS Ausgaben_Periode,
        DATEDIFF (dd, BeginnProjekt, EndeProjekt) + 1 AS Projektdauer,
        Ausgaben
    
    FROM #Calendar c
       RIGHT OUTER JOIN @Projekte p ON (c.Ende_Periode >= p.BeginnProjekt AND c.Beginn_Periode <=p.EndeProjekt) 


    Freitag, 13. Dezember 2019 20:53
  • Vielen Dank Joerg_x für Deine ausfürliche Antwort.

    Ich habe gerade Deinen Code mal versucht und bekomme direkt 2 out-of-range Fehler in Zeile 28 und 33.

    "The conversion of a varchar data type to a datetime data type resulted in an out-of-range value."

    Ich versuche es mal zu lösen, muss aber erst einmal Deinen Code analysieren


    Thorsten Schröer Dipl-Inform (FH) - EDV, Beratung & Service - double-D-IT -Office- / Information system support Bismarckstraße 18a Germany-12169 Berlin Steglitz phone: &#43;49-(0)30-303286770 fax: &#43;49-(0)30-303286779


    Dienstag, 17. Dezember 2019 07:04
  • Hallo Thorsten,

    vermutlich hast Du andere Spracheinstellungen.

    Ergänze bitte den Code als erste Zeile:

    SET dateformat dmy;

    Schönen Tag.

    Dienstag, 17. Dezember 2019 08:39
  • Habe ich ausprobiert. Das ändert nichts

    Thorsten Schröer Dipl-Inform (FH) - EDV, Beratung & Service - double-D-IT -Office- / Information system support Bismarckstraße 18a Germany-12169 Berlin Steglitz phone: &#43;49-(0)30-303286770 fax: &#43;49-(0)30-303286779

    Dienstag, 17. Dezember 2019 11:20
  • Ich nehme es zurück. Es funktioniert.

    Vielen Dank


    Thorsten Schröer Dipl-Inform (FH) - EDV, Beratung & Service - double-D-IT -Office- / Information system support Bismarckstraße 18a Germany-12169 Berlin Steglitz phone: &#43;49-(0)30-303286770 fax: &#43;49-(0)30-303286779

    Dienstag, 17. Dezember 2019 11:27