Sql server 单返回值的表值函数与标量值函数
我从一个定标器函数返回单个值,如下所示:Sql server 单返回值的表值函数与标量值函数,sql-server,sql-server-2012,sql-server-2014,Sql Server,Sql Server 2012,Sql Server 2014,我从一个定标器函数返回单个值,如下所示: CREATE FUNCTION [dbo].[GetNoOfAssignedCases] ( @UserID INT, @FromD DATETIME, @ToD DATETIME ) RETURNS NVARCHAR(MAX) AS BEGIN DECLARE @CaseCount INT = 0 SELECT @CaseCount = COUNT(1) FROM Cases WHERE
CREATE FUNCTION [dbo].[GetNoOfAssignedCases]
(
@UserID INT,
@FromD DATETIME,
@ToD DATETIME
)
RETURNS NVARCHAR(MAX)
AS
BEGIN
DECLARE @CaseCount INT = 0
SELECT @CaseCount = COUNT(1) FROM Cases
WHERE
CaseAssignedToAssessor = @UserID AND
CAST(ActionDateTime AS DATE) >= @FromD AND
CAST(ActionDateTime AS DATE) <= @ToD
RETURN @CaseCount
END
SELECT [Name], [DBO].[GetNoOfAssignedCases](UserID, GETDATE()-30, GETDATE()) FROM Users
可以用表值函数替换它吗它会对性能产生影响吗?哪个更快?表值函数将如下所示:
CREATE FUNCTION [dbo].[GETNOOFASSIGNEDCASES] (@UserID INT,
@FromD DATETIME,
@ToD DATETIME)
RETURNS @CaseCount TABLE (
cnt INT NULL )
AS
BEGIN
INSERT INTO @CaseCount
(cnt)
SELECT Count(1)
FROM Cases
WHERE CaseAssignedToAssessor = @UserID
AND Cast(ActionDateTime AS DATE) >= @FromD
AND Cast(ActionDateTime AS DATE) <= @ToD
END
表值函数如下所示:
CREATE FUNCTION [dbo].[GETNOOFASSIGNEDCASES] (@UserID INT,
@FromD DATETIME,
@ToD DATETIME)
RETURNS @CaseCount TABLE (
cnt INT NULL )
AS
BEGIN
INSERT INTO @CaseCount
(cnt)
SELECT Count(1)
FROM Cases
WHERE CaseAssignedToAssessor = @UserID
AND Cast(ActionDateTime AS DATE) >= @FromD
AND Cast(ActionDateTime AS DATE) <= @ToD
END
是的,你可以这样做(未经测试)
创建函数[dbo]。[GetNoOfAssignedCases]
(
@UserID INT,
@FromD DATETIME,
@ToD日期时间
)
返回表
作为
返回
选择计数(1)作为案例计数
从案例
哪里
casesassignedtoasessor=@UserID和
CAST(ActionDateTime作为日期)>=@FromD和
CAST(ActionDateTime作为日期)是的,您可以这样做(未经测试)
创建函数[dbo]。[GetNoOfAssignedCases]
(
@UserID INT,
@FromD DATETIME,
@ToD日期时间
)
返回表
作为
返回
选择计数(1)作为案例计数
从案例
哪里
casesassignedtoasessor=@UserID和
CAST(ActionDateTime作为日期)>=@FromD和
强制转换(ActionDateTime作为日期)
它会对性能产生影响吗
对。我们非常广泛地使用函数。在优化特定查询时,我发现SQL将标量值函数作为一个单独的实体进行优化,其中TVF“内联”到主查询中,然后作为一个整体进行优化。例外情况可能存在,但我们发现TVF普遍更快(只比自己内联函数稍微慢一点)
可以用表值函数替换它吗
对。如果您担心的是性能,则应使用以下格式:
CREATE FUNCTION [dbo].[GetNoOfAssignedCases]
(
@UserID INT,
@FromD DATETIME,
@ToD DATETIME
)
RETURNS TABLE
AS RETURN
SELECT COUNT(1) AS Count FROM Cases
WHERE
CaseAssignedToAssessor = @UserID AND
CAST(ActionDateTime AS DATE) >= @FromD AND
CAST(ActionDateTime AS DATE) <= @ToD;
您需要RETURN选择,才能进行“内联”。此外,您的工作是确保这些函数不会返回多个记录(除非您确实想要交叉应用行为,而您几乎从未这样做)
它会对性能产生影响吗
对。我们非常广泛地使用函数。在优化特定查询时,我发现SQL将标量值函数作为一个单独的实体进行优化,其中TVF“内联”到主查询中,然后作为一个整体进行优化。例外情况可能存在,但我们发现TVF普遍更快(只比自己内联函数稍微慢一点)
可以用表值函数替换它吗
对。如果您担心的是性能,则应使用以下格式:
CREATE FUNCTION [dbo].[GetNoOfAssignedCases]
(
@UserID INT,
@FromD DATETIME,
@ToD DATETIME
)
RETURNS TABLE
AS RETURN
SELECT COUNT(1) AS Count FROM Cases
WHERE
CaseAssignedToAssessor = @UserID AND
CAST(ActionDateTime AS DATE) >= @FromD AND
CAST(ActionDateTime AS DATE) <= @ToD;
您需要RETURN选择,才能进行“内联”。此外,您的工作是确保这些函数不会返回多个记录(除非您确实想要交叉应用<代码>行为,而您几乎从来没有这样做过)。我与Jonatha Dickinson的讨论(查看他的答案)让我做了一些快速测试:
结果表明,纯的、嵌入的标量子选择并没有那么糟糕。只查询一个值,它甚至是最快的。正如所料,标量函数是错误的。TVF返回的场越多,相对性能增益越好
唯一确定的答案是:标量函数是最差的,多行TVF在大多数情况下比内联的慢任何临时方法都会更快
但我可以为所有情况设置特殊情况(标量函数除外),其中一种方法最快
结论:(一如既往:-)这取决于
提示:最好是在一个包含许多表和列的大型数据库中进行此操作
CREATE FUNCTION dbo.CountColumnScalar(@TableSchema AS VARCHAR(100),@TableName AS VARCHAR(100))
RETURNS INT
AS
BEGIN
RETURN(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS AS c WHERE c.TABLE_SCHEMA=@TableSchema AND c.TABLE_NAME=@TableName);
END
GO
CREATE FUNCTION dbo.CountConstraintScalar(@TableSchema AS VARCHAR(100),@TableName AS VARCHAR(100))
RETURNS INT
AS
BEGIN
RETURN(SELECT COUNT(*) FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE AS c WHERE c.TABLE_SCHEMA=@TableSchema AND c.TABLE_NAME=@TableName);
END
GO
CREATE FUNCTION dbo.CountAllTVF(@TableSchema AS VARCHAR(100),@TableName AS VARCHAR(100))
RETURNS TABLE
RETURN SELECT (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS AS c WHERE c.TABLE_SCHEMA=@TableSchema AND c.TABLE_NAME=@TableName GROUP BY c.TABLE_SCHEMA,c.TABLE_NAME) AS ColCounter
,(SELECT COUNT(*) FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE AS c WHERE c.TABLE_SCHEMA=@TableSchema AND c.TABLE_NAME=@TableName GROUP BY c.TABLE_SCHEMA,c.TABLE_NAME) AS ConstraintCounter ;
GO
CREATE FUNCTION dbo.CountAllTVF_multiline(@TableSchema AS VARCHAR(100),@TableName AS VARCHAR(100))
RETURNS @tbl TABLE (ColCounter INT,ConstraintCounter INT)
AS
BEGIN
INSERT INTO @tbl
SELECT (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS AS c WHERE c.TABLE_SCHEMA=@TableSchema AND c.TABLE_NAME=@TableName GROUP BY c.TABLE_SCHEMA,c.TABLE_NAME) AS ColCounter
,(SELECT COUNT(*) FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE AS c WHERE c.TABLE_SCHEMA=@TableSchema AND c.TABLE_NAME=@TableName GROUP BY c.TABLE_SCHEMA,c.TABLE_NAME) AS ConstraintCounter;
RETURN;
END
GO
DECLARE @time DATETIME=GETDATE();
SELECT TABLE_SCHEMA,TABLE_NAME
,(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS AS c WHERE c.TABLE_SCHEMA=t.TABLE_SCHEMA AND c.TABLE_NAME=t.TABLE_NAME ) AS ColCounter
,(SELECT COUNT(*) FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE AS c WHERE c.TABLE_SCHEMA=t.TABLE_SCHEMA AND c.TABLE_NAME=t.TABLE_NAME ) AS ConstraintCounter
FROM INFORMATION_SCHEMA.TABLES AS t;
PRINT 'pure embedded scalar sub-select: ' + CAST(CAST(GETDATE()-@time AS TIME) AS VARCHAR(MAX)); SET @time=GETDATE();
SELECT TABLE_SCHEMA,TABLE_NAME
,dbo.CountColumnScalar(t.TABLE_SCHEMA,t.TABLE_NAME ) AS ColCounter
,dbo.CountConstraintScalar(t.TABLE_SCHEMA,t.TABLE_NAME ) AS ConstraintCount
FROM INFORMATION_SCHEMA.TABLES AS t
PRINT 'scalar function: ' + CAST(CAST(GETDATE()-@time AS TIME) AS VARCHAR(MAX)); SET @time=GETDATE();
SELECT t.TABLE_SCHEMA,t.TABLE_NAME
,colJoin.ColCount
,conJoin.ConstraintCount
FROM INFORMATION_SCHEMA.TABLES AS t
INNER JOIN (SELECT COUNT(*) As ColCount,c.TABLE_SCHEMA,c.TABLE_NAME
FROM INFORMATION_SCHEMA.COLUMNS AS c
GROUP BY c.TABLE_SCHEMA,c.TABLE_NAME) AS colJoin ON colJoin.TABLE_SCHEMA=t.TABLE_SCHEMA AND colJoin.TABLE_NAME=t.TABLE_NAME
INNER JOIN (SELECT COUNT(*) As ConstraintCount,c.TABLE_SCHEMA,c.TABLE_NAME
FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE AS c
GROUP BY c.TABLE_SCHEMA,c.TABLE_NAME) AS conJoin ON conJoin.TABLE_SCHEMA=t.TABLE_SCHEMA AND conJoin.TABLE_NAME=t.TABLE_NAME
PRINT 'JOINs on sub-selects: ' + CAST(CAST(GETDATE()-@time AS TIME) AS VARCHAR(MAX)); SET @time=GETDATE();
SELECT t.TABLE_SCHEMA,t.TABLE_NAME
,ColCounter.*
FROM INFORMATION_SCHEMA.TABLES AS t
CROSS APPLY dbo.CountAllTVF(t.TABLE_SCHEMA,t.TABLE_NAME) AS ColCounter
PRINT 'TVF inline: ' + CAST(CAST(GETDATE()-@time AS TIME) AS VARCHAR(MAX)); SET @time=GETDATE();
SELECT t.TABLE_SCHEMA,t.TABLE_NAME
,ColCounter.*
FROM INFORMATION_SCHEMA.TABLES AS t
CROSS APPLY dbo.CountAllTVF_multiline(t.TABLE_SCHEMA,t.TABLE_NAME) AS ColCounter
PRINT 'TVF multiline: ' + CAST(CAST(GETDATE()-@time AS TIME) AS VARCHAR(MAX)); SET @time=GETDATE();
GO
DROP FUNCTION dbo.CountColumnScalar;
DROP FUNCTION dbo.CountAllTVF;
DROP FUNCTION dbo.CountAllTVF_multiline;
DROP FUNCTION dbo.CountConstraintScalar;
我与Jonatha Dickinson的讨论(看看他的答案)让我做了一些快速测试:
结果表明,纯的、嵌入的标量子选择并没有那么糟糕。只查询一个值,它甚至是最快的。正如所料,标量函数是错误的。TVF返回的场越多,相对性能增益越好
唯一确定的答案是:标量函数是最差的,多行TVF在大多数情况下比内联的慢任何临时方法都会更快
但我可以为所有情况设置特殊情况(标量函数除外),其中一种方法最快
结论:(一如既往:-)这取决于
提示:最好是在一个包含许多表和列的大型数据库中进行此操作
CREATE FUNCTION dbo.CountColumnScalar(@TableSchema AS VARCHAR(100),@TableName AS VARCHAR(100))
RETURNS INT
AS
BEGIN
RETURN(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS AS c WHERE c.TABLE_SCHEMA=@TableSchema AND c.TABLE_NAME=@TableName);
END
GO
CREATE FUNCTION dbo.CountConstraintScalar(@TableSchema AS VARCHAR(100),@TableName AS VARCHAR(100))
RETURNS INT
AS
BEGIN
RETURN(SELECT COUNT(*) FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE AS c WHERE c.TABLE_SCHEMA=@TableSchema AND c.TABLE_NAME=@TableName);
END
GO
CREATE FUNCTION dbo.CountAllTVF(@TableSchema AS VARCHAR(100),@TableName AS VARCHAR(100))
RETURNS TABLE
RETURN SELECT (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS AS c WHERE c.TABLE_SCHEMA=@TableSchema AND c.TABLE_NAME=@TableName GROUP BY c.TABLE_SCHEMA,c.TABLE_NAME) AS ColCounter
,(SELECT COUNT(*) FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE AS c WHERE c.TABLE_SCHEMA=@TableSchema AND c.TABLE_NAME=@TableName GROUP BY c.TABLE_SCHEMA,c.TABLE_NAME) AS ConstraintCounter ;
GO
CREATE FUNCTION dbo.CountAllTVF_multiline(@TableSchema AS VARCHAR(100),@TableName AS VARCHAR(100))
RETURNS @tbl TABLE (ColCounter INT,ConstraintCounter INT)
AS
BEGIN
INSERT INTO @tbl
SELECT (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS AS c WHERE c.TABLE_SCHEMA=@TableSchema AND c.TABLE_NAME=@TableName GROUP BY c.TABLE_SCHEMA,c.TABLE_NAME) AS ColCounter
,(SELECT COUNT(*) FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE AS c WHERE c.TABLE_SCHEMA=@TableSchema AND c.TABLE_NAME=@TableName GROUP BY c.TABLE_SCHEMA,c.TABLE_NAME) AS ConstraintCounter;
RETURN;
END
GO
DECLARE @time DATETIME=GETDATE();
SELECT TABLE_SCHEMA,TABLE_NAME
,(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS AS c WHERE c.TABLE_SCHEMA=t.TABLE_SCHEMA AND c.TABLE_NAME=t.TABLE_NAME ) AS ColCounter
,(SELECT COUNT(*) FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE AS c WHERE c.TABLE_SCHEMA=t.TABLE_SCHEMA AND c.TABLE_NAME=t.TABLE_NAME ) AS ConstraintCounter
FROM INFORMATION_SCHEMA.TABLES AS t;
PRINT 'pure embedded scalar sub-select: ' + CAST(CAST(GETDATE()-@time AS TIME) AS VARCHAR(MAX)); SET @time=GETDATE();
SELECT TABLE_SCHEMA,TABLE_NAME
,dbo.CountColumnScalar(t.TABLE_SCHEMA,t.TABLE_NAME ) AS ColCounter
,dbo.CountConstraintScalar(t.TABLE_SCHEMA,t.TABLE_NAME ) AS ConstraintCount
FROM INFORMATION_SCHEMA.TABLES AS t
PRINT 'scalar function: ' + CAST(CAST(GETDATE()-@time AS TIME) AS VARCHAR(MAX)); SET @time=GETDATE();
SELECT t.TABLE_SCHEMA,t.TABLE_NAME
,colJoin.ColCount
,conJoin.ConstraintCount
FROM INFORMATION_SCHEMA.TABLES AS t
INNER JOIN (SELECT COUNT(*) As ColCount,c.TABLE_SCHEMA,c.TABLE_NAME
FROM INFORMATION_SCHEMA.COLUMNS AS c
GROUP BY c.TABLE_SCHEMA,c.TABLE_NAME) AS colJoin ON colJoin.TABLE_SCHEMA=t.TABLE_SCHEMA AND colJoin.TABLE_NAME=t.TABLE_NAME
INNER JOIN (SELECT COUNT(*) As ConstraintCount,c.TABLE_SCHEMA,c.TABLE_NAME
FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE AS c
GROUP BY c.TABLE_SCHEMA,c.TABLE_NAME) AS conJoin ON conJoin.TABLE_SCHEMA=t.TABLE_SCHEMA AND conJoin.TABLE_NAME=t.TABLE_NAME
PRINT 'JOINs on sub-selects: ' + CAST(CAST(GETDATE()-@time AS TIME) AS VARCHAR(MAX)); SET @time=GETDATE();
SELECT t.TABLE_SCHEMA,t.TABLE_NAME
,ColCounter.*
FROM INFORMATION_SCHEMA.TABLES AS t
CROSS APPLY dbo.CountAllTVF(t.TABLE_SCHEMA,t.TABLE_NAME) AS ColCounter
PRINT 'TVF inline: ' + CAST(CAST(GETDATE()-@time AS TIME) AS VARCHAR(MAX)); SET @time=GETDATE();
SELECT t.TABLE_SCHEMA,t.TABLE_NAME
,ColCounter.*
FROM INFORMATION_SCHEMA.TABLES AS t
CROSS APPLY dbo.CountAllTVF_multiline(t.TABLE_SCHEMA,t.TABLE_NAME) AS ColCounter
PRINT 'TVF multiline: ' + CAST(CAST(GETDATE()-@time AS TIME) AS VARCHAR(MAX)); SET @time=GETDATE();
GO
DROP FUNCTION dbo.CountColumnScalar;
DROP FUNCTION dbo.CountAllTVF;
DROP FUNCTION dbo.CountAllTVF_multiline;
DROP FUNCTION dbo.CountConstraintScalar;
在这种情况下,人们应该总是更喜欢即席(或内联)UDF!并注意没有案例的用户。更好地使用OUTER APPLY
…在这种情况下,您应该始终更喜欢即席(或内联)UDF!并注意没有案例的用户。更好地使用OUTER APPLY
…您刚刚编辑了关于性能的问题和询问:这取决于。。。如果您必须一次性为所有用户获得一个计数器,最快的可能是老式的分组方式
。。。带有APPLY
的内联UDF应该与带有子选择的JOIN
完全相同。这肯定比多行的标量计算或多语句UDF快。有多少行(在用户
和案例
)?关于更多提示:询问性能,您应该阅读“可搜索表达式”。您的CAST(ActionDateTime…
将不可搜索…@Shnugo将有100多个用户和20000多个案例。我正在使用其他3个类似的函数,根据一个选择的不同条件获取计数。因此,我不认为我可以使用group by。如果在案例
(以及其他表格)中外键上有索引,那么一个简单的计数肯定会永远不要导致性能问题。正如我在回答中所说,内联自定义项与子选择上的联接大致相同,如果同时对多个用户进行联接,则比逐行联接好得多。嗨,我很好奇:你能解决你的问题吗?您选择了哪种方法?您刚刚编辑了您的问题并询问了有关性能的问题:这取决于。。。如果你必须得到一个co