简史: 我正在编写一个存储过程来支持遗留Web应用程序上的旧报告系统(使用SQL Server Reporting Services 2000)。 为了与原始实现样式保持一致,每个报告在数据库中都有一个专用存储过程,该过程执行返回“最终”数据集所需的所有查询,该数据集只能由报表服务器呈现。
由于此报告的业务要求,返回的数据集具有未知数量的列(取决于执行报告的用户,但可能有4-30列)。
在整个存储过程中,我保留一列UserID来跟踪用户的ID以执行其他查询。但最后,我做了类似的事情:
UPDATE #result
SET Name = ppl.LastName + ', ' + ppl.FirstName
FROM #result r
LEFT JOIN Users u ON u.id = r.userID
LEFT JOIN People ppl ON ppl.id = u.PersonID
ALTER TABLE #result
DROP COLUMN [UserID]
SELECT * FROM #result r ORDER BY Name
实际上,我将Name varchar列(在我执行某些透视逻辑时先前保留为NULL)设置为纯文本格式的所需名称格式。
完成后,我想删除UserID列,因为报表用户不应该看到它。
最后,返回的数据集有一列用户名,以及具有性能总数的任意数量的INT列。因此,我不能简单地排除UserID列,因为SQL不支持“SELECT * EXCEPT [UserID]”等。
有了这个(任何风格指针都受到赞赏,但不是这个问题的中心),这就是问题所在:
执行此存储过程时,出现执行错误:
Invalid column name 'userID'.
但是,如果我注释掉我的DROP COLUMN语句并保留UserID,则存储过程会正确执行。
发生了什么事?它当然看起来像语句执行乱序,并且在我可以使用它来设置名称字符串之前删除列!
[编辑1] 我之前定义了UserID(整个存储过程大约是200个谎言,大多数是不相关的逻辑,所以我会粘贴片段:
CREATE TABLE #result ([Name] NVARCHAR(256), [UserID] INT);
区分大小写不是问题,但确实指向了正确的行 - 有一个地方我有userID而不是UserID。现在我修复了案例,错误消息抱怨UserID。
我的“破损”存储过程在SQL Server 2008中也能正常运行 - 这可能是一个2000错误,或者我严重误解了SQL Server以前的工作方式。
谢谢大家的欢呼!
对于将来搜索此内容的任何人,我都会添加一个非常粗略的解决方法,以便在我们更新生产版本之前兼容2000:
DECLARE @workaroundTableName NVARCHAR(256), @workaroundQuery NVARCHAR(2000)
SET @workaroundQuery = 'SELECT [Name]';
DECLARE cur_workaround CURSOR FOR
SELECT COLUMN_NAME FROM [tempdb].INFORMATION_SCHEMA.Columns WHERE TABLE_NAME LIKE '#result%' AND COLUMN_NAME <> 'UserID'
OPEN cur_workaround;
FETCH NEXT FROM cur_workaround INTO @workaroundTableName
WHILE @@FETCH_STATUS = 0
BEGIN
SET @workaroundQuery = @workaroundQuery + ',[' + @workaroundTableName + ']'
FETCH NEXT FROM cur_workaround INTO @workaroundTableName
END
CLOSE cur_workaround;
DEALLOCATE cur_workaround;
SET @workaroundQuery = @workaroundQuery + ' FROM #result ORDER BY Name ASC'
EXEC(@workaroundQuery);
谢谢大家!
答案 0 :(得分:4)
更简单的解决方案是不删除列,但不要在最终选择中返回。
有很多理由说明为什么你不应该从程序中返回select *
。
编辑:我现在看到你必须这样做,因为列数不明。
根据错误消息,数据库区分大小写,因此userID
和UserID
之间存在差异吗?
答案 1 :(得分:1)
这对我有用:
CREATE TABLE #temp_t
(
myInt int,
myUser varchar(100)
)
INSERT INTO #temp_t(myInt, myUser) VALUES(1, 'Jon1')
INSERT INTO #temp_t(myInt, myUser) VALUES(2, 'Jon2')
INSERT INTO #temp_t(myInt, myUser) VALUES(3, 'Jon3')
INSERT INTO #temp_t(myInt, myUser) VALUES(4, 'Jon4')
ALTER TABLE #temp_t
DROP Column myUser
SELECT * FROM #temp_t
DROP TABLE #temp_t
它为您说无效栏。您是否检查了拼写并确保临时表中甚至存在该列。
答案 2 :(得分:0)
您可以尝试在BEGIN ... COMMIT事务中包装DROP COLUMN之前的所有内容。
答案 3 :(得分:0)
在编译时,SQL Server可能会将*扩展为完整的列列表。因此,在运行时,SQL Server执行“SELECT UserID,Name,LastName,FirstName,...”而不是“SELECT *”。将最终的SELECT动态组装成一个字符串,然后在存储过程结束时执行它可能是最佳选择。