sql 从函数或存储过程返回表

Posted

技术标签:

【中文标题】sql 从函数或存储过程返回表【英文标题】:tsql returning a table from a function or store procedure 【发布时间】:2009-01-13 19:03:19 【问题描述】:

这更像是一个语法问题 我正在尝试编写一个可以嵌入到查询中的存储过程或函数,例如:

select * from MyBigProcOrFunction

我正在尝试定义一个表格函数,但我不明白该怎么做,因为我构建了 tmp 表来计算数据,然后才最终在 endtable 获得返回值。我对代码的标记是:

create function FnGetCompanyIdWithCategories()
returns table
as 
return 
(
select * into a #tempTable from stuff
'
etc
'
select companyid,Company_MarketSector from #tempTables 'the returning table data
)

如果我定义了一个函数,我如何将它作为一个表返回?

【问题讨论】:

直接使用sql命令select * from stuff,我们不能在函数中使用临时表,你的函数语法适合简单的sql查询 【参考方案1】:

您无法从 SQL 函数中访问临时表。您将需要如此本质地使用表变量:

ALTER FUNCTION FnGetCompanyIdWithCategories()
RETURNS  @rtnTable TABLE 
(
    -- columns returned by the function
    ID UNIQUEIDENTIFIER NOT NULL,
    Name nvarchar(255) NOT NULL
)
AS
BEGIN
DECLARE @TempTable table (id uniqueidentifier, name nvarchar(255)....)

insert into @myTable 
select from your stuff

--This select returns data
insert into @rtnTable
SELECT ID, name FROM @mytable 
return
END

编辑

基于对这个问题的 cmets,这是我的建议。您希望在另一个查询中加入过程或表值函数的结果。我将向您展示如何做到这一点,然后您选择您喜欢的那个。我将使用我的一个模式中的示例代码,但你应该能够适应它。两者都是首先使用存储过程的可行解决方案。

declare @table as table (id int, name nvarchar(50),templateid int,account nvarchar(50))

insert into @table
execute industry_getall

select * 
from @table 
inner join [user] 
    on account=[user].loginname

在这种情况下,您必须声明一个临时表或表变量来存储过程的结果。现在让我们看看如果您使用的是 UDF,您将如何做到这一点

select *
from fn_Industry_GetAll()
inner join [user] 
    on account=[user].loginname

正如您所见,UDF 更加简洁易读,并且由于您没有使用辅助临时表,因此性能可能会更好一些(性能完全是我的猜测)。

如果您要在许多其他地方重用您的函数/过程,我认为 UDF 是您的最佳选择。唯一的问题是您将不得不停止使用#Temp 表并使用表变量。除非您正在索引临时表,否则应该没有问题,并且您将使用 tempDb 较少,因为表变量保存在内存中。

【讨论】:

可能是因为他在描述一个 SP 比 UDF 更合适的场景。他在问使用哪个,SP 就足够了(而且更简单、更有凝聚力和更便携)。 假设他实际上并没有将参数传递给函数。 我认为 sproc 也是最好的我正在使用临时表来构建要返回的数据表,但是当我尝试“从 TheSproc 中选择 *”时,我得到一个“无效的对象名称” theSproc'" 您不要从 proc 中选择,我知道您可以执行 proc 并将结果放入临时表中。也使用表变量而不是临时表,临时表有一些情况,但通常表变量是更好的选择。 此外,如果您希望此功能可在多个领域重复使用,恕我直言,该功能更好。也就是说,如果这个函数将在许多 s-procs 中重用......否则你的 procs 将需要创建一个临时表来存储过程的结果。【参考方案2】:

将其用作模板

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author:      <Author,,Name>
-- Create date: <Create Date,,>
-- Description: <Description,,>
-- =============================================
CREATE FUNCTION <Table_Function_Name, sysname, FunctionName> 
(
    -- Add the parameters for the function here
    <@param1, sysname, @p1> <data_type_for_param1, , int>, 
    <@param2, sysname, @p2> <data_type_for_param2, , char>
)
RETURNS 
<@Table_Variable_Name, sysname, @Table_Var> TABLE 
(
    -- Add the column definitions for the TABLE variable here
    <Column_1, sysname, c1> <Data_Type_For_Column1, , int>, 
    <Column_2, sysname, c2> <Data_Type_For_Column2, , int>
)
AS
BEGIN
    -- Fill the table variable with the rows for your result set

    RETURN 
END
GO

这将定义您的功能。然后你就可以把它当作任何其他表来使用:

Select * from MyFunction(Param1, Param2, etc.)

【讨论】:

【参考方案3】:

您需要一种特殊类型的函数,称为table valued function. 下面是一个为数据仓库构建日期维度的有点冗长的示例。请注意定义表结构的returns 子句。您可以在表变量(本例中为@DateHierarchy)中插入任何您想要的内容,包括构建一个临时表并将内容复制到其中。

if object_id ('ods.uf_DateHierarchy') is not null
    drop function ods.uf_DateHierarchy
go

create function ods.uf_DateHierarchy (
       @DateFrom datetime
      ,@DateTo   datetime
) returns @DateHierarchy table (
        DateKey           datetime
       ,DisplayDate       varchar (20)
       ,SemanticDate      datetime
       ,MonthKey          int     
       ,DisplayMonth      varchar (10)
       ,FirstDayOfMonth   datetime
       ,QuarterKey        int
       ,DisplayQuarter    varchar (10)
       ,FirstDayOfQuarter datetime
       ,YearKey           int
       ,DisplayYear       varchar (10)
       ,FirstDayOfYear    datetime
) as begin
    declare @year            int
           ,@quarter         int
           ,@month           int
           ,@day             int
           ,@m1ofqtr         int
           ,@DisplayDate     varchar (20)
           ,@DisplayQuarter  varchar (10)
           ,@DisplayMonth    varchar (10)
           ,@DisplayYear     varchar (10)
           ,@today           datetime
           ,@MonthKey        int
           ,@QuarterKey      int
           ,@YearKey         int
           ,@SemanticDate    datetime
           ,@FirstOfMonth    datetime
           ,@FirstOfQuarter  datetime
           ,@FirstOfYear     datetime
           ,@MStr            varchar (2)
           ,@QStr            varchar (2)
           ,@Ystr            varchar (4)
           ,@DStr            varchar (2)
           ,@DateStr         varchar (10)


    -- === Previous ===================================================
    -- Special placeholder date of 1/1/1800 used to denote 'previous'
    -- so that naive date calculations sort and compare in a sensible
    -- order.
    --
    insert @DateHierarchy (
         DateKey
        ,DisplayDate
        ,SemanticDate
        ,MonthKey
        ,DisplayMonth
        ,FirstDayOfMonth
        ,QuarterKey
        ,DisplayQuarter
        ,FirstDayOfQuarter
        ,YearKey
        ,DisplayYear
        ,FirstDayOfYear
    ) values (
         '1800-01-01'
        ,'Previous'
        ,'1800-01-01'
        ,180001
        ,'Prev'
        ,'1800-01-01'
        ,18001
        ,'Prev'
        ,'1800-01-01'
        ,1800
        ,'Prev'
        ,'1800-01-01'
    )

    -- === Calendar Dates =============================================
    -- These are generated from the date range specified in the input
    -- parameters.
    --
    set @today = @Datefrom
    while @today <= @DateTo begin

        set @year = datepart (yyyy, @today)
        set @month = datepart (mm, @today)
        set @day = datepart (dd, @today)
        set @quarter = case when @month in (1,2,3) then 1
                            when @month in (4,5,6) then 2
                            when @month in (7,8,9) then 3
                            when @month in (10,11,12) then 4
                        end
        set @m1ofqtr = @quarter * 3 - 2 

        set @DisplayDate = left (convert (varchar, @today, 113), 11)
        set @SemanticDate = @today
        set @MonthKey = @year * 100 + @month
        set @DisplayMonth = substring (convert (varchar, @today, 113), 4, 8)
        set @Mstr = right ('0' + convert (varchar, @month), 2)
        set @Dstr = right ('0' + convert (varchar, @day), 2)
        set @Ystr = convert (varchar, @year)
        set @DateStr = @Ystr + '-' + @Mstr + '-01'
        set @FirstOfMonth = convert (datetime, @DateStr, 120)
        set @QuarterKey = @year * 10 + @quarter
        set @DisplayQuarter = 'Q' + convert (varchar, @quarter) + ' ' +
                                    convert (varchar, @year)
        set @QStr = right ('0' + convert (varchar, @m1ofqtr), 2)   
        set @DateStr = @Ystr + '-' + @Qstr + '-01' 
        set @FirstOfQuarter = convert (datetime, @DateStr, 120)
        set @YearKey = @year
        set @DisplayYear = convert (varchar, @year)
        set @DateStr = @Ystr + '-01-01'
        set @FirstOfYear = convert (datetime, @DateStr)


        insert @DateHierarchy (
             DateKey
            ,DisplayDate
            ,SemanticDate
            ,MonthKey
            ,DisplayMonth
            ,FirstDayOfMonth
            ,QuarterKey
            ,DisplayQuarter
            ,FirstDayOfQuarter
            ,YearKey
            ,DisplayYear
            ,FirstDayOfYear
        ) values (
             @today
            ,@DisplayDate
            ,@SemanticDate
            ,@Monthkey
            ,@DisplayMonth
            ,@FirstOfMonth
            ,@QuarterKey
            ,@DisplayQuarter
            ,@FirstOfQuarter
            ,@YearKey
            ,@DisplayYear
            ,@FirstOfYear
        )

        set @today = dateadd (dd, 1, @today)
    end

    -- === Specials ===================================================
    -- 'Ongoing', 'Error' and 'Not Recorded' set two years apart to
    -- avoid accidental collisions on 'Next Year' calculations.
    --
    insert @DateHierarchy (
         DateKey
        ,DisplayDate
        ,SemanticDate
        ,MonthKey
        ,DisplayMonth
        ,FirstDayOfMonth
        ,QuarterKey
        ,DisplayQuarter
        ,FirstDayOfQuarter
        ,YearKey
        ,DisplayYear
        ,FirstDayOfYear
    ) values (
         '9000-01-01'
        ,'Ongoing'
        ,'9000-01-01'
        ,900001
        ,'Ong.'
        ,'9000-01-01'
        ,90001
        ,'Ong.'
        ,'9000-01-01'
        ,9000
        ,'Ong.'
        ,'9000-01-01'
    )

    insert @DateHierarchy (
         DateKey
        ,DisplayDate
        ,SemanticDate
        ,MonthKey
        ,DisplayMonth
        ,FirstDayOfMonth
        ,QuarterKey
        ,DisplayQuarter
        ,FirstDayOfQuarter
        ,YearKey
        ,DisplayYear
        ,FirstDayOfYear
    ) values (
         '9100-01-01'
        ,'Error'
        ,null
        ,910001
        ,'Error'
        ,null
        ,91001
        ,'Error'
        ,null
        ,9100
        ,'Err'
        ,null
    )

    insert @DateHierarchy (
         DateKey
        ,DisplayDate
        ,SemanticDate
        ,MonthKey
        ,DisplayMonth
        ,FirstDayOfMonth
        ,QuarterKey
        ,DisplayQuarter
        ,FirstDayOfQuarter
        ,YearKey
        ,DisplayYear
        ,FirstDayOfYear
    ) values (
         '9200-01-01'
        ,'Not Recorded'
        ,null
        ,920001
        ,'N/R'
        ,null
        ,92001
        ,'N/R'
        ,null
        ,9200
        ,'N/R'
        ,null
    )

    return
end

go

【讨论】:

你认为他为什么需要这个而不是存储过程? 为什么你认为他需要这个而不是存储过程?如果没有任何进一步的问题,你能得出结论吗? 您实际上不能将存储过程嵌入到查询中 - 您能做的最好的事情就是从它选择的记录集中插入到表中。此外,他似乎非常特别地寻求有关如何编写表值函数的帮助。【参考方案4】:

据我所知,您不需要(不应该使用)功能。存储过程将从您包含的任何返回表格数据的 SELECT 语句中返回表格数据。

存储过程不使用 RETURN 语句。

 CREATE PROCEDURE name
 AS

 SELECT stuff INTO #temptbl1

 .......


 SELECT columns FROM #temptbln

【讨论】:

如果我“从 MySproc 中选择 *”,我会收到一条错误消息“无效的对象名称 'theSproc'” 就我而言,我还需要返回状态码和错误消息,这肯定是要走的路。基本上,返回的结果是表更新的副产品,因此不需要额外的选择。但在纯参数化复杂查询的情况下,TVF 听起来更合适。

以上是关于sql 从函数或存储过程返回表的主要内容,如果未能解决你的问题,请参考以下文章

存储过程和存储函数区别

如何从 T-SQL 存储过程返回表

ORACLE存储过程里可以声明过程和函数吗

存储过程和函数的区别

SQL插入,使用合并存储过程更新

如何将单个结果集从返回多个集的 SQL 存储过程保存到临时表?