从函数或存储过程返回表的T-SQL

67

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

select * from MyBigProcOrFunction

我正在尝试定义一个表格函数,但我不明白如何做到这一点,因为我需要在最终获得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 查询是正确的。 - Bharat Bhushan Sharma
4个回答

96

你不能从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

编辑

根据这个问题的评论,我有一个建议。你想在另一个查询中连接存储过程或表值函数的结果。我将向你展示如何实现它,然后你可以选择喜欢的方法。我将使用我架构中的示例代码,但你应该能够适应它。两种解决方案都可行,首先是使用存储过程。

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更少,因为表变量存储在内存中。


可能是因为他在描述一个场景,其中存储过程比用户定义函数更合适。他在询问应该使用哪个,而存储过程已经足够(而且更简单、更一致、更可移植)。 - dkretz
假设他实际上没有向函数传递参数。 - JoshBerke
我认为存储过程也是最好的选择,我使用临时表来构建要返回的数据表,但是当我尝试“select * from TheSproc”时,会出现“无效对象名称'theSproc'”的错误提示。 - thiswayup
你不需要从存储过程中选择数据,我知道你可以执行一个存储过程并将结果放入临时表中。此外,使用表变量而不是临时表,在某些情况下需要用到临时表,但通常表变量是更好的选择。 - JoshBerke
如果您希望在多个区域重复使用此功能,则使用函数更好,我认为。也就是说,如果此函数将在许多存储过程中重复使用...否则,您的存储过程将需要创建一个临时表来存储过程的结果。 - JoshBerke
我想做的是类似于“select * from TheSproc s join AnotherTable a on a.id=s.id”的操作,或者说我想实现的只能通过表值函数完成吗? - thiswayup

14

使用此内容作为模板

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.)

9
你需要一种特殊类型的函数,称为表值函数。以下是一个有点啰嗦的示例,用于构建数据仓库的日期维度。请注意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

你认为他为什么需要这个而不是存储过程? - dkretz
为什么你认为他需要这个而不是存储过程?你能在没有进一步询问的情况下得出结论吗? - dkretz
1
你实际上不能将存储过程嵌入到查询中,最好的方法是从由其选择的记录集中插入到表中。此外,他似乎非常明确地要求帮助编写一个表值函数。 - ConcernedOfTunbridgeWells

2
据我所知,您不需要(也不应该)使用函数。存储过程将返回任何包含返回表格数据的 SELECT 语句的表格数据。
存储过程不使用 RETURN 语句。
 CREATE PROCEDURE name
 AS

 SELECT stuff INTO #temptbl1

 .......


 SELECT columns FROM #temptbln

4
如果我执行“select * from MySproc”,会出现一个错误,指示“无效的对象名称'theSproc'”。 - thiswayup
在我的情况下,我还需要一个返回的状态码和错误消息,这肯定是正确的方法。基本上,返回的结果是表更新的副产品,因此不需要额外的选择。但是,在纯参数化复杂查询的情况下,TVF 似乎更加合适。 - Ben

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