none
While Schleife Verständnisfrage RRS feed

  • Frage

  • Hallo an alle,

    ich beschäftige mich gerade ein wenig mit Programmierung im TSQL Bereich und will diverse Möglichkeiten austesten.

    Aktuell habe ich folgendes Skript:

    declare @para_year_start int SET @para_year_start = 2016
    declare @para_month_start int SET @para_month_start = 2
    declare @para_year_stop int SET @para_year_stop = 2017
    declare @para_month_stop int SET @para_month_stop = 8
    
    WHILE @para_year_start <= @para_year_stop
    	BEGIN	
    		while @para_month_start <= @para_month_stop
    			BEGIN
    			print STR(@para_year_start) +' '+STR(@para_month_start);
    			
    			SET @para_month_start = @para_month_start + 1;
    				
    				WHILE @para_month_start = @para_month_stop 
    				BEGIN
    					SET @para_year_start = @para_year_start + 1; 
    					SET @para_month_start=1;
    					print'treffer';
    					
    				END
    	END
    END


    Nun ist mein Problem das ich den geänderten Wert durch

    SET@para_year_start =@para_year_start +1;

    SET@para_month_start=1;

    aus der zweiten While-Schleife an die erste wieder übergeben muss. Aber wie war das gleich noch mal? Da gab es doch irgendwas mit return oder? Ohne dieses Vorgehen bekommt die erste While-Schleife nie die Änderung mit und läuft ewig.

    Leider habe ich noch nichts gefunden beim stöbern im Netz. Oder bin ich auf dem Holzweg?

    Mittwoch, 4. Januar 2017 08:27

Antworten

  • Hallo Toot,

    ein RETURN kehrt zur aufrufen Prozedur zurück, was in T-SQL auch ein Trigger, Funktion sein kann. Bei einer WHILE Schleife gäbe es nur BREAK (verlässt die Schleife) oder CONTINUE (kehrt an den Schleifenanfang zurück).

    Bei Deinem Konstrukt vermute ich aber, Du möchtest von Februar 2016 bis August 2017 Jahr und Monate erzeugen. Dafür brauchte man jedoch das dritte WHILE (was wenn überhaupt eher ein IF sein müsste) nicht:

    DECLARE @para_year_start int = 2016,
    	@para_month_start int = 2,
    	@para_year_stop int = 2017,
    	@para_month_stop int = 8;
    
    WHILE (@para_year_start <= @para_year_stop)
    BEGIN
    	-- wenn nicht im Ende Jahr bis Monat 12, sonst bis zum Stop Monat
    	WHILE (@para_year_start < @para_year_stop AND @para_month_start <= 12) OR
    		(@para_month_start <= @para_month_stop)
    	BEGIN
    		PRINT STR(@para_year_start) + ' ' + STR(@para_month_start);
    			
    		SET @para_month_start += 1;
    	END;
    	SET @para_year_start += 1;
    	SET @para_month_start = 1;
    END;

    Wie zu sehen, reicht eine (etwas komplexere) Bedingung beim zweiten WHILE aus, um das Ziel zu erreichen. Man kann jedoch noch weiter gehen und es mit einer WHILE Schleife "bewältigen":

    WHILE (@para_year_start < @para_year_stop OR (@para_year_start = @para_year_stop AND @para_month_start <= @para_month_stop))
    BEGIN
    	PRINT STR(@para_year_start) + ' ' + STR(@para_month_start);
    			
    	IF (@para_month_start = 12)
    	BEGIN
    		SET @para_year_start += 1
    		SET @para_month_start = 1;
    	END ELSE BEGIN
    		SET @para_month_start += 1;
    	END;
    END;

    Nebenbei habe ich einige Verkürzungen vorgenommen, wie den Einsatz von "+=" sowie die direkte Zuweisung beim DECLARE, was alle neueren SQL Server Versionen (ab 2008) beherrschen.

    Die weitere Übung, das Ganze in einer SQL Anweisung vorzunehmen, überlasse ich jetzt aber Dir ;)

    Gruß Elmar

    Mittwoch, 4. Januar 2017 10:22
  • Hi,

    yop, das hilft :)

    DECLARE @para_year_start  INT = 2016
    DECLARE @para_month_start INT = 11
    DECLARE @para_year_stop   INT = 2020
    DECLARE @para_month_stop  INT = 8;
    
    WITH Monate AS
    (
        SELECT 1 AS Monat
        UNION ALL
        SELECT Monat + 1
        FROM   Monate AS M
        WHERE  M.Monat <= 11
    ), Jahre AS
    (
        SELECT @para_year_start AS Jahr
        UNION ALL
        SELECT Jahr + 1
        FROM   Jahre AS J
        WHERE  J.Jahr <= @para_year_stop
    )
    SELECT   *
    FROM     Jahre j
             CROSS JOIN Monate m
    WHERE    j.Jahr * 100 + m.Monat BETWEEN @para_year_start * 100 + @para_month_start AND @para_year_stop * 100 + @para_month_stop
    ORDER BY j.Jahr,
             m.Monat

    Probier's mal damit.


    Gruß, Stefan
    Microsoft MVP - Visual Developer ASP/ASP.NET
    http://www.asp-solutions.de/ - Consulting, Development
    http://www.aspnetzone.de/ - ASP.NET Zone, die ASP.NET Community

    Mittwoch, 4. Januar 2017 12:20
    Moderator
  • Hi,

    als Funktion evtl. sinnvoller:

    CREATE FUNCTION [dbo].[fct_Monate]
    (
        @YearStart  INT,
        @YearStop   INT,
        @MonthStart INT,
        @MonthStop  INT
    )
    RETURNS TABLE 
    AS
    RETURN 
    (
        WITH Monate AS
        (
            SELECT 1 AS Monat
            UNION ALL
            SELECT Monat + 1
            FROM   Monate AS M
            WHERE  M.Monat <= 11
        ), Jahre AS
        (
            SELECT @YearStart AS Jahr
            UNION ALL
            SELECT Jahr + 1
            FROM   Jahre AS J
            WHERE  J.Jahr <= @YearStop
        )
        SELECT   *
        FROM     Jahre j
                 CROSS JOIN Monate m
        WHERE    j.Jahr * 100 + m.Monat BETWEEN @YearStart * 100 + @MonthStart AND @YearStop * 100 + @MonthStop
    )
    

    Der Aufruf sähe dann bspw. so aus:

    SELECT   *
    FROM     dbo.[fct_Monate]( 2016, 2022, 8, 2 )
    ORDER BY Jahr,
             Monat


    Gruß, Stefan
    Microsoft MVP - Visual Developer ASP/ASP.NET
    http://www.asp-solutions.de/ - Consulting, Development
    http://www.aspnetzone.de/ - ASP.NET Zone, die ASP.NET Community

    Mittwoch, 4. Januar 2017 12:25
    Moderator

Alle Antworten

  • Hallo Toot,

    ein RETURN kehrt zur aufrufen Prozedur zurück, was in T-SQL auch ein Trigger, Funktion sein kann. Bei einer WHILE Schleife gäbe es nur BREAK (verlässt die Schleife) oder CONTINUE (kehrt an den Schleifenanfang zurück).

    Bei Deinem Konstrukt vermute ich aber, Du möchtest von Februar 2016 bis August 2017 Jahr und Monate erzeugen. Dafür brauchte man jedoch das dritte WHILE (was wenn überhaupt eher ein IF sein müsste) nicht:

    DECLARE @para_year_start int = 2016,
    	@para_month_start int = 2,
    	@para_year_stop int = 2017,
    	@para_month_stop int = 8;
    
    WHILE (@para_year_start <= @para_year_stop)
    BEGIN
    	-- wenn nicht im Ende Jahr bis Monat 12, sonst bis zum Stop Monat
    	WHILE (@para_year_start < @para_year_stop AND @para_month_start <= 12) OR
    		(@para_month_start <= @para_month_stop)
    	BEGIN
    		PRINT STR(@para_year_start) + ' ' + STR(@para_month_start);
    			
    		SET @para_month_start += 1;
    	END;
    	SET @para_year_start += 1;
    	SET @para_month_start = 1;
    END;

    Wie zu sehen, reicht eine (etwas komplexere) Bedingung beim zweiten WHILE aus, um das Ziel zu erreichen. Man kann jedoch noch weiter gehen und es mit einer WHILE Schleife "bewältigen":

    WHILE (@para_year_start < @para_year_stop OR (@para_year_start = @para_year_stop AND @para_month_start <= @para_month_stop))
    BEGIN
    	PRINT STR(@para_year_start) + ' ' + STR(@para_month_start);
    			
    	IF (@para_month_start = 12)
    	BEGIN
    		SET @para_year_start += 1
    		SET @para_month_start = 1;
    	END ELSE BEGIN
    		SET @para_month_start += 1;
    	END;
    END;

    Nebenbei habe ich einige Verkürzungen vorgenommen, wie den Einsatz von "+=" sowie die direkte Zuweisung beim DECLARE, was alle neueren SQL Server Versionen (ab 2008) beherrschen.

    Die weitere Übung, das Ganze in einer SQL Anweisung vorzunehmen, überlasse ich jetzt aber Dir ;)

    Gruß Elmar

    Mittwoch, 4. Januar 2017 10:22
  • Danke Elmar!

    ja manchmal denkt man einfach viel zu kompliziert.

    Schau dir doch mal bitte das an und sag mir mal was du davon hälst?

    declare @para_year_start int SET @para_year_start = 2016
    declare @para_month_start int SET @para_month_start = 11
    declare @para_year_stop int SET @para_year_stop = 2020
    declare @para_month_stop int SET @para_month_stop = 8
    
    
    --print Cast(str(@para_year_start)+RIGHT('00' + CONVERT(NVARCHAR(2), @para_month_start), 2) as int);
    
    
    
    while	Cast(str(@para_year_start)+RIGHT('00' + CONVERT(NVARCHAR(2), @para_month_start), 2) as int) 
    				<= 
    		Cast(str(@para_year_stop)+RIGHT('00' + CONVERT(NVARCHAR(2), @para_month_stop), 2) as int)
    
    		BEGIN 
    			
    			IF @para_month_start <12 
    				BEGIN
    				--PRINT Cast(str(@para_year_start)+RIGHT('00' + CONVERT(NVARCHAR(2), @para_month_start), 2) as int);
    				PRINT '|'+str(@para_year_start)+'|'+ str(@para_month_start)+'|'			
    				SET @para_month_start=@para_month_start+1
    				Continue
    				END
    			ELSE
    				BEGIN
    				--PRINT Cast(str(@para_year_start)+RIGHT('00' + CONVERT(NVARCHAR(2), @para_month_start), 2) as int);
    				PRINT '|'+str(@para_year_start)+'|'+ str(@para_month_start)+'|'	
    				
    				SET @para_month_start = 01
    				SET @para_year_start = @para_year_start+1
    				END
    
    		END
     

    Ich habe es jetzt nicht weiter vereinfacht aber das würde ich danke deinen Tipps noch tuen. Aber wie bekomme ich jetzt das Ergebnis in einer "Tabellenform" angezeigt. Also Spalte Jahr und Spalte Monat und darunter die Werte?

    Um das besser zu verdeutlichen habe ich die Print-Ausgaben angepasst damit man sieht was ich meine.

    Mittwoch, 4. Januar 2017 10:28
  • Hi,

    ich glaube, Du gehst komplett verkehrt an die Sache ran. Was genau soll denn bitte bei deiner "Abfrage" rauskommen?

    Wenn Du eine Datensatzliste als Ergebnis willst, solltest Du auch irgendwo etwas selektieren. Das tust Du aber nicht.


    Gruß, Stefan
    Microsoft MVP - Visual Developer ASP/ASP.NET
    http://www.asp-solutions.de/ - Consulting, Development
    http://www.aspnetzone.de/ - ASP.NET Zone, die ASP.NET Community

    Mittwoch, 4. Januar 2017 10:42
    Moderator
  • Hallo Stefan,

    nein ich möchte nichts selektieren. Da ich das auch nicht benötige. Am ende meiner mühen soll eine Prozedur herauskommen. Wenn ich diese 4 Parameter übergebe und eben besagte Tabelle herauskommt. Ich benötige dies für diverse Reports.

    Mittwoch, 4. Januar 2017 11:07
  • Hi,

    wenn eine Tabelle rauskommen soll, willst Du etwas selektieren. In welcher Form auch immer.

    Was genau also soll bei der Prozedur herauskommen?


    Gruß, Stefan
    Microsoft MVP - Visual Developer ASP/ASP.NET
    http://www.asp-solutions.de/ - Consulting, Development
    http://www.aspnetzone.de/ - ASP.NET Zone, die ASP.NET Community

    Mittwoch, 4. Januar 2017 11:21
    Moderator
  • Hallo Stefan,

    naja eine Tabelle soll rauskommen in der Form

    |      2016|        11|

    |      2016|        12|

    |      2017|         1|

    |      2017|         2|

    |      2017|         3|

    |      2017|         4|

    |      2017|         5|

    |      2017|         6|

    |      2017|         7|

    |      2017|         8|

    |      2017|         9|

    |      2017|        10|

    |      2017|        11|

    |      2017|        12|

    |      2018|         1|

    Hilft dir das weiter?

    Mittwoch, 4. Januar 2017 12:06
  • Hi,

    yop, das hilft :)

    DECLARE @para_year_start  INT = 2016
    DECLARE @para_month_start INT = 11
    DECLARE @para_year_stop   INT = 2020
    DECLARE @para_month_stop  INT = 8;
    
    WITH Monate AS
    (
        SELECT 1 AS Monat
        UNION ALL
        SELECT Monat + 1
        FROM   Monate AS M
        WHERE  M.Monat <= 11
    ), Jahre AS
    (
        SELECT @para_year_start AS Jahr
        UNION ALL
        SELECT Jahr + 1
        FROM   Jahre AS J
        WHERE  J.Jahr <= @para_year_stop
    )
    SELECT   *
    FROM     Jahre j
             CROSS JOIN Monate m
    WHERE    j.Jahr * 100 + m.Monat BETWEEN @para_year_start * 100 + @para_month_start AND @para_year_stop * 100 + @para_month_stop
    ORDER BY j.Jahr,
             m.Monat

    Probier's mal damit.


    Gruß, Stefan
    Microsoft MVP - Visual Developer ASP/ASP.NET
    http://www.asp-solutions.de/ - Consulting, Development
    http://www.aspnetzone.de/ - ASP.NET Zone, die ASP.NET Community

    Mittwoch, 4. Januar 2017 12:20
    Moderator
  • Hi,

    als Funktion evtl. sinnvoller:

    CREATE FUNCTION [dbo].[fct_Monate]
    (
        @YearStart  INT,
        @YearStop   INT,
        @MonthStart INT,
        @MonthStop  INT
    )
    RETURNS TABLE 
    AS
    RETURN 
    (
        WITH Monate AS
        (
            SELECT 1 AS Monat
            UNION ALL
            SELECT Monat + 1
            FROM   Monate AS M
            WHERE  M.Monat <= 11
        ), Jahre AS
        (
            SELECT @YearStart AS Jahr
            UNION ALL
            SELECT Jahr + 1
            FROM   Jahre AS J
            WHERE  J.Jahr <= @YearStop
        )
        SELECT   *
        FROM     Jahre j
                 CROSS JOIN Monate m
        WHERE    j.Jahr * 100 + m.Monat BETWEEN @YearStart * 100 + @MonthStart AND @YearStop * 100 + @MonthStop
    )
    

    Der Aufruf sähe dann bspw. so aus:

    SELECT   *
    FROM     dbo.[fct_Monate]( 2016, 2022, 8, 2 )
    ORDER BY Jahr,
             Monat


    Gruß, Stefan
    Microsoft MVP - Visual Developer ASP/ASP.NET
    http://www.asp-solutions.de/ - Consulting, Development
    http://www.aspnetzone.de/ - ASP.NET Zone, die ASP.NET Community

    Mittwoch, 4. Januar 2017 12:25
    Moderator