2010-09-24 39 views
13

Tôi thường viết các kịch bản Python nhỏ để lặp qua tất cả các hàng của bảng DB. Ví dụ: gửi tất cả cho tất cả người đăng ký một email.Cách tốt nhất để lặp qua tất cả các hàng trong bảng DB

tôi làm điều đó như thế này

conn = MySQLdb.connect(host = hst, user = usr, passwd = pw, db = db) 
cursor = conn.cursor() 
subscribers = cursor.execute("SELECT * FROM tbl_subscriber;") 

for subscriber in subscribers: 
... 

conn.close() 

Tôi tự hỏi nếu có một cách tốt hơn để làm nguyên nhân này có thể là tải mã của tôi hàng ngàn hàng vào bộ nhớ.

Tôi đã nghĩ về điều đó có thể được thực hiện tốt hơn với LIMIT. Có thể một cái gì đó như thế:

"SELECT * FROM tbl_subscriber LIMIT %d,%d;" % (actualLimit,steps)  

Làm cách nào tốt nhất để làm điều đó? Bạn sẽ làm như thế nào?

Trả lời

32

trừ khi bạn có BLOB trong đó, hàng nghìn hàng không phải là vấn đề. Bạn có biết rằng nó là?

Ngoài ra, tại sao mang xấu hổ về bản thân và cả gia đình của bạn bằng cách làm một cái gì đó giống như

"SELECT * FROM tbl_subscriber LIMIT %d,%d;" % (actualLimit,steps) 

khi con trỏ sẽ làm cho sự thay thế cho bạn trong một cách mà tránh SQL injection?

c.execute("SELECT * FROM tbl_subscriber LIMIT %i,%i;", (actualLimit,steps)) 
+1

+1 Tư vấn lành mạnh mà tôi bỏ qua bản thân mình – pyfunc

+9

@pyfunc. Tôi đã viết quá nhiều PHP trong cuộc đời tôi. Tôi không thể nhìn vào mã không an toàn mà không bị bẻ khóa. – aaronasterling

+1

Cảm ơn lời khuyên của bạn! Tôi không biết rằng thực hiện (...) có thể tránh SQL injection. Tuy nhiên nó là một kịch bản để sử dụng tại địa phương. – OemerA

2

Trước hết có lẽ bạn không cần Select * from ...

có lẽ nó đủ cho bạn chỉ để có được một số nội dung như: "SELECT email từ ..."

rằng sẽ giảm số lượng sử dụng bộ nhớ anyway :)

+0

Điểm tuyệt vời - 'SELECT *' Được coi là Có hại, hoặc ít nhất là lập trình lười biếng. – Piskvor

1

Bạn có vấn đề về bộ nhớ thực tế không? Khi lặp qua một con trỏ, các kết quả được tìm nạp cùng một lúc (việc triển khai DB-API của bạn có thể quyết định tìm nạp trước các kết quả, nhưng sau đó nó có thể cung cấp một hàm để đặt số kết quả được tìm nạp trước).

6

Hầu hết các kết nối MySQL dựa trên libmysqlclient sẽ đệm tất cả các kết quả trong bộ nhớ máy khách theo mặc định vì lý do hiệu suất (với giả thiết bạn sẽ không đọc kết quả lớn).

Khi bạn cần đọc kết quả lớn trong MySQLdb, bạn có thể sử dụng SSCursor để tránh đệm toàn bộ các tập kết quả lớn.

http://mysql-python.sourceforge.net/MySQLdb.html#using-and-extending

SSCursor - A "server-side" cursor. Like Cursor but uses CursorUseResultMixIn. Use only if you are dealing with potentially large result sets.

này giới thiệu các biến chứng mà bạn phải cẩn thận với. Nếu bạn không đọc tất cả các kết quả từ các con trỏ, một truy vấn thứ hai sẽ nâng cao một ProgrammingError:

>>> import MySQLdb 
>>> import MySQLdb.cursors 
>>> conn = MySQLdb.connect(read_default_file='~/.my.cnf') 
>>> curs = conn.cursor(MySQLdb.cursors.SSCursor) 
>>> curs.execute('SELECT * FROM big_table') 
18446744073709551615L 
>>> curs.fetchone() 
(1L, '2c57b425f0de896fcf5b2e2f28c93f66') 
>>> curs.execute('SELECT NOW()') 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "/usr/lib64/python2.6/site-packages/MySQLdb/cursors.py", line 173, in execute 
    self.errorhandler(self, exc, value) 
    File "/usr/lib64/python2.6/site-packages/MySQLdb/connections.py", line 36, in defaulterrorhandler 
    raise errorclass, errorvalue 
_mysql_exceptions.ProgrammingError: (2014, "Commands out of sync; you can't run this command now") 

Điều này có nghĩa bạn phải luôn luôn đọc tất cả mọi thứ từ con trỏ (và có thể nhiều resultsets) trước khi phát hành khác - MySQLdb sẽ không làm điều này cho bạn.

16

Bạn không phải sửa đổi truy vấn, bạn có thể sử dụng phương thức fetchmany của con trỏ.Đây là cách tôi làm điều đó:

def fetchsome(cursor, some=1000): 
    fetch = cursor.fetchmany 
    while True: 
     rows = fetch(some) 
     if not rows: break 
     for row in rows: 
      yield row 

Bằng cách này bạn có thể "CHỌN * TỪ tbl_subscriber;" nhưng bạn sẽ chỉ tìm nạp một số cùng một lúc.

+0

Trừ khi tôi nhầm, mysql sẽ tìm tất cả các hàng thỏa mãn truy vấn của bạn, nhưng giữ lại gửi chúng cho đến khi bạn hỏi. Truy vấn ban đầu của 'SELECT * FROM tbl_subscriber' ở trên sẽ thực hiện khủng khiếp cho một bảng với hàng tỷ hàng. 'LIMIT' có nghĩa là mysql chỉ tìm kiếm các hàng thỏa mãn yêu cầu của bạn và ngừng tìm kiếm. Các tài liệu trên fetchmany không phải là rất hữu ích, vì vậy có thể có một số phép thuật nhưng tôi nghi ngờ nó. –

Các vấn đề liên quan