You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

无存储过程权限时使用pyodbc执行多段SQL报错的解决方法

问题根因

你碰到的两类报错和存储过程权限无关,不需要建存储过程就能运行,问题来自三个明确的点:

  • SQL本身存在拼写错误:你写了三次变量声明关键字declare,后两次拼成了decalre,直接导致@dedupedemails、@esc_seq两个变量未被成功声明,触发「无法识别标量变量」报错。
  • 默认配置下SQL Server会为每个执行的语句返回「影响N行」的计数消息,pyodbc会把这类消息识别为空结果集,你直接调用fetchall()时拿到的是前面SET、DROP、SELECT INTO这类非查询语句返回的空结果,就会抛出「No results. Previous SQL was not a query.」错误。
  • Python代码存在笔误:cursor = cnxn()写法错误,连接对象不能直接作为函数调用,生成游标的正确写法是cursor = cnxn.cursor()。
修复方案

不需要申请数据库写权限,也不需要改动核心查询逻辑,按以下步骤调整即可:

  1. 修正SQL里的拼写错误,在SQL最开头加上SET NOCOUNT ON;,关闭执行过程中的行数计数返回,避免空结果集干扰pyodbc的结果识别。
  2. 修正Python侧连接、游标的写法,执行SQL后通过nextset()轮询跳过所有前置的非查询结果段,定位到最后需要的SELECT结果集再拉取数据。
  3. (可选兼容性优化)如果使用微软官方ODBC Driver for SQL Server,可以在连接字符串里加上MARS_Connection=Yes开启多活动结果集支持,多语句执行的稳定性更好。
修正后的可运行代码
import pyodbc

# 替换为你的实际连接字符串,可按需添加MARS_Connection=Yes配置
# 连接字符串示例:cnxn_str = "DRIVER={ODBC Driver 18 for SQL Server};SERVER=你的服务器地址;DATABASE=目标库名;UID=账号;PWD=密码;MARS_Connection=Yes;Encrypt=yes;TrustServerCertificate=no"
q = """
SET NOCOUNT ON;
set ANSI_WARNINGS OFF;
declare @html varchar(MAX)
declare @dedupedemails varchar(MAX) -- 修正原拼写错误
declare @esc_seq int -- 修正原拼写错误
    
set @esc_seq = 5;

if object_id('tempdb.dbo.##dedupemail', 'U') is not null
drop table ##dedupemail;
    
with sub1 as (
  select p.ID, p.LNAME, p.FNAME, p.EMAIL
  from dbo.person p
),
sub2 as (
  select
    s1.*,
    case when s1.ID = 1
    then 'Yes'
    else 'No'
    end as IS_ADMIN
  from sub1 s1
)
select distinct s2.* into ##dedupemail
from sub2 s2
where s2.IS_ADMIN = 'Yes'
    
set @html = 'abc';
    
select @dedupedemails = ltrim(stuff((
select '; ' + d.email 
from ##dedupemail d
for xml path('')), 1,1,''));
    
select @dedupedemails as EMAIL_LIST, @html as EMAIL_BODY
"""
    
try:
    cnxn = pyodbc.connect(cnxn_str)
    cursor = cnxn.cursor() # 修正原游标生成的笔误
    cursor.execute(q)
    # 轮询跳过所有前置非结果集,直到定位到最终查询结果
    while cursor.description is None:
        cursor.nextset()
    result = cursor.fetchall()
    print(result)
    cnxn.close()
except pyodbc.Error as e:
    print("Error caught: ", e)
补充注意事项
  • 尽量不要使用##开头的全局临时表,多用户同时运行脚本时会出现数据冲突,换成#开头的局部临时表即可,局部临时表会在连接断开后自动清理,没有并发问题。
  • 后续如果要增加更多段SQL逻辑,只要保留SET NOCOUNT ON配置和轮询nextset的逻辑,就不会再碰到同类报错,完全不需要依赖存储过程。

内容的提问来源于stack exchange,提问作者mmatick

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 01:45:42