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 从函数或存储过程返回表的主要内容,如果未能解决你的问题,请参考以下文章