Sql Server Computed Column Specification [duplicate] - sql-server

I have this function for a computed column :
CREATE FUNCTION [dbo].[GetAllocatedStartTime](#Year INT, #Week INT)
RETURNS DATETIME
WITH schemabinding
AS BEGIN
RETURN dateadd(week,#Week-(1),dateadd(day,(-1),dateadd(week,datediff(week,(0),CONVERT([varchar](4),#Year,(0))+'-01-01'),(1))))
END
GO
I added the WITH schemabinding in the hope it would make it deterministic so I can persist it. It should be as the two inputs [Week] and [Year] will always yield the same results.
The exact error is :
Computed column 'AllocatedTimeStart' in table 'Tmp_Bookings' cannot be persisted because the column is non-deterministic.
I am using this formula in the column :
([dbo].[GetAllocatedStartTime]([Year],[Week]))
And the column defs :
[Week] [int] NOT NULL,
[Year] [int] NOT NULL,
[AllocatedTimeStart] AS ([dbo].[GetAllocatedStartTime]([Year],[Week])),
Any ideas?
EDIT:
Changed line to :
RETURN dateadd(week,#Week-(1),dateadd(day,(-1),dateadd(week,datediff(week,(0),CONVERT(datetime,CONVERT([varchar](4),#Year,(0))+'0101',112)),(1))))
But now I get an error saying the formula for the column is invalid. Even though the function saves fine.
EDIT 2:
I've shown exactly what I am doing (or atleast I've tried). There is nothing extra really. As it says the previous function (original one) coupled with the formula ref [dbo].AllocatedStartDate(...) to it in the column worked, but was not persisting, it said it was non deterministic. So according to the suggestion I changed the FUNCTION, replacing the conversion part with the new code, so the function now looks like :
FUNCTION [dbo].[GetSTime](#Year INT, #Week INT)
RETURNS DATETIME
WITH schemabinding
AS BEGIN
RETURN dateadd(week,#Week-(1),dateadd(day,(-1),dateadd(week,datediff(week,(0),CONVERT(datetime,CONVERT([varchar](4),#Year,(0))+'0101',112)),(1))))
END
Then I tried the same formula as before in the computed field (([dbo].[GetAllocatedStartTime]([Year],[Week]))) ... and it rejects the formula, says its not valid... which is strange as the formula is the same, so it must be doing some sort of check of the changed function and finding that to be invalid, which is also strange because I did a plain SELECT dbo.GetAllocatedStartTime(2012,13) and it worked...
So yes I am confused, and I've never seen SqlFiddle never mind use it. But really there is nothing more than what I have just said.

CONVERT([varchar](4),#Year,(0))+'-01-01' is being passed to a DATEDIFF call, in a position where a date is expected, forcing an implicit conversion to occur.
From the rules for deterministic functions:
CAST
Deterministic unless used with datetime, smalldatetime, or sql_variant.
CONVERT
Deterministic unless one of these conditions exists:
...
Source or target type is datetime or smalldatetime, the other source or target type is a character string, and a nondeterministic style is specified. To be deterministic, the style parameter must be a constant. Additionally, styles less than or equal to 100 are nondeterministic, except for styles 20 and 21. Styles greater than 100 are deterministic, except for styles 106, 107, 109 and 113.
Well, you're calling neither, but you're relying on an implicit conversion, which I'd expect to act like CAST. Rather than rely on this, I'd switch to using CONVERT and give a deterministic style parameter.
So, I'd do: CONVERT(datetime,CONVERT([varchar](4),#Year,(0))+'0101',112) in its place. Having done so, the function itself becomes deterministic

Related

Why is my SQL function non-deterministic, when it shouldn't be?

According to MS docs DATEADD is a deterministic function hence my function below should be deterministic too:
CREATE FUNCTION [dbo].[Epoch2Date] (#i INT)
RETURNS DATETIME WITH SCHEMABINDING
BEGIN
RETURN DATEADD(SECOND,#i,'1970-01-01 00:00:00')
END
But when I check it with SELECT OBJECTPROPERTY(OBJECT_ID('[dbo].[Epoch2Date]'), 'IsDeterministic') it returns 0 (Non-deterministic).
Why it is non-deterministic?
How can I make my function deterministic?
There is a similar question but it uses non-deterministic function CAST which is not the case here.
DATEADD is. Implicitly converting a varchar to a datetime, however, is not. This is especially worse when the format you use is ambiguous for datetime (though at least the value would be the same).
You need to explicitly convert the value with a style:
DATEADD(SECOND,#i,CONVERT(datetime,'19700101',112))

SQL Server - DATE conversion from DATETIME is non-deterministic but only in user-defined function

Why is this type conversion rejected as non-deterministic for a PERSISTED computed column in return tables of user-defined functions (UDF) in SQL Server?
CREATE FUNCTION MyTimeIntervalFunction(#Param1 INT)
RETURNS #MyTimeInterval TABLE
(
StartUtc DATETIME NOT NULL PRIMARY KEY
,EndUtc DATETIME NOT NULL
,DateUtc AS CONVERT(DATE, StartUtc) PERSISTED
)
AS BEGIN
--do stuff
RETURN
END
Note this is not converting to or from a string representation, so I don't know why it doesn't work because globalization/region stuff should be irrelevant.
This works outside of a UDF (including stored procedures):
DECLARE #MyTimeInterval TABLE
(
StartUtc DATETIME NOT NULL PRIMARY KEY
,EndUtc DATETIME NOT NULL
,DateUtc AS CONVERT(DATE, StartUtc) PERSISTED
)
INSERT INTO #MyTimeInterval(StartUtc, EndUtc)
VALUES ('2018-01-01', '2018-01-02')
SELECT * FROM #MyTimeInterval
It seems that adding WITH SCHEMABINDING to the UDF definition shuts it up, but I don't understand why, because it looks like that only marks the function output as deterministic based on input parameters. And I have to do other non-deterministic stuff in my function, so it is not a candidate workaround.
Wonky string manipulation could also be a workaround, but is not preferable. Style 126 for ISO-8601 on CONVERT is still non-deterministic according to SQL Server. It seems the only option is to abandon use of persisted computed columns?
As mentioned at the beginning of this somewhat related answer, not specifying WITH SCHEMABINDING means SQL Server skips checks on such things as determinism and data access.
Since PERSISTED in a computer column requires the "computed column expression" to be deterministic and SQL Server skips any checks on whether or not it actually is deterministic, it won't be allowed. The same error would occur even if you had something as simple as i AS 1 PERSISTED.
(This is unrelated to whether everything in the function itself is deterministic.)
All that said, using PERSISTED in a TVF doesn't actually add anything to the function, as far as I know.

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

I have a stored procedure causing a date conversion error. I don't know what changed as it's been working fine for many months but our development group is downstream of a national DW. There are two variables used to retrieve date range #DatePlus1 & #DayOffset.
#DatePlus1 is used in a dozen or so CTEs that I won't post unless asked for but I commented out the date calculation and hard set the date as '2017-10-31 00:00:00' and ran the procedure. It ran without error but I don't understand why.
The original code set the #DatePlus1 as Date type which generated same error in subject.
SELECT #DayOffset = ISNULL(#DayOffset, 0)
DECLARE #DatePlus1 DATETIME2(0) --Was date but changed to datetime2 while troubleshooting
SELECT
#DatePlus1 = CONVERT(DATE, DATEADD(dd, 1, DATEADD(dd, #DayOffset, GETDATE())))
A static value would NOT be interpreted any differently than the same value passed in a variable. Therefore the problem is not what you think it is.
You are testing with a static variable that is in-range for a smalldatetime datatype. And your test is successful.
Therefore the reason your test fails when you use a variable is because the value of the variable is out-of-range for a smalldatetime, OR results in some selection of a value that is out-of-range.
The problem is not that you are using a variable. The problem is that you are giving that variable an invalid value in some part of the code that you are not showing in your question.
To troubleshoot this, you need to find all the places in your code where you populate a smalldatetime variable or column, and use PRINT or SELECT statements to see what value you are trying to insert into the smalldatetime.
So far I've done 2 and it fails on 1 RequestDate comparison but not another but RequestDate appears in different tables.
So another thing to look for is places in your code where the value of your variable could get changed programmatically. I would look right between the two queries where the comparison works, and the one where it fails.

attribute key was not found - MS SQL Server

I got the following message on MS SQL Server (I'm translating from German):
"Table 'VF_Fact', column ORGUNIT_CD, Value: 1185. The attribute is
ORGUNIT_CD. Row was dropped because attribute key was not found.
Attribute: ORGUNIT_CD in the dimension 'Organization' from database
'Dashboard', Cube 'Box Cube'..."
I checked the fact table 'VF_Fact' and the column ORGUNIT_CD - there I was able to found the value '1185'. The column ORGUNIT_CD is defined as follows in the view:
CAST( COALESCE( emp.ORGUNIT_CD, 99999999 ) AS char(8)) AS ORGUNIT_CD,
In addition the view retrieves the column from L_Employee_SAP TABLE, where ORGUNIT_CD is defined as follows:
[ORGUNIT_CD] [char](8) NOT NULL,
AND the value I find here is not '1185' but '00001185'.
The Fact table 'VF_Fact' is connected with the table L_ORG in which the column ORGUNIT_CD is defined as follows:
[ORGUNIT_CD] [char](8) NOT NULL,
This table hast the following value in the ORGUNIT_CD column: '00001185'.
Can anyone please explain, why am i getting this error, and how to remove it?
From this answer:
COALESCE:
Return Types
Returns the data type of expression with the highest data type precedence. If all expressions are nonnullable, the result is typed
as nonnullable.
(Emphasis added). int had a higher precedence than varchar, so
the return type of your COALESCE must be of type int. And obviously,
your varchar value cannot be so converted.
As another answer noted, ISNULL() behaves differently: rather than return the data type with the highest precedence, it returns the data type of the first value (thus, #Aleem's answer would solve your issue). A more detailed explanation can be found here under the section "Data Type of Expression."
In your specific case, I'd actually recommend that you encase the alternative string in single quotes, thus tipping SQL Server off to the fact that you intend this to be a character field. This means your expression would be one of the following:
CAST (ISNULL( emp.ORGUNIT_CD, '99999999' ) as char(8))
CAST (COALESCE( emp.ORGUNIT_CD, '99999999' ) AS char(8))
The advantage of using quotes in this situation? If you (or another developer) comes back to this down the line and tries to change it to COALESCE() or do any other type of modification, it's still going to work without breaking anything, because you told SQL Server what data type you want to use in the string itself. Depending on what else you're trying to do, you might even be able to remove the CAST() statement entirely.
COALESCE( emp.ORGUNIT_CD, '99999999' )
COALESCE function is dropping the leading zeroes. If you are checking for nulls you can do this and it will keep the zeroes.
CAST (ISNULL( emp.ORGUNIT_CD, 99999999 ) as char(8))

Cannot persist computed column - not deterministic

I have this function for a computed column :
CREATE FUNCTION [dbo].[GetAllocatedStartTime](#Year INT, #Week INT)
RETURNS DATETIME
WITH schemabinding
AS BEGIN
RETURN dateadd(week,#Week-(1),dateadd(day,(-1),dateadd(week,datediff(week,(0),CONVERT([varchar](4),#Year,(0))+'-01-01'),(1))))
END
GO
I added the WITH schemabinding in the hope it would make it deterministic so I can persist it. It should be as the two inputs [Week] and [Year] will always yield the same results.
The exact error is :
Computed column 'AllocatedTimeStart' in table 'Tmp_Bookings' cannot be persisted because the column is non-deterministic.
I am using this formula in the column :
([dbo].[GetAllocatedStartTime]([Year],[Week]))
And the column defs :
[Week] [int] NOT NULL,
[Year] [int] NOT NULL,
[AllocatedTimeStart] AS ([dbo].[GetAllocatedStartTime]([Year],[Week])),
Any ideas?
EDIT:
Changed line to :
RETURN dateadd(week,#Week-(1),dateadd(day,(-1),dateadd(week,datediff(week,(0),CONVERT(datetime,CONVERT([varchar](4),#Year,(0))+'0101',112)),(1))))
But now I get an error saying the formula for the column is invalid. Even though the function saves fine.
EDIT 2:
I've shown exactly what I am doing (or atleast I've tried). There is nothing extra really. As it says the previous function (original one) coupled with the formula ref [dbo].AllocatedStartDate(...) to it in the column worked, but was not persisting, it said it was non deterministic. So according to the suggestion I changed the FUNCTION, replacing the conversion part with the new code, so the function now looks like :
FUNCTION [dbo].[GetSTime](#Year INT, #Week INT)
RETURNS DATETIME
WITH schemabinding
AS BEGIN
RETURN dateadd(week,#Week-(1),dateadd(day,(-1),dateadd(week,datediff(week,(0),CONVERT(datetime,CONVERT([varchar](4),#Year,(0))+'0101',112)),(1))))
END
Then I tried the same formula as before in the computed field (([dbo].[GetAllocatedStartTime]([Year],[Week]))) ... and it rejects the formula, says its not valid... which is strange as the formula is the same, so it must be doing some sort of check of the changed function and finding that to be invalid, which is also strange because I did a plain SELECT dbo.GetAllocatedStartTime(2012,13) and it worked...
So yes I am confused, and I've never seen SqlFiddle never mind use it. But really there is nothing more than what I have just said.
CONVERT([varchar](4),#Year,(0))+'-01-01' is being passed to a DATEDIFF call, in a position where a date is expected, forcing an implicit conversion to occur.
From the rules for deterministic functions:
CAST
Deterministic unless used with datetime, smalldatetime, or sql_variant.
CONVERT
Deterministic unless one of these conditions exists:
...
Source or target type is datetime or smalldatetime, the other source or target type is a character string, and a nondeterministic style is specified. To be deterministic, the style parameter must be a constant. Additionally, styles less than or equal to 100 are nondeterministic, except for styles 20 and 21. Styles greater than 100 are deterministic, except for styles 106, 107, 109 and 113.
Well, you're calling neither, but you're relying on an implicit conversion, which I'd expect to act like CAST. Rather than rely on this, I'd switch to using CONVERT and give a deterministic style parameter.
So, I'd do: CONVERT(datetime,CONVERT([varchar](4),#Year,(0))+'0101',112) in its place. Having done so, the function itself becomes deterministic

Resources