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を実行して正しい結果を得たので、DBには正しいデータがあると確信しています。

接続を不適切に閉じていないことを確認するために、接続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

私はある意味問題を理解しました。最後はばかげていた。競合状態でした!

これが私の実際のコードの編成方法です:


 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の内部動作について少し知って(ブラックボックスのようになっているはずですが)、次に同様の問題に遭遇したときに、このような競合状態について考えることは、私にとっての教訓です。

于 2012-12-20T00:26:59.947 に答える
1

これは実際には答えではありません。RBK とのチャットからすべての情報を集めて、潜在的な問題を除外しましたが、説明や解決策を思いつきませんでした。問題を見つけたり、何か他のことを考えたりすることができます。

それは明らかにこの行の何かです:

cur.execute("SELECT * FROM Products WHERE account_id = %d" % acc_id)

特に、322配置するとacc_idすべてが修正されるためです。(以下で証明されているように。)

実際には、その行には 2 つの問題があり、邪魔になる可能性があります。SQL インジェクション攻撃を回避し、エスケープ/変換などの正確性を確保し、効率を高めるために、文字列の書式設定 (および他の言語での同等のもの) ではなく、常に DB-API バインディングを使用する必要があります。また、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

acc_id繰り返し実行すると、最後の数字は 0 から 6 まで変化しますが、最初の数字は常に 10333です。そして、最初の 2 行がなくても、1 つのクエリが何らかの形で次のクエリに「感染」した場合に備えて、残りは同じように機能します。

したがって、 usingacc_idが using と異なる可能性はありません333。それでも、そうです。

チャット中のある時点で、製品からデバイス、322 から 333 に移動したようですが、上記のテストは間違いなく示されているとおりに行われ、異なる結果が返されました。

おそらく、彼は MySQLDb のバグのあるバージョンまたは正しくインストールされていないバージョンを使用しています。彼は、新しいバージョンまたは他の Python MySQL ライブラリの 1 つを探して、違いが生じるかどうかを確認するつもりです。

この時点での次善の推測は、RBK が技術的に洗練された悪戯の神をうっかり怒らせてしまったということですが、頭のてっぺんからそれらの 1 つを思い出すことさえできません。

于 2012-12-19T23:51:39.047 に答える