我有一些sps创建了一个包含各种字段的临时表#TempData
。在这些sp中,我将一些处理sp称为#TempData
。临时数据处理取决于sp输入参数。 SP代码是:
CREATE PROCEDURE [dbo].[tempdata_proc]
@ID int,
@NeedAvg tinyint = 0
AS
BEGIN
SET NOCOUNT ON;
if @NeedAvg = 1
Update #TempData set AvgValue = 1
Update #TempData set Value = -1;
END
然后,使用以下代码在外部sp中调用此sp:
USE [BN]
--GO
--DBCC FREEPROCCACHE;
GO
Create table #TempData
(
tele_time datetime
, Value float
--, AvgValue float
)
Create clustered index IXTemp on #TempData(tele_time);
insert into #TempData(tele_time, Value ) values( GETDATE(), 50 ); --sample data
declare
@ID int,
@UpdAvg int;
select
@ID = 1000,
@UpdAvg = 1
;
Exec dbo.tempdata_proc @ID, @UpdAvg ;
select * from #TempData;
drop table #TempData
此代码抛出错误:消息207,级别16,状态1,过程tempdata_proc,第8行:列名无效" AvgValue"。
但如果我只取消注释声明AvgValue float
- 一切正常。
问题:是否有任何解决方法让存储的proc代码保持不变并向优化器提供提示 - 跳过此步骤,因为sps传递后sp不会使用AvgValue列。
动态SQL不是一个受欢迎的解决方案BTW。根据现有的tsql代码(对此必需的大量修改),使用#TempData
tablename的替代方法是不可取的解决方案。
尝试了SET FMTONLY,tempdb.tempdb.sys.columns,尝试抓包装没有任何成功。
答案 0 :(得分:5)
处理存储过程的方式分为两部分 - 一部分,检查语法正确性,在创建或更改存储过程时执行。编译的剩余部分将推迟到执行存储过程的时间点。这称为Deferred Name Resolution,并允许存储过程包含对创建过程的时间点不存在的表(不仅限于临时表)的引用。
不幸的是,当涉及到执行过程的时间点时,它需要能够编译所有单个语句,并且此时它就是它会发现该表存在但该列不存在 - 所以此时它会生成错误并拒绝运行该过程。
遗憾的是,T-SQL语言是一种非常简单的编译器,在尝试执行编译时并没有考虑运行时控制流程。它没有分析控制流或试图推迟条件路径中的编译 - 它只是因为列没有(此时)存在而无法编译。
不幸的是,SQL Server内置的任何机制都没有控制这种行为 - 这是你得到的行为,任何解决它的行为都将被视为一种解决方法 - 正如已经证明的那样(注释中的有效建议 - 处理它的两种主要方法是使用动态SQL或确保临时表始终包含所需的所有列。
解决维护方面的问题的一种方法是,如果你对“临时表”的所有使用都应该包含所有列"是将列定义移动到单独的存储过程中,然后可以使用所有必需的列扩充临时表 - 如:
create procedure S_TT_Init
as
alter table #TT add Column1 int not null
alter table #TT add Column2 varchar(9) null
go
create procedure S_TT_Consumer
as
insert into #TT(Column1,Column2) values (9,'abc')
go
create procedure S_TT_User
as
create table #TT (tmp int null)
exec S_TT_Init
insert into #TT(Column1) values (8)
exec S_TT_Consumer
select Column1 from #TT
go
exec S_TT_User
产生输出8
和9
。您将临时表定义放在S_TT_Init
中,S_TT_Consumer
是多个存储过程调用的内部查询,S_TT_User
是一个此类存储过程的示例。
答案 1 :(得分:1)
最初使用列创建表。如果您使用SPROC输出填充TEMP表,只需将其设置为IDENTITY INT(1,1),这样列就会与您的输出对齐。
然后删除该列,稍后在SPROC中将其重新添加为适当的数据类型。
答案 2 :(得分:0)
除了动态SQL之外我唯一能够做到的事情就是使用对数据库结构的检查。
if exists (Select 1 From tempdb.sys.columns Where object_id=OBJECT_ID('tempdb.dbo.#TTT') and name = 'AvgValue')
begin
--do something AvgValue related
end
也许创建一个简单的函数,如果它始终是#TempTable,则只取表名和列,或者只取列;如果列存在,则返回1/0,从长远来看,我认为
if dbo.TempTableHasField('AvgValue')=1
begin
-- do something AvgValue related
end
EDIT1: Dang,你是对的,对不起,我确定我有......这.... :(让我的东西多一点