TopAnswers Databases
or
dsungaro imported from SECC BY-SA 4.0
sql-server sql-server-2019
I have moved an SQL Server 2012 Database (8GB in size) to a newly setup SQL Server 2019 virtual machine with the same memory and CPU configuration and changed the compatibility level to SQL Server 2019. 

Everything in my application works fine except for one stored procedure that consists of one big SQL query with two parameters (and no fancy options). When this SP executed, it lets the memory of the SQL Server process go up to the specified max level and then returns an error:

> "There is insufficient memory to run this query"

When I execute the SQL query (inside the stored procedure) in a separate query window of SSMS, it executes in no-time and returns the expected 300 rows. Also, when I change the DB's compatibility level to "SQL Server 2017" and execute the stored procedure, everything is ok.

I first thought it might be a parameter sniffing issue, but none of the workarounds helped (e.g. `OPTION (RECOMPILE)`).

I have drilled down the problem to the call of a scalar valued function. Every time I call this function, the memory error occurs.

Here is the DDL of the function (sorry, partly in German):

	CREATE FUNCTION [dbo].[GetWtmTime] (
		@WorkTimeModelID uniqueidentifier,
		@Date DATETIME,
		@SequenceNo TINYINT)
	  RETURNS VARCHAR(5)
	AS
	BEGIN
		-- SET DATEFIRST 7; has to be executed before calling this function
		DECLARE @WtmTime VARCHAR(5)
		DECLARE @WtmWeeks INT
		DECLARE @WtmTakeHolidays BIT
		DECLARE @WtmMaxMemberCount TINYINT
		SELECT @WtmWeeks = AnzahlWochen
			 , @WtmTakeHolidays = ÜbernimmtFeiertage 
			 , @WtmMaxMemberCount = MaxAnzahlMitglieder
		FROM Arbeitszeitmodelle 
		WHERE ArbeitszeitmodellID = @WorkTimeModelID;
		IF @WtmWeeks = 1
		BEGIN
			IF (dbo.IstFeiertag(@Date, 0) = 1     -- Holiday
				AND @WtmMaxMemberCount = 1)
			BEGIN
				IF @WtmTakeHolidays = 0
				BEGIN 
					IF @Date >= '20130901'
						SET @WtmTime = 'KD'
					ELSE
						SET @WtmTime = 'ZA';
				END ELSE
				BEGIN
					IF EXISTS ( SELECT *
								FROM AzmWochen
								WHERE ArbeitszeitmodellID = @WorkTimeModelID
									AND Folgenummer = @SequenceNo
									AND AzmZeitMo IN ('KD','T')
									AND AzmZeitDi IN ('KD','T')
									AND AzmZeitMi IN ('KD','T')
									AND AzmZeitDo IN ('KD','T')
									AND AzmZeitFr IN ('KD','T')
									AND AzmZeitSa IN ('KD','T')
									AND AzmZeitSo IN ('KD','T') )
						SET @WtmTime = 'T';
					ELSE
						SET @WtmTime = 'G';
				END
			END ELSE IF DATEPART(dw, @Date) = 1				-- Sunday
				SELECT @WtmTime = AzmZeitSo FROM AzmWochen 
					WHERE ArbeitszeitmodellID = @WorkTimeModelID
						AND Folgenummer = @SequenceNo;
			ELSE IF DATEPART(dw, @Date) = 2				-- Monday
				SELECT @WtmTime = AzmZeitMo FROM AzmWochen 
					WHERE ArbeitszeitmodellID = @WorkTimeModelID
						AND Folgenummer = @SequenceNo;
			ELSE IF DATEPART(dw, @Date) = 3				-- Tuesday
				SELECT @WtmTime = AzmZeitDi FROM AzmWochen 
					WHERE ArbeitszeitmodellID = @WorkTimeModelID
						AND Folgenummer = @SequenceNo;
			ELSE IF DATEPART(dw, @Date) = 4				-- Wednesday
				SELECT @WtmTime = AzmZeitMi FROM AzmWochen 
					WHERE ArbeitszeitmodellID = @WorkTimeModelID
						AND Folgenummer = @SequenceNo;
			ELSE IF DATEPART(dw, @Date) = 5				-- Thursday
				SELECT @WtmTime = AzmZeitDo FROM AzmWochen 
					WHERE ArbeitszeitmodellID = @WorkTimeModelID
						AND Folgenummer = @SequenceNo;
			ELSE IF DATEPART(dw, @Date) = 6				-- Friday
				SELECT @WtmTime = AzmZeitFr FROM AzmWochen 
					WHERE ArbeitszeitmodellID = @WorkTimeModelID
						AND Folgenummer = @SequenceNo;
			ELSE 											-- Saturday
				SELECT @WtmTime = AzmZeitSa FROM AzmWochen 
					WHERE ArbeitszeitmodellID = @WorkTimeModelID
						AND Folgenummer = @SequenceNo;
		END ELSE
		BEGIN
			DECLARE @NUMWEEKS INT
			SELECT @NUMWEEKS = DATEDIFF(week, CONVERT(CHAR(10), '01.01.2000', 104), @Date)
			IF DATEPART(dw, @Date) = 1 
				SET @NUMWEEKS = @NUMWEEKS - 1;
			DECLARE @WEEKNUMBER INT 
			IF @NUMWEEKS % 2 = 0
				SET @WEEKNUMBER = 1
			ELSE
				SET @WEEKNUMBER = 2;
			IF DATEPART(dw, @Date) = 1				-- Sunday
				SELECT @WtmTime = AzmZeitSo FROM AzmWochen 
					WHERE Folgenummer = @SequenceNo AND Wochennummer = @WEEKNUMBER 
					  AND ArbeitszeitmodellID = @WorkTimeModelID
			ELSE IF DATEPART(dw, @Date) = 2		-- Monday
				SELECT @WtmTime = AzmZeitMo FROM AzmWochen 
					WHERE Folgenummer = @SequenceNo AND Wochennummer = @WEEKNUMBER 
					  AND ArbeitszeitmodellID = @WorkTimeModelID
			ELSE IF DATEPART(dw, @Date) = 3		-- Tuedsay
				SELECT @WtmTime = AzmZeitDi FROM AzmWochen 
					WHERE Folgenummer = @SequenceNo AND Wochennummer = @WEEKNUMBER 
					  AND ArbeitszeitmodellID = @WorkTimeModelID
			ELSE IF DATEPART(dw, @Date) = 4		-- Wednesday
				SELECT @WtmTime = AzmZeitMi FROM AzmWochen 
					WHERE Folgenummer = @SequenceNo AND Wochennummer = @WEEKNUMBER 
					  AND ArbeitszeitmodellID = @WorkTimeModelID
			ELSE IF DATEPART(dw, @Date) = 5		-- Thursday
				SELECT @WtmTime = AzmZeitDo FROM AzmWochen 
					WHERE Folgenummer = @SequenceNo AND Wochennummer = @WEEKNUMBER 
					  AND ArbeitszeitmodellID = @WorkTimeModelID
			ELSE IF DATEPART(dw, @Date) = 6		-- Friday
				SELECT @WtmTime = AzmZeitFr FROM AzmWochen 
					WHERE Folgenummer = @SequenceNo AND Wochennummer = @WEEKNUMBER 
					  AND ArbeitszeitmodellID = @WorkTimeModelID
			ELSE									-- Saturday
				SELECT @WtmTime = AzmZeitSa FROM AzmWochen 
					WHERE Folgenummer = @SequenceNo AND Wochennummer = @WEEKNUMBER 
					  AND ArbeitszeitmodellID = @WorkTimeModelID
		END
		IF @Date >= '20130901' AND @WtmTime = 'ZA'
			SET @WtmTime = 'KD';

		RETURN @WtmTime;
	END


	CREATE FUNCTION [dbo].[IstFeiertag] (
		@Datum DATETIME,
		@IstEvangelisch BIT)
	  RETURNS INT
	AS
	BEGIN
		DECLARE @I INT
		DECLARE @Y INT
		DECLARE @A INT
		DECLARE @B INT
		SET @I = DATEPART(year, @Datum) / 100 - DATEPART(year, @Datum) / 400 + 4;
		SET @Y = @I - DATEPART(year, @Datum) / 300 + 11;
		SET @A = (((DATEPART(year, @Datum) % 19) * 19) + @Y) % 30;
		SET @B = (((DATEPART(year, @Datum) % 4) * 2 + 4 * DATEPART(year, @Datum) + 6 * @A + @I) % 7) + @A - 9;
		DECLARE @OstTag INT
		DECLARE @OstMon INT
		IF @B < 1
		BEGIN
			SET @OstTag = 31 + @B
			SET @OstMon = 3
		END ELSE
		BEGIN
			IF ((@B = 26) OR ((@A = 28) AND (@B = 25) AND ((11 * (@Y + 1) % 30) < 19)))
			BEGIN
				SET @B = @B - 7;
			END
			SET @OstTag = @B
			SET @OstMon = 4
		END

		DECLARE @Ostersonntag DATETIME
		SET @Ostersonntag = dbo.CreateDate(DATEPART(year, @Datum), @OstMon, @OstTag)

		IF @Datum >= @Ostersonntag
		BEGIN
			DECLARE @TAGE INT
			SET @TAGE = DATEDIFF(day, @Ostersonntag, @Datum)
			IF @TAGE = 0 OR @TAGE = 1 OR @TAGE = 39 OR @TAGE = 50 OR @TAGE = 60
			BEGIN
				RETURN 1
			END
		END
		DECLARE @TEMP INT 
		SET @TEMP = DATEPART(month, @Datum) * 100 + DATEPART(day, @Datum)
		IF @TEMP = 101 OR @TEMP = 106 OR @TEMP = 501 OR @TEMP = 815 OR @TEMP = 1026
			 OR @TEMP = 1101 OR @TEMP = 1208 OR @TEMP = 1225 OR @TEMP = 1226
		BEGIN
			RETURN 1
		END 
		RETURN 0
	END
	GO


	CREATE FUNCTION [dbo].[CreateDate] (
		@Year int, 
		@Month int, 
		@Day int)
	  RETURNS DATETIME
	AS
	BEGIN
		declare @d datetime;
		set @d = dateadd(year,(@Year - 1753),'1/1/1753');
		set @d = dateadd(month,@Month - 1,@d);
		return dateadd(day,@Day - 1,@d)
	END
	GO

These are the table definitions (in German):


    CREATE TABLE [dbo].[Arbeitszeitmodelle]
    (
    	[ArbeitszeitmodellID] uniqueidentifier ROWGUIDCOL NOT NULL 
    		CONSTRAINT [DF_Arbeitszeitmodelle_ArbeitszeitmodellID] DEFAULT (newid()) 
    		CONSTRAINT [PK_Arbeitszeitmodelle_ArbeitszeitmodellID] PRIMARY KEY CLUSTERED,
    	[Name] nvarchar(25) NOT NULL,	
    	[MaxAnzahlMitglieder]  tinyint NOT NULL 
		CONSTRAINT [CK_Arbeitszeitmodelle_MaxAnzahlMitglieder] CHECK (([MaxAnzahlMitglieder] > 0) AND ([MaxAnzahlMitglieder] < 10)), 
    	[AnzahlWochen] tinyint NOT NULL
		CONSTRAINT [CK_Arbeitszeitmodelle_AnzahlWochen] CHECK (([AnzahlWochen] > 0) AND ([AnzahlWochen] < 5)),
    	[ÜbernimmtFeiertage] bit 
    );

    CREATE TABLE [dbo].[AzmWochen]
    (
    	[AzmWochenID] uniqueidentifier ROWGUIDCOL NOT NULL 
    		CONSTRAINT [DF_AzmWochen_AzmWochenID] DEFAULT (newid()) 
    		CONSTRAINT [PK_AzmWochen_AzmWochenID] PRIMARY KEY CLUSTERED,
    	[Folgenummer] tinyint NOT NULL
    		CONSTRAINT [CK_AzmWochen_Folgenummer] CHECK (([Folgenummer] > 0) AND ([Folgenummer] < 10)),		
        	[Wochennummer] tinyint NOT NULL
    		CONSTRAINT [CK_AzmWochen_Wochennummer] CHECK (([Wochennummer] > 0) AND ([Wochennummer] < 3)),
        	[ArbeitszeitmodellID] uniqueidentifier NOT NULL 
    		CONSTRAINT [FK_AzmWochen_ArbeitszeitmodellID] FOREIGN KEY ([ArbeitszeitmodellID]) REFERENCES [dbo].[Arbeitszeitmodelle] ([ArbeitszeitmodellID]) ON UPDATE CASCADE ON DELETE CASCADE,	
    	[AzmZeitMo] varchar(5) NOT NULL,
    	[AzmZeitDi] varchar(5) NOT NULL,
    	[AzmZeitMi] varchar(5) NOT NULL,
    	[AzmZeitDo] varchar(5) NOT NULL,
    	[AzmZeitFr] varchar(5) NOT NULL,
    	[AzmZeitSa] varchar(5) NOT NULL,
    	[AzmZeitSo] varchar(5) NOT NULL
    );
    ALTER TABLE AzmWochen ADD CONSTRAINT [UQ_AzmWochen_FolgeWochen] UNIQUE ([ArbeitszeitmodellID] ASC, [Folgenummer] ASC, [Wochennummer] ASC);

I tried the hints:

* `OPTION(USE HINT('FORCE_LEGACY_CARDINALITY_ESTIMATION'))`
* `OPTION(USE HINT('QUERY_OPTIMIZER_COMPATIBILITY_LEVEL_140'))`

... but they did not prevent the error.

I inserted the two tables, test data, and the functions (`GetWtmTime` depends on two other scalar functions) into an empty test database and was able to execute the function twice. Then I again got the memory error.
Top Answer
CC BY-SA 4.0Paul White
### Cause

SQL Server is trying to [inline the function][1] but failing due to the complexity.

Using so much memory while doing so is unexpected and almost certainly a bug.

A definition for the nested function `dbo.IstFeiertag` would be needed for a full repro.

### Workaround

Add `WITH INLINE = OFF` to the function(s) definition. Once this issue is resolved, you should be able to remove that option to reap the performance benefits of function inlining.

### Reporting and Status

You should [report][2] this issue to Microsoft. If you have a support agreement, go that route. Alternatively, post a bug report on [User Voice][3], and email the Intelligent Query Processing team at intelligentqp@microsoft.com.

[Joe Sack][4] (Principal Program Manager, Microsoft SQL Server product team) commented:

> Thank you for reporting.  Paul White gave me a heads-up and I've reported to our team for investigation.

---

### Resolution

A [fix for this issue][5] was released as part of [Cumulative Update 2 for SQL Server 2019][6].


  [1]: https://docs.microsoft.com/en-us/sql/relational-databases/user-defined-functions/scalar-udf-inlining
  [2]: https://docs.microsoft.com/en-us/sql/sql-server/sql-server-get-help
  [3]: https://aka.ms/sqlfeedback
  [4]: https://dba.stackexchange.com/users/110891/joe-sack
  [5]: https://support.microsoft.com/en-us/help/4538581/fix-scalar-udf-inlining-issues-in-sql-server-2019
  [6]: https://support.microsoft.com/en-us/help/4536075/cumulative-update-2-for-sql-server-2019

This is a dedicated room for discussion about this question.

Once logged in you can direct comments to the question poster (or any answer poster) here.