表值函数与标量值单个返回值的函数

时间:2015-11-30 07:56:24

标签: 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 
        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

可以用表值函数替换吗? 它会对性能产生任何影响吗?哪个会更快?

4 个答案:

答案 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