3

我正在尝试在 Python 中使用 MySQLDB 进行简单的提取。

我有 2 张桌子(帐户和产品)。我必须查找 Accounts 表,从中获取 acc_id 并使用它查询 Products 表。

Products 表有 10 多行。但是当我运行这段代码时,每次运行它时它都会随机返回 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)单步执行代码,它正确地获取了所有行(存储在变量“键”中的值以及控制台输出都是正确的)。

我确信我的数据库有正确的数据,因为我在 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)并返回所有行

4

2 回答 2

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。 usingacc_id与 using 没有任何不同333,但它确实如此。万一一个查询以某种方式“感染”下一个查询,没有前两行,其余的工作方式相同。

因此, usingacc_id不可能与 using 不同333。然而,确实如此。

在聊天期间的某个时间点,我们显然从 Products 移到了 Devices,从 322 移到了 333,但无论如何,上面显示的测试肯定完全按照所示完成,并返回不同的结果。

也许他的 MySQLDb 版本有问题或安装不当。他将尝试寻找更新的版本,或其他 Python MySQL 库之一,看看它是否有所作为。

在这一点上,我的下一个最佳猜测是 RBK 无意中激怒了一些技术老练的恶作剧之神,但我什至无法想到其中的一个。

于 2012-12-19T23:51:39.047 回答
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 中调用)需要几秒钟才能将 10 个条目添加到 Product 表中。

当我的代码(代码块 2)运行提取查询时,没有添加所有 10 行,因此提取了 0 到 6 行之间的某个位置(当时添加了多少)。

为了解决这个问题,我在执行 SQL 查询之前让代码休眠了 5 秒:

Code Block 1
time.sleep(5)
Code Block 2

当我硬编码 acc_id 时它起作用的原因是,我硬编码的 acc_id 来自一个宝贵的执行(每次运行都返回一个新的 acc_id)。它在单步调试器中工作的原因是手动单步执行就像给它一个睡眠时间。

对我来说,了解一点 API 的内部工作原理(即使它们应该像一个黑匣子)并在下次遇到类似问题时考虑这样的竞争条件对我来说是一个教训。

于 2012-12-20T00:26:59.947 回答