为什么 SQL 服务器说这个函数是不确定的?
Why does SQL Server say this function is nondeterministic?
在T-SQL:
中执行这个函数
CREATE FUNCTION [dbo].[Parse_URI_For_Scheme](
@URI nvarchar(4000))
RETURNS nvarchar(250)
WITH SCHEMABINDING
AS
BEGIN
DECLARE @temp_string varchar(4000)
DECLARE @return_string nvarchar(250)
DECLARE @pos int
SET @pos = CHARINDEX('://', @URI);
--select @pos
IF @pos > 0
BEGIN
SET @temp_string = SUBSTRING(@URI, 0, @pos);
-- SET @pos = CHARINDEX('/', @temp_string)
IF @pos > 0
BEGIN
SET @temp_string = LEFT(@temp_string, @pos - 1);
SET @pos = CHARINDEX('@', @temp_string);
IF @pos > 0
SET @return_string = SUBSTRING(@temp_string, @pos + 1, 250);
ELSE
SET @return_string = @temp_string;
END
ELSE
SET @return_string = '';
END
ELSE
SET @return_string = '';
RETURN @return_string;
END;
然后执行这条命令 which returns 0:
SELECT OBJECTPROPERTY(OBJECT_ID('[dbo].Parse_URI_For_Scheme'), 'IsDeterministic')
谁能告诉我为什么这不是确定性函数?
SQL 服务器将函数标记为确定性函数的关键点之一是 SchemaBinding
特性。为了使您的函数具有确定性,您需要使用 With SchemaBinding
.
定义函数
在您的示例中,如果删除 With SchemaBinding
,ObjectProperty
函数将为 IsDeterministic
属性设置 return 0,因此通过添加 With SchemaBinding
问题会为您解决
Vahid 的回复很好。我同意他所说的一切,并且喜欢 link 对 Paul White 的评论,这是非常好的。
有几点需要注意:
1.您使用的逻辑对于您要执行的操作来说过于复杂。
我认为你可以逃脱惩罚:
SUBSTRING(@URI, 1, ISNULL(NULLIF(CHARINDEX('://', @URI)-1,0),''))
2。如果您必须为此任务使用函数,请使用内联 Table 值函数 (iTVF)。
这个建议改变了我的职业生涯,多年来我学到的是有两种 T-SQL 函数:iTVF 和非常慢的函数(例如标量和多语句 table 值函数)。以下是 Paul White 关于此主题的更多精彩读物:Understanding and Using APPLY (Part 1).
让我们将您的函数变成一个高性能、支持并行性的 DETERMINISITC 函数。
CREATE FUNCTION dbo.parse_uri_for_scheme_itvf(@URI nvarchar(4000))
RETURNS TABLE WITH SCHEMABINDING AS RETURN
SELECT newURI = SUBSTRING(@URI, 1, ISNULL(NULLIF(CHARINDEX('://', @URI)-1,0),''));
和一个性能测试来证明为什么标量函数不适合匆忙的人。
示例数据
SELECT string = cast(pr+'://'+samples.txt as nvarchar(4000))
INTO #strings
FROM (VALUES ('http'),('https'),('ftp')) pr(pr)
CROSS JOIN
(
SELECT replicate(newid(), abs(checksum(newid())%2)+1)
FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) a(x),
(VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) b(x),
(VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) c(x),
(VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) d(x),
(VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) e(x)
) samples(txt);
性能测试
SET NOCOUNT ON;
PRINT 'Scalar'+char(10)+replicate('-',60);
GO
DECLARE @st datetime = getdate(), @txt nvarchar(4000);
SELECT @txt = dbo.Parse_URI_For_Scheme(t.string)
FROM #strings t;
PRINT datediff(ms,@st,getdate())
GO 3
PRINT 'iTVF'+char(10)+replicate('-',60);
GO
DECLARE @st datetime = getdate(), @txt nvarchar(4000);
SELECT @txt = itvf.newURI
FROM #strings t
CROSS APPLY dbo.parse_uri_for_scheme_itvf(t.string) itvf;
PRINT datediff(ms,@st,getdate())
GO 3
结果
Scalar
------------------------------------------------------------
Beginning execution loop
1423
1380
1360
Batch execution completed 3 times.
iTVF
------------------------------------------------------------
Beginning execution loop
423
427
437
Batch execution completed 3 times.
我在准备测试之前就知道 iTVF 会快 3-4 倍。
在T-SQL:
中执行这个函数CREATE FUNCTION [dbo].[Parse_URI_For_Scheme](
@URI nvarchar(4000))
RETURNS nvarchar(250)
WITH SCHEMABINDING
AS
BEGIN
DECLARE @temp_string varchar(4000)
DECLARE @return_string nvarchar(250)
DECLARE @pos int
SET @pos = CHARINDEX('://', @URI);
--select @pos
IF @pos > 0
BEGIN
SET @temp_string = SUBSTRING(@URI, 0, @pos);
-- SET @pos = CHARINDEX('/', @temp_string)
IF @pos > 0
BEGIN
SET @temp_string = LEFT(@temp_string, @pos - 1);
SET @pos = CHARINDEX('@', @temp_string);
IF @pos > 0
SET @return_string = SUBSTRING(@temp_string, @pos + 1, 250);
ELSE
SET @return_string = @temp_string;
END
ELSE
SET @return_string = '';
END
ELSE
SET @return_string = '';
RETURN @return_string;
END;
然后执行这条命令 which returns 0:
SELECT OBJECTPROPERTY(OBJECT_ID('[dbo].Parse_URI_For_Scheme'), 'IsDeterministic')
谁能告诉我为什么这不是确定性函数?
SQL 服务器将函数标记为确定性函数的关键点之一是 SchemaBinding
特性。为了使您的函数具有确定性,您需要使用 With SchemaBinding
.
在您的示例中,如果删除 With SchemaBinding
,ObjectProperty
函数将为 IsDeterministic
属性设置 return 0,因此通过添加 With SchemaBinding
问题会为您解决
Vahid 的回复很好。我同意他所说的一切,并且喜欢 link 对 Paul White 的评论,这是非常好的。
有几点需要注意:
1.您使用的逻辑对于您要执行的操作来说过于复杂。
我认为你可以逃脱惩罚:
SUBSTRING(@URI, 1, ISNULL(NULLIF(CHARINDEX('://', @URI)-1,0),''))
2。如果您必须为此任务使用函数,请使用内联 Table 值函数 (iTVF)。
这个建议改变了我的职业生涯,多年来我学到的是有两种 T-SQL 函数:iTVF 和非常慢的函数(例如标量和多语句 table 值函数)。以下是 Paul White 关于此主题的更多精彩读物:Understanding and Using APPLY (Part 1).
让我们将您的函数变成一个高性能、支持并行性的 DETERMINISITC 函数。
CREATE FUNCTION dbo.parse_uri_for_scheme_itvf(@URI nvarchar(4000))
RETURNS TABLE WITH SCHEMABINDING AS RETURN
SELECT newURI = SUBSTRING(@URI, 1, ISNULL(NULLIF(CHARINDEX('://', @URI)-1,0),''));
和一个性能测试来证明为什么标量函数不适合匆忙的人。
示例数据
SELECT string = cast(pr+'://'+samples.txt as nvarchar(4000))
INTO #strings
FROM (VALUES ('http'),('https'),('ftp')) pr(pr)
CROSS JOIN
(
SELECT replicate(newid(), abs(checksum(newid())%2)+1)
FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) a(x),
(VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) b(x),
(VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) c(x),
(VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) d(x),
(VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) e(x)
) samples(txt);
性能测试
SET NOCOUNT ON;
PRINT 'Scalar'+char(10)+replicate('-',60);
GO
DECLARE @st datetime = getdate(), @txt nvarchar(4000);
SELECT @txt = dbo.Parse_URI_For_Scheme(t.string)
FROM #strings t;
PRINT datediff(ms,@st,getdate())
GO 3
PRINT 'iTVF'+char(10)+replicate('-',60);
GO
DECLARE @st datetime = getdate(), @txt nvarchar(4000);
SELECT @txt = itvf.newURI
FROM #strings t
CROSS APPLY dbo.parse_uri_for_scheme_itvf(t.string) itvf;
PRINT datediff(ms,@st,getdate())
GO 3
结果
Scalar
------------------------------------------------------------
Beginning execution loop
1423
1380
1360
Batch execution completed 3 times.
iTVF
------------------------------------------------------------
Beginning execution loop
423
427
437
Batch execution completed 3 times.
我在准备测试之前就知道 iTVF 会快 3-4 倍。