あるプログラムにて
とあるテーブルにある1500件程度のデータをSELECTしようと以下のようなコードを書いたが、何故か1000件しか取得できなかった。
sample_before.py
from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base
schema = 'mysql://user_id:password@localhost/sample'
engine = create_engine(schema, echo=False, encoding='utf-8')
Base = declarative_base(engine)
class Model(Base):
__tablename__ = 'sample'
__table_args__ = {'autoload': True}
def __repr__(self):
return "<sample class>"
Session = sessionmaker(bind=engine)()
records = Session.query(Model)
print "RECORDS: %d" % records.count()
for record in records:
print 'ID: %d, NAME:%s, ADDRESS:%s, BIRTHDAY:%s'
上記のコードを実行すると、records.count()
が1000となる。
今まで1000件以上のデータを操作したことがなかったので、調査してみた。
まずは、使っている環境は以下の通り。
ミドルウェア関連
- MariaDB 5.5.37
Python関連
- Python 2.7.x
- MySQLdb 1.2.5
- SQLAlchemy 0.9.4
原因
調査の結果、MySQLdbのデフォルトカーソルがローカルのプログラム側にあることで制限がかかってしまうことがわかった。
対策
サーバー側にカーソルを持たせてしまえばいけそうだということで、以下のようにコードを修正。
sample_after.py
from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base
import MySQLdb.cursors
schema = 'mysql://user_id:password@localhost/sample'
engine = create_engine(schema, echo=False, encoding='utf-8',
connect_args={'cursorclass': MySQLdb.cursors.SSCursor})
Base = declarative_base(engine)
class Model(Base):
__tablename__ = 'sample'
__table_args__ = {'autoload': True}
def __repr__(self):
return "<sample class>"
conn = engine.connect()
Session = sessionmaker(bind=conn)()
records = Session.query(Model)
print "RECORDS: %d" % records.count()
for record in records:
print 'ID: %d, NAME:%s, ADDRESS:%s, BIRTHDAY:%s'
対策のポイント
- 5行目にMySQLdb.cursorsを追加
- 9行目でcreate_engineを作成する際に、カーソルをサーバ側へ持たせるようにconnect_argsへMySQLdb.cursors.SSCursorを設定
- 19、20行目を追加&修正
おわりに
MySQL(MariaDB)の別のドライバを使った場合はどうなるのか? については調査してないので分からないけども、多分connect_argsで設定してやれば動くのではないかと思われる。
他に良いやり方知ってる人がいたら教えて欲しいです。