将表作为参数传递给sql server UDF

时间:2009-10-22 18:28:49

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

我想将一个表作为参数传递给一个缩放器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)

10 个答案:

答案 0 :(得分:68)

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

  

对于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并定义funcion,它接受这种类型的参数。示例函数:

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

参数必须是READONLY。示例用法:

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

答案 1 :(得分:15)

不幸的是,在SQL Server 2005中没有简单的方法.Lukasz的答案对于SQL Server 2008是正确的,而且该功能 long 已逾期

任何解决方案都涉及临时表,或者在UDF中传入xml / CSV和解析。示例:更改为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

答案 2 :(得分:4)

第1步:创建一个名为TableType的Type作为Table,它将接受一个包含一个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, ',')

答案 3 :(得分:2)

切换到底线,您希望将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,因为这是在函数中硬编码的。

答案 4 :(得分:1)

我一直在处理一个非常类似的问题并且已经能够实现我正在寻找的东西,即使我正在使用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'

*/

答案 5 :(得分:0)

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

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
*/

答案 6 :(得分:0)

    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 

答案 7 :(得分:0)

以下内容可让您快速删除重复的空值,并仅返回有效值作为列表。

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中有效,然后您可以将这些值存储在临时表中并将其与您的函数一起使用。

答案 8 :(得分:0)

将表作为存储过程中的参数

第1步:

CREATE TABLE [DBO] .T_EMPLOYEES_DETAILS ( Id int, 姓名nvarchar(50), 性别nvarchar(10), 薪水int )

第2步:

CREATE TYPE EmpInsertType AS TABLE ( Id int, 姓名nvarchar(50), 性别nvarchar(10), 薪水int )

第3步:

/ *必须在变量* /

的末尾添加READONLY关键字

CREATE PROC PRC_EmpInsertType @EmployeeInsertType EmpInsertType READONLY 如 开始     INSERT INTO [DBO] .T_EMPLOYEES_DETAILS     SELECT * FROM @EmployeeInsertType END

第4步:

DECLARE @EmployeeInsertType EmpInsertType

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

EXEC PRC_EmpInsertType @EmployeeInsertType

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

SELECT * FROM T_EMPLOYEES_DETAILS

<强>输出

1 John Male 50000

2 Praveen Male 60000

3 Chitra女45000

4 Mathy Female 6600

5 Sam Male 50000

答案 9 :(得分:-1)

你可以做这样的事情

/ * CREATE USER DEFINED TABLE TYPE * /

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