简体   繁体   English

SQL Server临时表在pyodbc代码中不可用

[英]SQL Server temp table not available in pyodbc code

I'm running a series of complex sql queries in python and it involves temp tables. 我在python中运行了一系列复杂的sql查询,它涉及临时表。 My auto-commit method doesn't seem to be working to retrieve the data from the temp table. 我的自动提交方法似乎无法从临时表中检索数据。 The code snippet I'm using below and this is the output I'm getting: 我在下面使用的代码片段,这是我得到的输出:

testQuery="""
    Select top 10 *
    INTO #Temp1
    FROM Table1 t1
    JOIN Table2 t2
    on t1.key=t2.key
"""
    cnxn=pyodbc.connect(r'DRIVER={SQL Server Native Client 11.0};SERVER=server;DATABASE=DB;UID=UID;PWD=PWD')
    cnxn.autocommit=True
    cursor=cnxn.cursor()
    cursor.execute(testQuery)
    cursor.execute("""Select top 10 * from #Temp1""")
    <pyodbc.Cursor at 0x8f78930>


cnxn=pyodbc.connect(r'DRIVER={SQL Server Native Client 11.0};SERVER=server;DATABASE=DB;UID=UID;PWD=PWD')
cnxn.autocommit=True
cursor=cnxn.cursor()
cursor.execute(testQuery)
cursor.execute("""Select top 10 * from #Temp1""")

Even though this question has a "solution", ie, using global temp table instead of a local temp table, future readers might benefit from understanding why the problem happened in the first place. 即使这个问题有一个“解决方案”,即使用全局临时表而不是本地临时表,未来的读者可能会从首先理解问题发生的原因中受益。

A temporary table is automatically dropped when the last connection using said table is closed. 使用所述表的最后一个连接关闭时,将自动删除临时表。 The difference between a local temp table ( #Temp1 ) and a global temp table ( ##Temp1 ) is that the local temp table is only visible to the connection that created it, while an existing global temp table is available to any connection. 本地临时表( #Temp1 )和全局临时表( ##Temp1 )之间的区别在于本地临时表仅对创建它的连接可见,而现有的全局临时表可用于任何连接。

So the following code using a local temp table will fail ... 因此,使用本地临时表的以下代码将失败...

conn = pyodbc.connect(conn_str, autocommit=True)
crsr = conn.cursor()

sql = """\
SELECT 1 AS foo, 2 AS bar INTO #Temp1
"""
crsr.execute(sql)

conn = pyodbc.connect(conn_str, autocommit=True)
crsr = conn.cursor()

sql = """\
SELECT foo, bar FROM #Temp1
"""
crsr.execute(sql)
row = crsr.fetchone()
print(row)

... while the exact same code using a global temp table will succeed ... ...使用全局临时表的完全相同的代码将成功...

conn = pyodbc.connect(conn_str, autocommit=True)
crsr = conn.cursor()

sql = """\
SELECT 1 AS foo, 2 AS bar INTO ##Temp1
"""
crsr.execute(sql)

conn = pyodbc.connect(conn_str, autocommit=True)
crsr = conn.cursor()

sql = """\
SELECT foo, bar FROM ##Temp1
"""
crsr.execute(sql)
row = crsr.fetchone()
print(row)

... because the second pyodbc.connect call opens a separate second connection to the SQL Server without closing the first one. ...因为第二个pyodbc.connect调用打开一个单独的第二个连接到SQL Server而不关闭第一个连接。

The second connection cannot see the local temp table created by the first connection. 第二个连接无法看到第一个连接创建的本地临时表。 Note that the local temp table still exists because the first connection was never closed, but the second connection cannot see it. 请注意,本地临时表仍然存在,因为第一个连接从未关闭,但第二个连接无法看到它。

However, the second connection can see the global temp table because the first connection was never closed and therefore the global temp table continued to exist. 但是,第二个连接可以看到全局临时表,因为第一个连接从未关闭,因此全局临时表继续存在。

This type of behaviour has implications for ORMs and other mechanisms that may implicitly open and close connections to the server for each SQL statement that it executes. 这种类型的行为会影响ORM和其他机制,这些机制可能会为其执行的每个SQL语句隐式打开和关闭与服务器的连接。

I asked a colleague about this live and his suggestions worked. 我向一位同事询问了这次活动,他的建议也奏效了。 So I went and changed the testQuery to create a global temp table instead of a local (##Temp1 instead of #Temp1). 所以我去改变testQuery来创建一个全局临时表而不是本地(## Temp1而不是#Temp1)。 And went to sql server to test whether the temp table was actually being created-it was. 然后去sql server测试临时表是否真正被创建 - 它是。 So I isolated that the problem was the second cursor.execute statement. 所以我发现问题是第二个cursor.execute语句。 I modified the code to use pandas read_sql_query instead and it all worked out! 我修改了代码以使用pandas read_sql_query而且一切都解决了! Below is the code I used: 以下是我使用的代码:

testQuery="""
    Select top 10 *
    INTO ##Temp1
    FROM Table1 t1
    JOIN Table2 t2
    on t1.key=t2.key
"""
    cnxn=pyodbc.connect(r'DRIVER={SQL Server Native Client 11.0};SERVER=server;DATABASE=DB;UID=UID;PWD=PWD')
    cnxn.autocommit=True
    cursor=cnxn.cursor()
    cursor.execute(testQuery)
    cnxn.commit()
    query1="Select top 10 * from ##Temp1"
    data1=pd.read_sql_query(query1, cnxn)
    data1[:10]

Best way to go about this is to start your SQL query with: 最好的方法是使用以下命令启动SQL查询:

"SET NOCOUNT ON" “设置NOCOUNT ON”

This will output the desired data 这将输出所需的数据

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM