调用存储过程通过OPENROWSET同步数据遇Msg7357权限/无列错误求助
咱们来搞定这个Msg 7357的错误,先理清楚问题根源,再给你几个靠谱的解决办法:
问题回顾
你原本的逻辑是传入当前数据库到存储过程,从临时表#clients_pom取数据做更新,但运行时碰到了这个错误:
Msg 7357, Level 16, State 2, Line 2 无法处理对象"SET FMTONLY OFF; SET NOCOUNT ON; EXEC WBANKA_KBBL2.dbo.sp_kbbl_WachLista_Priprema '2017-09-30', '2017-09-30', 0"。链接服务器"(null)"对应的OLE DB提供程序"SQLNCLI"提示:该对象要么无列,要么当前用户无权限。
你的执行代码是这样的:
DECLARE @SQL VARCHAR(MAX) DECLARE @Datum varchar(20) SET @Datum= '2017-09-30' IF OBJECT_ID('tempdb..##WL_Klijenti') IS NOT NULL DROP TABLE ##WL_Klijenti SELECT @SQL = ' SELECT * INTO ##WL_Klijenti FROM OPENROWSET (''SQLOLEDB'',''Server= (local);TRUSTED_CONNECTION=YES;'',''SET FMTONLY OFF; SET NOCOUNT ON; EXEC ' + DB_NAME()+'.dbo.sp_kbbl_WachLista_Priprema ''''' + @Datum + ''''', ''''' + @Datum + ''''', 0'') AS tbl' EXEC(@SQL) UPDATE C SET C.watchListStatus = '1' FROM #clients_pom AS C INNER JOIN ##WL_Klijenti AS WL ON WL.mbr = C.registrationNumber IF OBJECT_ID('tempdb..##WL_Klijenti') IS NOT NULL DROP TABLE ##WL_Klijenti
错误根源分析
我帮你拆解下这个问题的几个可能原因:
- OPENROWSET的列检测机制坑:当用OPENROWSET调用存储过程时,SQL Server会先去探测存储过程返回的列结构。虽然你加了
SET FMTONLY OFF,但这个设置可能没在正确的时机生效,导致SQL Server还是识别不到存储过程返回的列,于是抛出“无列”的错误。 - 权限问题:执行这段代码的账户,可能没有足够权限去访问
sp_kbbl_WachLista_Priprema存储过程,或者没法读取存储过程返回的结果集。 - 多此一举的OPENROWSET:你连接的是本地服务器
(local),完全没必要绕OPENROWSET,直接调用存储过程就行,这反而增加了复杂度和出错概率。
解决办法
方案1:直接调用存储过程(最推荐)
既然是本地环境,直接把存储过程的结果插入全局临时表就好,这是最简单最稳妥的方式:
DECLARE @Datum varchar(20) SET @Datum= '2017-09-30' IF OBJECT_ID('tempdb..##WL_Klijenti') IS NOT NULL DROP TABLE ##WL_Klijenti -- 先手动创建全局临时表,列结构必须和存储过程返回的结果完全匹配 CREATE TABLE ##WL_Klijenti ( mbr VARCHAR(50), -- 这里要替换成你存储过程实际返回的列名和类型 -- 把存储过程返回的其他列都列在这里,比如: clientName VARCHAR(100), statusCode INT -- 其他列按需补充 ) -- 直接插入存储过程的执行结果 INSERT INTO ##WL_Klijenti EXEC dbo.sp_kbbl_WachLista_Priprema @Datum, @Datum, 0 -- 执行你的更新逻辑 UPDATE C SET C.watchListStatus = '1' FROM #clients_pom AS C INNER JOIN ##WL_Klijenti AS WL ON WL.mbr = C.registrationNumber -- 清理临时表 IF OBJECT_ID('tempdb..##WL_Klijenti') IS NOT NULL DROP TABLE ##WL_Klijenti
划重点:临时表的列结构必须和sp_kbbl_WachLista_Priprema返回的结果集完全一致,包括列名、数据类型和长度,不然插入会报错。
方案2:调整OPENROWSET语句,让FMTONLY生效
如果你因为某些原因一定要用OPENROWSET,可以试试把SET FMTONLY OFF的位置调整下,或者在远程语句里用变量传递参数,让SQL Server能正确识别列:
DECLARE @SQL VARCHAR(MAX) DECLARE @Datum varchar(20) SET @Datum= '2017-09-30' IF OBJECT_ID('tempdb..##WL_Klijenti') IS NOT NULL DROP TABLE ##WL_Klijenti SELECT @SQL = ' SELECT * INTO ##WL_Klijenti FROM OPENROWSET (''SQLOLEDB'',''Server= (local);TRUSTED_CONNECTION=YES;'','' SET NOCOUNT ON; SET FMTONLY OFF; -- 用变量传递参数,避免字符串拼接的问题 DECLARE @dt1 VARCHAR(20) = ''''' + @Datum + '''''; DECLARE @dt2 VARCHAR(20) = ''''' + @Datum + '''''; EXEC ' + DB_NAME()+'.dbo.sp_kbbl_WachLista_Priprema @dt1, @dt2, 0 '') AS tbl' EXEC(@SQL) UPDATE C SET C.watchListStatus = '1' FROM #clients_pom AS C INNER JOIN ##WL_Klijenti AS WL ON WL.mbr = C.registrationNumber IF OBJECT_ID('tempdb..##WL_Klijenti') IS NOT NULL DROP TABLE ##WL_Klijenti
另外,要确保执行这段代码的账户对sp_kbbl_WachLista_Priprema有EXECUTE权限,同时能访问存储过程用到的所有底层表。
方案3:用EXEC AT替代OPENROWSET
如果必须用分布式查询的方式,可以用EXEC AT来调用存储过程并插入临时表:
DECLARE @Datum varchar(20) SET @Datum= '2017-09-30' IF OBJECT_ID('tempdb..##WL_Klijenti') IS NOT NULL DROP TABLE ##WL_Klijenti -- 同样要先创建匹配列结构的临时表 CREATE TABLE ##WL_Klijenti ( mbr VARCHAR(50), -- 其他列... ) DECLARE @ExecSQL NVARCHAR(MAX) = N' INSERT INTO ##WL_Klijenti EXEC ' + QUOTENAME(DB_NAME()) + N'.dbo.sp_kbbl_WachLista_Priprema @dt, @dt, 0' -- 用sp_executesql传递参数,避免注入风险 EXEC sp_executesql @ExecSQL, N'@dt VARCHAR(20)', @dt = @Datum UPDATE C SET C.watchListStatus = '1' FROM #clients_pom AS C INNER JOIN ##WL_Klijenti AS WL ON WL.mbr = C.registrationNumber IF OBJECT_ID('tempdb..##WL_Klijenti') IS NOT NULL DROP TABLE ##WL_Klijenti
总结
最推荐方案1,本地环境下直接调用存储过程是最简洁、最不容易出问题的方式,完全避开了分布式查询带来的权限和列检测坑。如果一定要用OPENROWSET,记得把SET FMTONLY OFF的位置调整好,同时确保账户权限足够。
内容的提问来源于stack exchange,提问作者unknown
相关产品推荐
相关产品推荐

