MSSQL 为啥这个函数是不确定的
Posted
技术标签:
【中文标题】MSSQL 为啥这个函数是不确定的【英文标题】:MSSQL Why is this function non-deterministicMSSQL 为什么这个函数是不确定的 【发布时间】:2018-03-08 14:39:26 【问题描述】:我有这个用户函数,它总是被标记为非确定性的,尽管只要输入参数相同,值总是相同的。我读过的所有内容都表明这应该是确定性的。
有人能找出原因吗?
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: "]
并根据日期范围、包含的分支数量等返回总分。
下面的 IsDeterministic 检查总是 0?
SELECT OBJECTPROPERTY(OBJECT_ID('[dbo].[udfGetCriteriaScore]'), 'IsDeterministic')
【问题讨论】:
标记您正在使用的 dbms。该代码是特定于产品的。 道歉。微软 SQL 2012 发现这是由于 SET @criteriaCount = DATEDIFF (dd, ....);如果您评论此行,它将是确定性的。检查原因 啊,是的,谢谢。我可以看到它切换到确定性。我认为 DATEDIFF 本身就是一个确定性函数? 您依赖从字符串到DATETIME
值的隐式转换。这些因语言设置而异,因此不被视为确定性。
【参考方案1】:
其他人也有同样的问题:
https://www.sqlservercentral.com/Forums/Topic1545616-392-1.aspx
在那里,解决方案是将字符串日期转换为实际日期:
您需要执行显式 CONVERT 才能使用字符串文字。一世 稍微更改了您的代码以删除 +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
相同的答案解释了这是因为存在不确定的日期格式,因此您需要显式设置(确定的)日期格式,以便将字符串转换视为确定性。
【讨论】:
已编辑答案中的线程是通过逐字询问谷歌“sql server datediff deterministic”找到的 @JeroenMostert 完成以上是关于MSSQL 为啥这个函数是不确定的的主要内容,如果未能解决你的问题,请参考以下文章
不能对不可变值使用变异成员:函数调用返回不可变值 - 不确定为啥值是不可变的