I have this user function, which is always flagged as non-deterministic, although the value will always be the same as long as the input parameter is the same. Everything I've read suggests this should be deterministic.
Can anybody spot why?
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER FUNCTION [dbo].[udfGetCriteriaScore]
(
@InputString varchar(max)
)
RETURNS int
WITH SCHEMABINDING
AS
BEGIN
DECLARE @returnScore int;
DECLARE @IdentifierChar NCHAR(1)= '"'
DECLARE @overallScore int
DECLARE @FirstID int
DECLARE @SecondID int
DECLARE @TargetString varchar(MAX)
DECLARE @startDateCriteria varchar(MAX);
DECLARE @endDateCriteria varchar(MAX);
declare @scoringTable table (
Criterion varchar(max),
CriteriaScore int,
Occurances int,
SumTotalScore int
)
DECLARE @TotalCriterions int = (SELECT LEN(@InputString) - LEN(REPLACE(@InputString, '@', '')))
declare @COUNT int = 0
declare @Length int = 0
WHILE(@COUNT) < @TotalCriterions
BEGIN
Set @FirstID = CHARINDEX(@IdentifierChar, @InputString, @Length)
Set @SecondID = CHARINDEX(@IdentifierChar, @InputString, @FirstID + 1)
Set @Length = @SecondID - @FirstID
Set @TargetString = SUBSTRING(@InputString, @FirstID + 1, @Length - 1)
SET @COUNT = @COUNT + 1
Set @Length = @SecondID + 1
DECLARE @criteriaScore int
DECLARE @criteriaCount int
DECLARE @Criterion varchar(max)
SET @Criterion = SUBSTRING(@TargetString, 0, CHARINDEX(':', @TargetString))
-- Calculate date range score
IF (LOWER(@Criterion) = '@fromdate' OR LOWER(@Criterion) = '@todate')
BEGIN
IF LOWER(@Criterion) = '@fromdate'
SET @startDateCriteria = SUBSTRING(@TargetString, CHARINDEX(':', @TargetString) + 2, LEN(@TargetString) - CHARINDEX(':', @TargetString))
IF LOWER(@Criterion) = '@todate'
SET @endDateCriteria = SUBSTRING(@TargetString, CHARINDEX(':', @TargetString) + 2, LEN(@TargetString) - CHARINDEX(':', @TargetString))
IF @startDateCriteria IS NOT NULL AND @endDateCriteria IS NOT NULL
BEGIN
SET @criteriaScore = 5
SET @criteriaCount = DATEDIFF (dd, @startDateCriteria, @endDateCriteria)
INSERT INTO @scoringTable
(Criterion, CriteriaScore, Occurances, SumTotalScore)
VALUES
('DateRange', @criteriaScore, @criteriaCount, (@criteriaScore * @criteriaCount))
END
END
ELSE
-- Calculate individual criterion score
BEGIN
SET @criteriaScore =
CASE
WHEN LOWER(@Criterion) = '@branchid' THEN 10
WHEN LOWER(@Criterion) = '@locationid' THEN 10
WHEN LOWER(@Criterion) = '@salesexecid' THEN 1
WHEN LOWER(@Criterion) = '@thedate' THEN 5
ELSE 1
END
SET @criteriaCount =
(SELECT
CASE
WHEN LEN(REPLACE(@TargetString, @Criterion, '')) < 3 THEN 0
ELSE LEN(@TargetString) - LEN(REPLACE(@TargetString, ';', '')) + 1
END
)
INSERT INTO @scoringTable
(Criterion, CriteriaScore, Occurances, SumTotalScore)
VALUES
(@Criterion, @criteriaScore, @criteriaCount, (@criteriaScore * @criteriaCount))
END
END
IF EXISTS (SELECT Occurances from @scoringTable where Occurances > 0 AND LOWER(Criterion) in ('@salesexecid', '@locationid'))
UPDATE @scoringTable SET SumTotalScore = 0 where LOWER(Criterion) = '@branchid'
set @returnScore = (select SUM(SumTotalScore) from @scoringTable)
Return @returnScore;
END
It is designed to split out strings like this:
["@BranchID: 154","@FromDate: 2018-02-01T00:00:00","@ToDate: 2018-02-26T00:00:00","@SalesExecID: "]
and return an overall score based on date range, number of branches included etc.
The following IsDeterministic check is always 0?
SELECT OBJECTPROPERTY(OBJECT_ID('[dbo].[udfGetCriteriaScore]'), 'IsDeterministic')
Other people have the same problem:
https://www.sqlservercentral.com/Forums/Topic1545616-392-1.aspx
There, the solution was to convert the string date to an actual date:
You need to do an explicit CONVERT to use the string literal. I changed your code a little bit to remove the +1
CREATE TABLE Test( DayDate DATE, DayNumber AS (DATEDIFF( DD,
CONVERT( DATE, '2014-04-30', 120), DayDate)) PERSISTED)
INSERT INTO Test(DayDate) VALUES(GETDATE()) SELECT * FROM Test DROP
TABLE Test
The same answer explains this is because there are date formats that are non-deterministic and therefore you need to explicitly set a (deterministic) date format for the string conversion to be considered deterministic.