【发布时间】:2018-06-12 08:38:39
【问题描述】:
设置
MWE:我在 SQL Server 中有一个这样的表
CREATE TABLE dbo.MyTable(
order_id INT IDENTITY(1,1),
column2 DECIMAL,
column3 INT
PRIMARY KEY(order_id)
)
我正在使用 pyodbc 将一些数据以 pandas.DataFrame 的形式插入到表中。我正在使用以下数据:
column2 column3
0 1.23 5
1 4.95 9
2 6.79 10
我使用
创建了这个示例数据框 data = pd.DataFrame({'column2':[1.23, 4.95, 6.79], 'column3':[5,9,10]})
我使用下面的语句插入数据
stmt = "INSERT INTO dbo.MyTable(column2, column3) OUTPUT Inserted.order_id VALUES (?, ?)"
问题
这是我用来插入所有内容并返回值的代码:
# Set up connection and create cursor
conn_string = "DRIVER={MyDriver};SERVER=MyServer;DATABASE=MyDb;UID=MyUID;PWD=MyPWD"
cnxn = pyodbc.connect(conn_string)
cnxn.autocommit = False
cursor = cnxn.cursor()
cursor.fast_executemany = True
# Upload data
cursor.executemany(stmt, data.values.tolist())
# Process the result
try:
first_result = cursor.fetchall()
except pyodbc.ProgrammingError:
first_result = None
result_sets = []
while cursor.nextset():
result_sets.append(cursor.fetchall())
all_inserted_ids = np.array(result_sets).flatten()
但是,我没有得到我应该得到的所有 id!例如,假设表中没有数据,我不会得到 p>
all_inserted_ids = np.array([1, 2, 3])
但我只会得到
all_inserted_ids = np.array([2, 3])
这意味着我在某处丢失了第一个 id!
请注意first_result 永远不会起作用。它总是抛出以下内容:
pyodbc.ProgrammingError: No results. Previous SQL was not a query.
我也尝试过使用 cursor.fetchone()、cursor.fetchone()[0] 或 cursor.fetchval(),但他们也遇到了同样的错误。
我尝试过但不起作用的方法
1) 添加“SET NOCOUNT ON”
我尝试使用与问题中相同的代码,但使用了
stmt =
"""
SET NOCOUNT ON;
INSERT INTO dbo.MyTable(column2, column3)
OUTPUT Inserted.order_id
VALUES (?, ?)
"""
输出是[1, 2],所以我错过了3。
2) 添加“SET NOCOUNT ON”并将输出插入到表变量中
我使用了以下语句:
stmt =
"""
SET NOCOUNT ON;
DECLARE @NEWID TABLE(ID INT);
INSERT INTO dbo.MyTable(column2, column3)
OUTPUT Inserted.order_id INTO @NEWID(ID)
VALUES (?, ?)
SELECT ID FROM @NEWID
"""
这同样不起作用,因为我只获得了 '[2, 3]' 但没有获得 '1'。
3) 选择@@IDENTITY
我使用了以下语句:
stmt =
"""
INSERT INTO dbo.MyTable(column2, column3)
OUTPUT Inserted.order_id
VALUES (?, ?)
SELECT @@IDENTITY
"""
但它没有工作,因为我获得了array([Decimal('1'), 2, Decimal('2'), 3, Decimal('3')]
4) 在 SET NOCOUNT ON 的情况下选择@@IDENTITY
我用过
stmt =
"""
SET NOCOUNT ON
INSERT INTO dbo.MyTable(column2, column3)
OUTPUT Inserted.order_id
VALUES (?, ?);
SELECT @@IDENTITY
"""
但我又收到了array([Decimal('1'), 2, Decimal('2'), 3, Decimal('3')], dtype=object)。
5) 在不使用 OUTPUT 的情况下选择 @@IDENTITY
我用过:
stmt =
"""
INSERT INTO dbo.MyTable(column2, column3)
VALUES (?, ?);
SELECT @@IDENTITY
"""
但我得到了[Decimal('2') Decimal('3')]
6) 选择@@IDENTITY 而不使用 OUTPUT 但 SET NOCOUNT ON
我用过:
stmt =
"""
SET NOCOUNT ON
INSERT INTO dbo.MyTable(column2, column3)
VALUES (?, ?);
SELECT @@IDENTITY
"""
但我又得到了:[Decimal('2') Decimal('3')]
一种可能的解决方法,这确实很糟糕,但确实有效
一种可能的方法是创建一个新表,我们将在其中存储 ID,并在完成后截断它。这太可怕了,但我找不到任何其他解决方案..
创建表:
CREATE TABLE NEWID(
ID INT
PRIMARY KEY (ID)
)
接下来是完整的代码:
import pyodbc
import pandas as pd
import numpy as np
# Connect
conn_string = """
DRIVER={MYDRIVER};
SERVER=MYSERVER;
DATABASE=DB;
UID=USER;
PWD=PWD
"""
cnxn = pyodbc.connect(conn_string)
cnxn.autocommit = False
cursor = cnxn.cursor()
cursor.fast_executemany = True
# Data, Statement, Execution
data = pd.DataFrame({'column2': [1.23, 4.95, 6.79], 'column3': [5, 9, 10]})
stmt = """
INSERT INTO dbo.MyTable(column2, column3)
OUTPUT Inserted.order_id INTO NEWID(ID)
VALUES (?, ?);
"""
cursor.executemany(stmt, data.values.tolist())
cursor.execute("SELECT ID FROM NEWID;")
# Get stuff
try:
first_result = cursor.fetchall()
except pyodbc.ProgrammingError:
first_result = None
result_sets = []
while cursor.nextset():
result_sets.append(cursor.fetchall())
all_inserted_ids = np.array(result_sets).flatten()
print('First result: ', first_result)
print('All IDs: ', all_inserted_ids)
cursor.commit()
# Remember to truncate the table for next use
cursor.execute("TRUNCATE TABLE dbo.NEWID;", [])
cursor.commit()
这将返回
First result: [(1, ), (2, ), (3, )]
All IDs: []
所以我们只保留第一个结果。
【问题讨论】:
-
看这里:stackoverflow.com/questions/30058957/… 似乎添加“SET NOCOUNT ON”可以解决它。然而事实并非如此。如果您在上面添加
stmt运行我的最小工作示例,您将得到all_inserted_ids = np.array([1, 2]),所以现在我失去了最后一个ID! -
我在这里读到的另一种解决方案:stackoverflow.com/questions/47188976/… 将使用以下语句
stmt = """SET NOCOUNT ON; DECLARE @NEWID TABLE(ID INT); INSERT INTO dbo.MyTable(column2, column3) OUTPUT Inserted.order_id INTO @NEWID(ID) VALUES (?, ?) SELECT ID FROM @NEWID"""但是在这里我再次在第一个结果集和all_inserted_ids = np.array([2, 3])上获得错误。所以这也不起作用 -
第三种可能在这里阅读:stackoverflow.com/questions/44777348/… 说我可以使用“SELECT @@IDENTITY”。所以我修改了声明,现在我使用
stmt = """INSERT INTO dbo.MyTable(column2, column3) OUTPUT Inserted.order_id VALUES (?, ?) SELECT @@IDENTITY"""。这似乎有效!但是我仍然需要一些测试。但是,现在我得到了 first_result None 和all_inserted_ids = np.array([1, 2, 3])。 -
关于我的最后一条评论,我认为它正在“工作”,但只是因为一些错误聚集在一起使其以这种方式工作......所以它可能不是完全正确的解决方案。我还注意到,如果您省略
"OUTPUT Inserted.order_id"而只使用stmt = """INSERT INTO dbo.MyTable(column2, column3) VALUES (?, ?) SELECT @@IDENTITY""",那么您会遇到上述同样的问题,因为您会得到all_inserted_ids = np.array([2, 3]) -
实际上,使用“SELECT @@IDENTITY”是行不通的。我误读了输出。我得到的实际上是:实际上,这不起作用!我只得到
array([Decimal('1'), 2, Decimal('2'), 3, Decimal('3')], dtype=object)我误读了输出!
标签: python sql sql-server pyodbc