我从缩放器函数返回单个值,如下所示:
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
可以用表值函数替换吗? 它会对性能产生任何影响吗?哪个会更快?
答案 0 :(得分:2)
是的,你可以这样(未经测试)
CREATE FUNCTION [dbo].[GetNoOfAssignedCases]
(
@UserID INT,
@FromD DATETIME,
@ToD DATETIME
)
RETURNS TABLE
AS
RETURN
SELECT COUNT(1) AS CaseCount
FROM Cases
WHERE
CaseAssignedToAssessor = @UserID AND
CAST(ActionDateTime AS DATE) >= @FromD AND
CAST(ActionDateTime AS DATE) <= @ToD;
GO
--And a call like this
SELECT [Name],CaseCounter.CaseCount
FROM Users
OUTER APPLY [DBO].[GetNoOfAssignedCases](UserID, GETDATE()-30, GETDATE()) AS CaseCounter
但是 - 如果你真的只需要一个标量值! - 我不知道为什么......
TVF是个好主意的原因:
答案 1 :(得分:2)
会对性能产生任何影响吗?
是。我们非常广泛地使用函数。在优化特定查询时,我发现SQL优化了标量值函数作为一个单独的实体,其中TVF是内联的&#34;进入主查询然后整体优化。可能存在例外情况,但我们发现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;
然后可以使用交叉应用来执行该功能:
SELECT [Name], [AC].[Count] FROM Users
CROSS APPLY [DBO].[GetNoOfAssignedCases](UserID, GETDATE()-30, GETDATE()) AS [AC]
你需要RETURN SELECT
进行&#34;内联&#34;发生。此外,您的工作是确保这些功能不会返回多个记录(除非您实际需要CROSS APPLY
行为 - 您几乎从不这样做。)
答案 2 :(得分:2)
我与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;
答案 3 :(得分:0)
表值函数将如下所示:
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
您应该使用cross apply或outer apply:
调用上面定义的表值函数SELECT [Name],tmp.cnt
FROM Users
CROSS apply [DBO].[GETNOOFASSIGNEDCASES](UserID, Getdate() - 30, Getdate()) as tmp