我正在尝试使用Python中的MySQLDB进行简单的提取。
我有2张桌子(帐户和产品)。我必须查看Accounts表,从中获取acc_id&使用它查询Products表。
Products表有10行以上。但是,当我运行此代码时,它会在0和0之间随机返回。每次运行时都有6行。
以下是代码段:
# Set up connection
con = mdb.connect('db.xxxxx.com', 'user', 'password', 'mydb')
# Create cursor
cur = con.cursor()
# Execute query
cur.execute("SELECT acc_id FROM Accounts WHERE ext_acc = '%s'" % account_num ) # account_num is alpha-numberic and is got from preceding part of the program
# A tuple is returned, so get the 0th item from it
acc_id = cur.fetchone()[0]
print "account_id = ", acc_id
# Close the cursor - I was not sure if I can reuse it
cur.close()
# Reopen the cursor
cur = con.cursor()
# Second query
cur.execute("SELECT * FROM Products WHERE account_id = %d" % acc_id)
keys = cur.fetchall()
print cur.rowcount # This prints incorrect row count
for key in keys: # Does not print all rows. Tried to directly print keys instead of iterating - same result :(
print key
# Closing the cursor & connection
cur.close()
con.close()
奇怪的是,我尝试使用调试器(Eclipse上的PyDev)逐步执行代码并正确获取所有行(存储在变量'keys'中的值以及控制台输出都是正确的)。
我确信我的数据库有正确的数据,因为我在MySQL控制台上运行了相同的SQL&得到了正确的结果。
为了确保我没有正确关闭连接,我尝试使用with con
而不是手动关闭连接,这是相同的结果。
我做过RTM但我找不到太多帮助我解决这个问题。
我哪里错了?
谢谢。
编辑:我现在注意到了另一件奇怪的事情。在线
cur.execute("SELECT * FROM Products WHERE account_id = %d" % acc_id)
,我对acc_id值进行了硬编码,即成功
cur.execute("SELECT * FROM Products WHERE account_id = %d" % 322)
并返回所有行
答案 0 :(得分:1)
这实际上不是一个答案,只是试图将与RBK聊天的所有信息聚集在一起,排除了一堆潜在的问题,但仍未提出解释或解决方案,希望其他人可以发现问题或考虑其他事情来尝试。
这显然属于这一行:
cur.execute("SELECT * FROM Products WHERE account_id = %d" % acc_id)
特别是因为用322
代替acc_id
可以解决所有问题。 (如下所述。)
这条线实际上有两个问题,可能会妨碍它。您始终希望使用DB-API绑定而不是字符串格式(以及任何其他语言中的等效项),以避免SQL注入攻击,转义/转换等的正确性以及效率。此外,DB-ABI绑定和字符串格式化都需要tuple
个参数,而不是单个参数。 (由于遗留原因,单个参数经常有效,但有时它不起作用,然后它只是混乱调试...最好不要这样做。)所以,这应该是:
cur.execute("SELECT * FROM Products WHERE account_id = %d", (acc_id,))
不幸的是,在聊天中讨论这个问题并尝试了很多东西后,我们无法在这里找到真正的错误。总结我们的尝试:
那么,我们尝试了:
cur.execute("SELECT COUNT(*) FROM Devices WHERE account_id = %s" , (333,))
print cur.fetchone()[0]
print 'account id =', acc_id
print type(acc_id)
cur.execute("SELECT COUNT(*) FROM Devices WHERE account_id = %s" , (acc_id,))
print cur.fetchone()[0]
输出结果为:
10
account id = 333
<type 'long'>
2
重复运行时,最后一个数字在0-6之间变化,而第一个数字始终为10.使用acc_id
时,使用333
的方法与使用acc_id
的方式不同,但实际上并非如此。如果一个查询以某种方式“感染”下一个查询,没有前两行,其余查询的工作方式相同。
因此,使用333
可能与使用{{1}}不同。然而,确实如此。
在聊天的某个时刻,我们显然已经从产品转移到了设备,从322移到了333,但无论如何,上面显示的测试肯定完全如图所示,并返回了不同的结果。
也许他有一个错误或安装严重的MySQLDb版本。他将尝试寻找更新版本或其他Python MySQL库之一,看看它是否有所作为。
我在这一点上的最佳猜测是,RBK无意中激怒了一些技术上复杂的恶作剧之神,但我甚至无法想到其中一个不在我头顶。
答案 1 :(得分:1)
我想出了问题。最后很傻。这是一场竞争条件!
这就是我的实际代码的组织方式:
Code Block 1
{code which calls an API which creates an entry in Accounts table &
Creates corresponding entries in Product table(10 entries)}
...
Code Block2
{The code I had posted in my question}
问题是API(在代码块1中调用)需要几秒钟才能在Product表中添加10个条目。
当我的代码(代码块2)运行了一个获取查询时,所有10行都没有被添加,因此被提取到0到6行之间(当时添加了多少)。
我做了解决这个问题的方法是让代码在我执行SQL查询之前休眠5秒钟:
Code Block 1
time.sleep(5)
Code Block 2
当我对acc_id进行硬编码时它起作用的原因是,我硬编码的acc_id来自一个宝贵的执行(每次运行返回一个新的acc_id)。 并且它在调试器中工作的原因是手动步进就像给它一个睡眠时间。
对我来说,了解一些关于API的内部工作(即使它们应该像黑盒子)并且考虑这样的竞争条件,下次我遇到类似问题时,这是一个教训。