MSSQL 为什么这个函数是不确定的

MSSQL Why is this function non-deterministic

我有这个用户函数,它总是被标记为不确定的,尽管只要输入参数相同,值就会始终相同。我读过的所有内容都表明这应该是确定性的。

有人能找出原因吗?

    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

它被设计成像这样拆分字符串:

["@BranchID: 154","@FromDate: 2018-02-01T00:00:00","@ToDate: 2018-02-26T00:00:00","@SalesExecID: "]

和return基于日期范围、包含的分支数量等的总分

下面的 IsDeterministic 检查总是 0?

SELECT OBJECTPROPERTY(OBJECT_ID('[dbo].[udfGetCriteriaScore]'), 'IsDeterministic')

其他人有同样的问题:

https://www.sqlservercentral.com/Forums/Topic1545616-392-1.aspx

在那里,解决方案是将字符串日期转换为实际日期:

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

同样的答案解释了这是因为有 non-deterministic 的日期格式,因此您需要明确设置(确定性的)日期格式以使字符串转换被视为确定性的。