TSQL UDF 根据第一个函数参数的值返回具有不同列数的表

Posted

技术标签:

【中文标题】TSQL UDF 根据第一个函数参数的值返回具有不同列数的表【英文标题】:TSQL UDF return table with different number of columns depending on value of first function parameter 【发布时间】:2020-03-21 14:28:58 【问题描述】:

有可能吗?如果没有,还有什么其他选择?

到目前为止,我尝试做的其中一件事是(不起作用;我也知道,如果第一个参数的值是 4,我会尝试返回一个未初始化的变量,但是这大约是我的目标):


SET NOCOUNT ON;
USE AdventureWorks2008;
GO

IF OBJECT_ID('dbo.fn_GetPopleInfo') IS NOT NULL
 DROP FUNCTION dbo.fn_GetPopleInfo;
GO

CREATE FUNCTION dbo.fn_GetPopleInfo
 (@columns INT) RETURNS TABLE
AS
BEGIN
    IF (@columns = 1)
        CREATE TABLE @People ( LastName AS VARCHAR(50) );
        INSERT INTO @People SELECT P.LastName
                            FROM Person.Person AS P
    ELSE IF (@columns = 2)
        CREATE TABLE @People ( LastName AS VARCHAR(50), FirstName AS VARCHAR(50) );
        INSERT INTO @People SELECT P.LastName, Person.FirstName
                            FROM Person.Person AS P
    ELSE IF (@columns = 3)
        CREATE TABLE @People ( LastName AS VARCHAR(50), FirstName AS VARCHAR(50), Email AS VARCHAR(50) );
        INSERT INTO @People SELECT P.LastName, P.FirstName, E.EmailAddress
                            FROM Person.Person AS P
                            INNER JOIN Person. EmailAddress AS E ON P.BusinessEntityID = E.BusinessEntityID
    ELSE IF (@columns = 4)
        CREATE TABLE @People ( LastName AS VARCHAR(50), FirstName AS VARCHAR(50), Email AS VARCHAR(50), Address AS VARCHAR(50) );
        INSERT INTO @People SELECT P.LastName, P.FirstName, E.EmailAddress, A.AddressLine1
                            FROM Person.Person AS P
                            INNER JOIN Person.EmailAddress AS E ON P.BusinessEntityID = E.BusinessEntityID
                            INNER JOIN Person.Address AS A ON E.AddressID = A.AddressID
    RETURN @People
END

我可以使用的另一个选项是返回一个带有串联信息的标量值,如下所示(但我更希望获得一个表):

SET NOCOUNT ON;
USE AdventureWorks2008;
GO

IF OBJECT_ID('dbo.fn_ConcatPeopleData') IS NOT NULL
 DROP FUNCTION dbo.fn_ConcatPeopleData;
GO


CREATE FUNCTION dbo.fn_ConcatPeopleData
 (@bid AS NCHAR(5), @columns AS INT ) RETURNS NCHAR(1000)
AS
BEGIN
DECLARE @data AS VARCHAR(8000);
SET @data = '';
    IF (@columns = 1)
        SELECT @data = @data + CAST(LastName AS VARCHAR(100))
        FROM Person.Person AS P
        WHERE P.BusinessEntityID = @bid;

    ELSE IF (@columns = 2)
        SELECT @data = @data + CAST(LastName AS VARCHAR(100))+' ' + CAST(FirstName AS VARCHAR(100))
        FROM Person.Person AS P
        WHERE P.BusinessEntityID = @bid;

    ELSE IF (@columns = 3)
         SELECT @data = @data + CAST(LastName AS VARCHAR(100))+' ' + CAST(FirstName AS VARCHAR(100))+', ' +
                               CAST(E.EmailAddress AS VARCHAR(100))
         FROM Person.Person AS P 
         INNER JOIN Person. EmailAddress AS E ON P.BusinessEntityID = E.BusinessEntityID
         WHERE P.BusinessEntityID = @bid;

    ELSE IF (@columns = 4)
        SELECT @data = @data + CAST(LastName AS VARCHAR(100))+' ' + CAST(FirstName AS VARCHAR(100))+', ' + 
                               CAST(E.EmailAddress AS VARCHAR(100))+', ' + CAST(A.AddressLine1 AS VARCHAR(100))+' ' +
                               CAST(A.City AS VARCHAR(100))+' ' + CAST(A.PostalCode AS VARCHAR(100))
               FROM Person.Person AS P
               INNER JOIN Person.EmailAddress AS E ON P.BusinessEntityID = E.BusinessEntityID
               INNER JOIN Person.BusinessEntityAddress AS BA ON P.BusinessEntityID = BA.BusinessEntityID
               INNER JOIN Person.Address AS A ON BA.AddressID = A.AddressID
               WHERE P.BusinessEntityID = @bid;
RETURN @data;
END


SELECT BusinessEntityID, dbo.fn_ConcatPeopleData(BusinessEntityID, 3) AS [Person Info]
FROM Person.Person;

【问题讨论】:

不确定你的用法,但你需要一个存储过程而不是一个函数。也就是说,依赖于提供的参数值的不同结果集架构表明接口存在缺陷。 @DanGuzman 完全同意,听起来像 XY 问题。获得特定输出的几个想法:1)JSON 并在 func/2)临时表和删除未使用的列(hackish)3)Polymorphic functions - 不适用于 SQL Server 【参考方案1】:

实际上,如果你定义了返回值,你的代码就可以工作:

DECLARE @People TABLE (
    Lastname VARCHAR(50),
    FirstName VARCHAR(50),
    Email VARCHAR(50),
    Address VARCHAR(50)
) ;

IF @columns = 1 BEGIN
    INSERT INTO @People (LastName)
        SELECT P.LastName
        FROM Person.Person AS P;
END;
ELSE IF @columns = 2 BEGIN
    INSERT INTO @People (LastName, FirstName)
        SELECT P.LastName, P.FirstName
        FROM Person.Person P;
END;
. . . 
RETURN @People

未使用的列将是NULL,对于没有分配值的列来说,这是一个完全合理的值。

注意事项:

临时表不在@ 中。它们以# 开头。在这种情况下,一个表变量就可以了。 我不知道在声明表中列的类型时可以使用as。它用于生成的列。 注意表别名。

【讨论】:

以上是关于TSQL UDF 根据第一个函数参数的值返回具有不同列数的表的主要内容,如果未能解决你的问题,请参考以下文章

在 TSQL 中,我可以创建一个以表行作为输入的 UDF 吗?

创建具有可变数量参数的 CLR UDF

参数为空时,Spark Scala UDF 不返回预期值

具有多个参数的 PySpark UDF 返回 null

比较两个字符串的 TSQL 函数

PySpark UDF 无法识别参数数量