如何在SQL Server中的表值函数中使用CTE语句

60
我了解到,部分 Microsoft OLE DB Provider for SQL Server 的版本(主要是在 Windows XP 上)不支持 WITH 语句。因此,我决定将我的 SQL 语句移动到一个表值函数中,并从我的应用程序中调用它。现在,我遇到了困难。我应该如何使用 WITH 语句的 INSERT INTO 语句?这是我目前想出来的代码,但 SQL Server 不喜欢它... :-(
CREATE FUNCTION GetDistributionTable 
(
    @IntID int,
    @TestID int,
    @DateFrom datetime,
    @DateTo datetime
)
RETURNS 
@Table_Var TABLE 
(
    [Count] int, 
    Result float
)
AS
BEGIN
INSERT INTO @Table_Var ([Count], Result) WITH T(Result)
     AS (SELECT ROUND(Result - AVG(Result) OVER(), 1)
         FROM RawResults WHERE IntID = @IntID AND DBTestID = @TestID AND Time >= @DateFrom AND Time <= @DateTo)
SELECT COUNT(*) AS [Count],
       Result
FROM   T
GROUP  BY Result

    RETURN 
END
GO
3个回答

79
在表值函数中,CTE 的语法应为:
CREATE FUNCTION GetDistributionTable 
(
    @IntID int,
    @TestID int,
    @DateFrom datetime,
    @DateTo datetime
)
RETURNS TABLE
AS
RETURN  
(
    WITH cte AS
    (
        SELECT ROUND(Result - AVG(Result) OVER(), 1) Result
        FROM   RawResults 
        WHERE  IntID = @IntID 
        AND    DBTestID = @TestID 
        AND    Time >= @DateFrom 
        AND Time <= @DateTo    
    )

    SELECT  COUNT(*) AS [Count],
            Result
    FROM    cte
    GROUP  BY 
            Result
)
GO

如果可能的话,您也可以省略CTE(WITH语句),而是创建一个使用子查询的内联表值函数。
CREATE FUNCTION GetDistributionTable 
(
    @IntID int,
    @TestID int,
    @DateFrom datetime,
    @DateTo datetime
)
RETURNS TABLE
AS
RETURN  
(
    SELECT  COUNT(*) AS [Count],
            Result
    FROM    (
                 SELECT ROUND(Result - AVG(Result) OVER(), 1) Result
                 FROM   RawResults 
                 WHERE  IntID = @IntID 
                 AND    DBTestID = @TestID 
                 AND    Time >= @DateFrom 
                 AND Time <= @DateTo    
    ) t
    GROUP  BY 
            Result
)
GO

你的示例似乎使用了多语句表值函数(插入和选择),如果可以,请尝试使用内联表值函数,因为多语句表值函数可能会阻止查询优化器选择更好的执行计划(性能差异在这里解释)。


如果您的CTE是递归的,那么您可能无法将其重写为子查询形式,因此CTE形式可能不仅仅是口味问题。当然,如果您不小心,递归CTE可能会使优化器失效: http://blogs.msdn.com/b/sqlcat/archive/2011/04/28/optimize-recursive-cte-query.aspx?Redirected=true - MattW
1
习惯在CTE的with部分前面使用; - JJS
@JSS - SQL Server中的语句应该以分号结尾。到目前为止,只有在一些有限的区域强制执行此规则,而不会破坏向后兼容性,特别是一些较新的构造需要在前面的语句之后加上分号。CTE就是其中的一个例子。在CTE之前添加分号是避免语言要求的一种略微hacky的方式,这会导致混淆,例如在没有需要终止的先前语句的情况下。始终终止您的语句可以避免这种情况。 - Richard Abey-Nesbit
仅供参考:根据此链接 https://social.msdn.microsoft.com/Forums/sqlserver/en-US/7c7d5fea-38ad-4bc5-9038-a157e640561f/using-option-clause-within-create-function-statement-for-inline-table-functions?forum=transactsql,我们不能在UDF内使用maxrecursion 0。 - luisvenezian
值得注意的是,在单语句UDF中在查询末尾加上分号(在最后一个括号之前)会被解释为多个语句并导致错误。@RichardAbey-Nesbit - Graham

19

像这样..

CREATE FUNCTION GetDistributionTable 
(
    @IntID int,
    @TestID int,
    @DateFrom datetime,
    @DateTo datetime
)
RETURNS 
@Table_Var TABLE 
(
    [Count] int, 
    Result float
)
AS
BEGIN
  WITH T 
    AS (    
        select Ticket_Id,COUNT(1) Result from 
        Customer_Survey
        group by MemberID,SiteId,Ticket_Id
   )
  INSERT INTO @Table_Var ([Count], Result)
  SELECT COUNT(*) AS [Count],
       Result
  FROM   T
  GROUP  BY Result
  RETURN 
END
GO

2
如果可能的话,应该测试多语句函数和单语句RETURNS TABLE函数,因为后者可以内联,因此有时具有更好的性能(请参见Ivan G答案底部的链接)。然而,通常情况下,这取决于具体情况——有时,虽然很少,使用多语句函数更快。 - ErikE

0
CTE with if else in UDF 

USE [SchoolDB]
GO

/****** Object:  UserDefinedFunction [dbo].[GetDistributionTable]    Script Date: 24-08-2019 05:17:55 PM ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO





alter  FUNCTION [dbo].[GetDistributionTable] 
(
   @DepartmentName varchar(50) = 'Production'
)
RETURNS 
@Table_Var TABLE 
(   NUM int IDENTITY(1,1),
    [ParentEmployeeKey] int, 
    Result float
)
AS
BEGIN
Declare @Table_Vars table
(
    [ParentEmployeeKey] int, 
    Result float
);
insert into @Table_Vars([ParentEmployeeKey])
select COUNT(1) Result from 
        [SchoolDB].[dbo].[DimEmployee] where DepartmentName = @DepartmentName
if(@@rowcount >0)
begin
WITH T 
    AS (    
        select [ParentEmployeeKey],COUNT(1) Result from 
        [SchoolDB].[dbo].[DimEmployee] where DepartmentName = @DepartmentName
        group by [ParentEmployeeKey]
   )
  INSERT INTO @Table_Var ([ParentEmployeeKey], Result)
  SELECT COUNT(*) AS [Count],
       Result
  FROM   T
  GROUP  BY Result
end
else

 WITH T 
    AS (    
        select [ParentEmployeeKey],COUNT(1) Result from 
        [SchoolDB].[dbo].[DimEmployee] where DepartmentName = @DepartmentName
        group by [ParentEmployeeKey]
   )
  INSERT INTO @Table_Var ([ParentEmployeeKey], Result)
  SELECT COUNT(*) AS [Count],
       Result
  FROM   T
  GROUP  BY Result
  RETURN 
END



GO

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接