【问题标题】:Pass table as parameter into sql server UDF将表作为参数传递到 sql server UDF
【发布时间】:2010-12-09 05:01:34
【问题描述】:

我想将表格作为参数传递到缩放器 UDF。

我还希望将参数限制为只有一列的表。 (可选)

这可能吗?

编辑

我不想传递表名,我想传递数据表(我假设作为参考)

编辑

我希望我的 Scaler UDF 基本上采用一个值表并返回一个 CSV 行列表。

IE

col1  
"My First Value"  
"My Second Value"
...
"My nth Value"

会回来

"My First Value, My Second Value,... My nth Value"

不过,我想对表格进行一些过滤,IE 确保没有空值并确保没有重复项。我期待着这样的事情:

SELECT dbo.MyFunction(SELECT DISTINCT myDate FROM myTable WHERE myDate IS NOT NULL)

【问题讨论】:

    标签: sql sql-server sql-server-2005 parameters user-defined-functions


    【解决方案1】:

    你可以,但是没有任何桌子。来自文档:

    对于 Transact-SQL 函数,所有数据 类型,包括 CLR 用户定义的 类型和用户定义的表类型, 允许除时间戳数据外 输入。

    您可以使用user-defined table types

    用户自定义表类型示例:

    CREATE TYPE TableType 
    AS TABLE (LocationName VARCHAR(50))
    GO 
    
    DECLARE @myTable TableType
    INSERT INTO @myTable(LocationName) VALUES('aaa')
    SELECT * FROM @myTable
    

    所以你可以做的是定义你的表类型,例如TableType,并定义接受这种类型参数的函数。一个示例函数:

    CREATE FUNCTION Example( @TableName TableType READONLY)
    RETURNS VARCHAR(50)
    AS
    BEGIN
        DECLARE @name VARCHAR(50)
    
        SELECT TOP 1 @name = LocationName FROM @TableName
        RETURN @name
    END
    

    参数必须是只读的。以及示例用法:

    DECLARE @myTable TableType
    INSERT INTO @myTable(LocationName) VALUES('aaa')
    SELECT * FROM @myTable
    
    SELECT dbo.Example(@myTable)
    

    根据您想要实现的目标,您可以修改此代码。

    编辑: 如果您在表中有数据,您可以创建一个变量:

    DECLARE @myTable TableType
    

    并将数据从您的表中取出到变量中

    INSERT INTO @myTable(field_name)
    SELECT field_name_2 FROM my_other_table
    

    【讨论】:

    • 啊,这正是我想要的,但似乎这可用于 SqlServer 2008。我在 sqlserver 2005 中尝试过,但出现语法错误,文档特别提到了 2008。我查看了创建类型的 SqlServer 2005 文档,它没有在选项中列出 TABLE (msdn.microsoft.com/en-us/library/ms175007(SQL.90).aspx)
    【解决方案2】:

    不幸的是,SQL Server 2005 中没有简单的方法。Lukasz 的回答对 SQL Server 2008 来说是正确的,但该功能long过期

    任何解决方案都将涉及临时表,或传入 xml/CSV 并在 UDF 中解析。示例:改成xml,在udf中解析

    DECLARE @psuedotable xml
    
    SELECT
        @psuedotable = ...
    FROM
        ...
    FOR XML ...
    
    SELECT ... dbo.MyUDF (@psuedotable)
    

    但是,您想在更大的范围内做什么?可能还有其他方法可以做到这一点...

    编辑:为什么不将查询作为字符串传递并使用带有输出参数的存储过程

    注意:这是一段未经测试的代码,您需要考虑 SQL 注入等。但是,它也满足您的“单列”要求,应该可以帮助您

    CREATE PROC dbo.ToCSV (
        @MyQuery varchar(2000),
        @CSVOut varchar(max)
    )
    AS
    SET NOCOUNT ON
    
    CREATE TABLE #foo (bar varchar(max))
    
    INSERT #foo
    EXEC (@MyQuery)
    
    SELECT
        @CSVOut = SUBSTRING(buzz, 2, 2000000000)
    FROM
        (
        SELECT 
            bar -- maybe CAST(bar AS varchar(max))??
        FROM 
            #foo
        FOR XML PATH (',')
        ) fizz(buzz)
    GO
    

    【讨论】:

      【解决方案3】:

      第 1 步:创建一个名为 TableType 的类型作为表,它将接受具有一个 varchar 列的表

      create type TableType
      as table ([value] varchar(100) null)
      

      第 2 步:创建一个函数,该函数将接受上面声明的 TableType 作为表值参数和字符串值作为分隔符

      create function dbo.fn_get_string_with_delimeter (@table TableType readonly,@Separator varchar(5))
      returns varchar(500)
      As
      begin
      
          declare @return varchar(500)
      
          set @return = stuff((select @Separator + value from @table for xml path('')),1,1,'')
      
          return @return
      
      end
      

      第 3 步:将具有一列 varchar 列的表传递给用户定义的类型 TableType 和 ',' 作为函数中的分隔符

      select dbo.fn_get_string_with_delimeter(@tab, ',')
      

      【讨论】:

      • 太好了,这就是我要找的。​​span>
      【解决方案4】:

      切入底线,您希望将 SELECT x FROM y 之类的查询传递给一个函数,该函数以逗号分隔的字符串形式返回值。

      正如已经解释过的,您可以通过创建表类型并将 UDT 传递给函数来执行此操作,但这需要多行语句。

      您可以在不声明类型表的情况下传递 XML,但这似乎需要一个 xml 变量,它仍然是一个多行语句,即

      DECLARE @MyXML XML = (SELECT x FROM y FOR XML RAW);
      SELECT Dbo.CreateCSV(@MyXml);
      

      “FOR XML RAW”使 SQL 将它的结果集作为某些 xml 提供给您。

      但是您可以使用 Cast(... AS XML) 绕过该变量。那么这只是一些 XQuery 和一些连接技巧的问题:

      CREATE FUNCTION CreateCSV (@MyXML XML) 
      RETURNS VARCHAR(MAX)
      BEGIN
          DECLARE @listStr VARCHAR(MAX);
          SELECT 
                  @listStr = 
                      COALESCE(@listStr+',' ,'') + 
                      c.value('@Value[1]','nvarchar(max)') 
              FROM @myxml.nodes('/row') as T(c)
          RETURN @listStr
      END
      GO
      
      -- And you call it like this:
      SELECT Dbo.CreateCSV(CAST((    SELECT x FROM y    FOR XML RAW) AS XML));
      
      -- Or a working example
      SELECT Dbo.CreateCSV(CAST((
              SELECT DISTINCT number AS Value 
              FROM master..spt_values 
              WHERE type = 'P' 
                  AND number <= 20
          FOR XML RAW) AS XML));
      

      只要您使用 FOR XML RAW,您所需要做的就是将您想要的列作为 Value 的别名,因为这是在函数中硬编码的。

      【讨论】:

        【解决方案5】:

        在存储过程中将表作为参数传递

        第 1 步:

        创建表 [DBO].T_EMPLOYEES_DETAILS ( Id int, 名称 nvarchar(50), 性别 nvarchar(10), 工资整数 )

        第 2 步:

        创建类型 EmpInsertType 作为表 ( Id int, 名称 nvarchar(50), 性别 nvarchar(10), 工资整数 )

        第 3 步:

        /* 必须在变量末尾添加 READONLY 关键字 */

        CREATE PROC PRC_EmpInsertType @EmployeeInsertType EmpInsertType 只读 作为 开始 插入 [DBO].T_EMPLOYEES_DETAILS 选择 * FROM @EmployeeInsertType 结束

        第 4 步:

        声明@EmployeeInsertType EmpInsertType

        INSERT INTO @EmployeeInsertType VALUES(1,'John','Male',50000) 插入@EmployeeInsertType 值(2,'Praveen','Male',60000) 插入@EmployeeInsertType 值(3,'Chitra','Female',45000) 插入@EmployeeInsertType 值(4,'Mathy','Female',6600) INSERT INTO @EmployeeInsertType VALUES(5,'Sam','Male',50000)

        EXEC PRC_EmpInsertType @EmployeeInsertType

        ========================================

        从 T_EMPLOYEES_DETAILS 中选择 *

        输出

        1 约翰男 50000

        2 普拉文男 60000

        3 奇特拉女 45000

        4 马蒂女 6600

        5 萨姆男 50000

        【讨论】:

          【解决方案6】:

          即使我使用的是 SQL Server 2000,我也一直在处理一个非常相似的问题,并且能够实现我想要的。我知道这是一个老问题,但认为在此处发布它是有效的解决方案,因为应该有像我这样使用旧版本但仍需要帮助的其他人。

          诀窍在于:SQL Server 不接受将表传递给 UDF,您也不能传递 T-SQL 查询,因此该函数会创建临时表,甚至调用存储过程来执行此操作。所以,相反,我创建了一个保留表,我称之为 xtList。这将保存要使用的值列表(1 列,根据需要)。

          CREATE TABLE [dbo].[xtList](
              [List] [varchar](1000) NULL
          ) ON [PRIMARY]
          

          然后,一个存储过程来填充列表。这不是绝对必要的,但我认为这是非常有用的最佳实践。

          -- =============================================
          -- Author:      Zark Khullah
          -- Create date: 20/06/2014
          -- =============================================
          CREATE PROCEDURE [dbo].[xpCreateList]
              @ListQuery varchar(2000)
          AS
          BEGIN
              SET NOCOUNT ON;
          
            DELETE FROM xtList
          
            INSERT INTO xtList
              EXEC(@ListQuery)
          END
          

          现在,只需使用 xtList 以您想要的任何方式处理列表。您可以在过程中使用(用于执行多个 T-SQL 命令)、标量函数(用于检索多个字符串)或多语句表值函数(检索字符串,但就像在表中一样,每行 1 个字符串)。对于其中任何一个,您都需要游标:

          DECLARE @Item varchar(100)
          DECLARE cList CURSOR DYNAMIC
            FOR (SELECT * FROM xtList WHERE List is not NULL)
            OPEN cList
          
          FETCH FIRST FROM cList INTO @Item
          WHILE @@FETCH_STATUS = 0 BEGIN
          
            << desired action with values >>
          
          FETCH NEXT FROM cList INTO @Item
          END
          CLOSE cList
          DEALLOCATE cList
          

          所需的操作如下所示,具体取决于创建的对象类型:

          存储过程

          -- =============================================
          -- Author:      Zark Khullah
          -- Create date: 20/06/2014
          -- =============================================
          CREATE PROCEDURE [dbo].[xpProcreateExec]
          (
              @Cmd varchar(8000),
              @ReplaceWith varchar(1000)
          )
          AS
          BEGIN
            DECLARE @Query varchar(8000)
          
            << cursor start >>
              SET @Query = REPLACE(@Cmd,@ReplaceWith,@Item)
              EXEC(@Query)
            << cursor end >>
          END
          
          /* EXAMPLES
          
            (List A,B,C)
          
            Query = 'SELECT x FROM table'
              with EXEC xpProcreateExec(Query,'x') turns into
            SELECT A FROM table
            SELECT B FROM table
            SELECT C FROM table
          
            Cmd = 'EXEC procedure ''arg''' --whatchout for wrong quotes, since it executes as dynamic SQL
              with EXEC xpProcreateExec(Cmd,'arg') turns into
            EXEC procedure 'A'
            EXEC procedure 'B'
            EXEC procedure 'C'
          
          */
          

          标量函数

          -- =============================================
          -- Author:      Zark Khullah
          -- Create date: 20/06/2014
          -- =============================================
          CREATE FUNCTION [dbo].[xfProcreateStr]
          (
              @OriginalText varchar(8000),
              @ReplaceWith varchar(1000)
          )
          RETURNS varchar(8000)
          AS
          BEGIN
              DECLARE @Result varchar(8000)
          
            SET @Result = ''
            << cursor start >>
              SET @Result = @Result + REPLACE(@OriginalText,@ReplaceWith,@Item) + char(13) + char(10)
            << cursor end >>
          
              RETURN @Result
          END
          
          /* EXAMPLE
          
            (List A,B,C)
          
            Text = 'Access provided for user x'
              with "SELECT dbo.xfProcreateStr(Text,'x')" turns into
            'Access provided for user A
            Access provided for user B
            Access provided for user C'
          
          */
          

          多语句表值函数

          -- =============================================
          -- Author:      Zark Khullah
          -- Create date: 20/06/2014
          -- =============================================
          CREATE FUNCTION [dbo].[xfProcreateInRows]
          (
              @OriginalText varchar(8000),
              @ReplaceWith varchar(1000)
          )
          RETURNS 
          @Texts TABLE 
          (
              Text varchar(2000)
          )
          AS
          BEGIN
            << cursor start >>
                INSERT INTO @Texts VALUES(REPLACE(@OriginalText,@ReplaceWith,@Item))
            << cursor end >>
          END
          
          /* EXAMPLE
          
            (List A,B,C)
          
            Text = 'Access provided for user x'
              with "SELECT * FROM dbo.xfProcreateInRow(Text,'x')" returns rows
            'Access provided for user A'
            'Access provided for user B'
            'Access provided for user C'
          
          */
          

          【讨论】:

            【解决方案7】:

            要获取表的列数,请使用:

            select count(id) from syscolumns where id = object_id('tablename')
            

            要将表传递给函数,请尝试使用 XML,如 show here:

            create function dbo.ReadXml (@xmlMatrix xml)
            returns table
            as
            return
            ( select
            t.value('./@Salary', 'integer') as Salary,
            t.value('./@Age', 'integer') as Age
            from @xmlMatrix.nodes('//row') x(t)
            )
            go
            
            declare @source table
            ( Salary integer,
            age tinyint
            )
            insert into @source
            select 10000, 25 union all
            select 15000, 27 union all
            select 12000, 18 union all
            select 15000, 36 union all
            select 16000, 57 union all
            select 17000, 44 union all
            select 18000, 32 union all
            select 19000, 56 union all
            select 25000, 34 union all
            select 7500, 29
            --select * from @source
            
            declare @functionArgument xml
            
            select @functionArgument =
            ( select
            Salary as [row/@Salary],
            Age as [row/@Age]
            from @source
            for xml path('')
            )
            --select @functionArgument as [@functionArgument]
            
            select * from readXml(@functionArgument)
            
            /* -------- Sample Output: --------
            Salary Age
            ----------- -----------
            10000 25
            15000 27
            12000 18
            15000 36
            16000 57
            17000 44
            18000 32
            19000 56
            25000 34
            7500 29
            */
            

            【讨论】:

            • 我不是要获取表格中的列数。
            • 如果您试图将它们限制在只有一列的表中,那么是的。
            • 我正在尝试传递一个仅包含一列的表,如果我传递一个包含多列的表,我希望它会引发错误。就像我将一个 int 传递给一个期望浮点数的 UDF
            • ^ 所以你说的是,如果你想这样做,你确实需要查询 syscolumns 表。否则你会怎么做?
            【解决方案8】:
                create table Project (ProjectId int, Description varchar(50));
                insert into Project values (1, 'Chase tail, change directions');
                insert into Project values (2, 'ping-pong ball in clothes dryer');
            
                create table ProjectResource (ProjectId int, ResourceId int, Name varchar(15));
                insert into ProjectResource values (1, 1, 'Adam');
                insert into ProjectResource values (1, 2, 'Kerry');
                insert into ProjectResource values (1, 3, 'Tom');
                insert into ProjectResource values (2, 4, 'David');
                insert into ProjectResource values (2, 5, 'Jeff');
            
            
                SELECT *, 
                  (SELECT Name + ' ' AS [text()] 
                   FROM ProjectResource pr 
                   WHERE pr.ProjectId = p.ProjectId 
                   FOR XML PATH ('')) 
                AS ResourceList 
                FROM Project p
            
            -- ProjectId    Description                        ResourceList
            -- 1            Chase tail, change directions      Adam Kerry Tom 
            -- 2            ping-pong ball in clothes dryer    David Jeff 
            

            【讨论】:

              【解决方案9】:

              以下内容将使您能够快速删除重复的空值并仅将有效的值作为列表返回。

              CREATE TABLE DuplicateTable (Col1 INT)
              INSERT INTO DuplicateTable
              SELECT 8
              UNION ALL
              SELECT 1--duplicate
              UNION ALL
              SELECT 2 --duplicate
              UNION ALL
              SELECT 1
              UNION ALL
              SELECT 3
              UNION ALL
              SELECT 4
              UNION ALL
              SELECT 5
              UNION 
              SELECT NULL
              GO
              
              WITH CTE (COl1,DuplicateCount)
              AS
              (
              SELECT COl1,
              ROW_NUMBER() OVER(PARTITION BY COl1 ORDER BY Col1) AS DuplicateCount
              FROM DuplicateTable
              WHERE (col1 IS NOT NULL) 
              )
              SELECT COl1
              FROM CTE
              WHERE DuplicateCount =1
              GO
              

              CTE 在 SQL 2005 中有效,然后您可以将值存储在临时表中并与您的函数一起使用。

              【讨论】:

                【解决方案10】:

                你可以这样做

                /* 创建用户定义的表类型 */

                CREATE TYPE StateMaster AS TABLE
                (
                 StateCode VARCHAR(2),
                 StateDescp VARCHAR(250)
                )
                GO
                

                /*创建以表为参数的函数*/

                CREATE FUNCTION TableValuedParameterExample(@TmpTable StateMaster READONLY)
                RETURNS  VARCHAR(250)
                AS
                BEGIN
                 DECLARE @StateDescp VARCHAR(250)
                 SELECT @StateDescp = StateDescp FROM @TmpTable
                 RETURN @StateDescp
                END
                GO
                

                /*创建以表为参数的存储过程*/

                CREATE PROCEDURE TableValuedParameterExample_SP
                (
                @TmpTable StateMaster READONLY
                )
                AS
                BEGIN
                 INSERT INTO StateMst 
                  SELECT * FROM @TmpTable
                END
                GO
                
                
                BEGIN
                /* DECLARE VARIABLE OF TABLE USER DEFINED TYPE */
                DECLARE @MyTable StateMaster
                
                /* INSERT DATA INTO TABLE TYPE */
                INSERT INTO @MyTable VALUES('11','AndhraPradesh')
                INSERT INTO @MyTable VALUES('12','Assam')
                
                /* EXECUTE STORED PROCEDURE */
                EXEC TableValuedParameterExample_SP @MyTable
                GO
                

                更多详情请查看此链接:http://sailajareddy-technical.blogspot.in/2012/09/passing-table-valued-parameter-to.html

                【讨论】:

                  猜你喜欢
                  • 2023-03-22
                  • 2015-09-11
                  • 2011-03-01
                  • 2018-06-03
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多