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

时间:2018-03-08 14:39:27

标签: sql sql-server user-defined-functions 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: "]

并根据日期范围,包括的分支数等返回总分。

以下IsDeminministic检查始终为0?

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

1 个答案:

答案 0 :(得分:2)

其他人也遇到同样的问题:

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

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

  

您需要使用显式CONVERT来使用字符串文字。一世   稍微更改了您的代码以删除+1

   #Normalize data

data[,1:(ncol(data)-1)] = normalize(data[,1:(ncol(data)-1)])

data[,ncol(data)] = as.numeric(data[,ncol(data)]) - 1

set.seed(128)

ind = sample(2,nrow(data),replace = T,prob = c(0.7,0.3))

training = data[ind==1,1:(ncol(data)-1)]

test = data[ind==2,1:(ncol(data)-1)]

traintarget = data[ind==1,ncol(data)]

testtarget = data[ind==2,ncol(data)]
# One hot encoding
trainLabels = to_categorical(traintarget)
testLabels = to_categorical(testtarget)
print(testLabels)

model = keras_model_sequential()

model %>% 
  layer_dense(units = 150, activation = 'relu', input_shape = c(520)) %>% 
  layer_dense(units = 50, activation = 'relu') %>%
  layer_dense(units = 9, activation = 'softmax') 


model %>%
  compile(loss = 'categorical_crossentropy', optimizer = 'adam',metrics = 'accuracy')


history = model %>%
  fit(training,
      trainLabels,
      epoch = 300,
      batch_size = 32,
      validation_split = 0.2)

prob = model %>%
  predict_proba(test)

pred = model %>%
  predict_classes(test)

table2 = table(Predicted = pred, Actual = testtarget)

cbind(prob,pred,testtarget)

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