2013-08-20 72 views
6

Tôi đang viết một tập lệnh để truy vấn SELECT một cơ sở dữ liệu và phân tích thông qua ~ 33.000 bản ghi. Rất tiếc, tôi đang gặp phải sự cố ở giai đoạn cursor.fetchone()/cursor.fetchall().Python chậm trên fetchone, treo trên fetchall

đầu tiên tôi đã cố gắng lặp lại thông qua con trỏ một kỷ lục tại một thời điểm như vậy:

# Run through every record, extract the kanji, then query for FK and weight 
printStatus("Starting weight calculations") 
while True: 
    # Get the next row in the cursor 
    row = cursor.fetchone() 
    if row == None: 
     break 

    # TODO: Determine if there's any kanji in row[2] 

    weight = float((row[3] + row[4]))/2 
    printStatus("Weight: " + str(weight)) 

Dựa trên kết quả của printStatus (nó in ra một dấu thời gian cộng với bất cứ chuỗi được truyền cho nó), kịch bản mất khoảng 1 giây để xử lý mỗi hàng. Điều này khiến tôi tin rằng truy vấn đã được chạy lại mỗi lần vòng lặp lặp lại (với LIMIT 1 hoặc cái gì đó), vì mất ~ 1 giây cho cùng một truy vấn chạy một lần trong một cái gì đó như SQLiteStudio [i] và [/ i] trả về tất cả 33.000 hàng. Tôi tính rằng, với tốc độ đó, sẽ mất khoảng 7 giờ để vượt qua tất cả 33.000 bản ghi.

Thay vì ngồi thông qua đó, tôi cố gắng sử dụng cursor.fetchall() thay vì:

results = cursor.fetchall() 

# Run through every record, extract the kanji, then query for FK and weight 
printStatus("Starting weight calculations") 
for row in results: 
    # TODO: Determine if there's any kanji in row[2] 

    weight = float((row[3] + row[4]))/2 
    printStatus("Weight: " + str(weight)) 

Thật không may, Python thực thi bị nhốt ở mức 25% CPU và ~ 6MB bộ nhớ RAM khi nó đã đến cursor.fetchall() hàng. Tôi để lại kịch bản chạy trong ~ 10 phút, nhưng không có gì xảy ra cả.

Có ~ 33.000 hàng trả về (khoảng 5MB dữ liệu) quá nhiều để Python lấy cùng một lúc không? Tôi có bị kẹt lặp lại từng cái một không? Hay có điều gì tôi có thể làm để tăng tốc độ?

EDIT: Dưới đây là một số giao diện điều khiển đầu ra

12:56:26.019: Adding new column 'weight' and related index to r_ele 
12:56:26.019: Querying database 
12:56:28.079: Starting weight calculations 
12:56:28.079: Weight: 1.0 
12:56:28.079: Weight: 0.5 
12:56:28.080: Weight: 0.5 
12:56:28.338: Weight: 1.0 
12:56:28.339: Weight: 3.0 
12:56:28.843: Weight: 1.5 
12:56:28.844: Weight: 1.0 
12:56:28.844: Weight: 0.5 
12:56:28.844: Weight: 0.5 
12:56:28.845: Weight: 0.5 
12:56:29.351: Weight: 0.5 
12:56:29.855: Weight: 0.5 
12:56:29.856: Weight: 1.0 
12:56:30.371: Weight: 0.5 
12:56:30.885: Weight: 0.5 
12:56:31.146: Weight: 0.5 
12:56:31.650: Weight: 1.0 
12:56:32.432: Weight: 0.5 
12:56:32.951: Weight: 0.5 
12:56:32.951: Weight: 0.5 
12:56:32.952: Weight: 1.0 
12:56:33.454: Weight: 0.5 
12:56:33.455: Weight: 0.5 
12:56:33.455: Weight: 1.0 
12:56:33.716: Weight: 0.5 
12:56:33.716: Weight: 1.0 

Và đây là các truy vấn SQL:

//...snip (it wasn't the culprit)... 

Kết quả của giải thích kế hoạch QUERY từ SQLiteStudio:

0 0 0 SCAN TABLE r_ele AS re USING COVERING INDEX r_ele_fk (~500000 rows) 
0 0 0 EXECUTE CORRELATED SCALAR SUBQUERY 1 
1 0 0 SEARCH TABLE re_pri USING INDEX re_pri_fk (fk=?) (~10 rows) 
0 0 0 EXECUTE CORRELATED SCALAR SUBQUERY 2 
2 0 0 SEARCH TABLE ke_pri USING INDEX ke_pri_fk (fk=?) (~10 rows) 
2 0 0 EXECUTE CORRELATED SCALAR SUBQUERY 3 
3 0 0 SEARCH TABLE k_ele USING AUTOMATIC COVERING INDEX (value=?) (~7 rows) 
3 0 0 EXECUTE CORRELATED SCALAR SUBQUERY 4 
4 0 0 SEARCH TABLE k_ele USING COVERING INDEX idx_k_ele (fk=?) (~10 rows) 
0 0 0 EXECUTE CORRELATED SCALAR SUBQUERY 5 
5 0 0 SEARCH TABLE k_ele USING COVERING INDEX idx_k_ele (fk=?) (~10 rows) 
0 0 0 EXECUTE CORRELATED SCALAR SUBQUERY 6 
6 0 0 SEARCH TABLE re_pri USING INDEX re_pri_fk (fk=?) (~10 rows) 
0 0 0 EXECUTE CORRELATED SCALAR SUBQUERY 7 
7 0 0 SEARCH TABLE ke_pri USING INDEX ke_pri_fk (fk=?) (~10 rows) 
7 0 0 EXECUTE CORRELATED SCALAR SUBQUERY 8 
8 0 0 SEARCH TABLE k_ele USING AUTOMATIC COVERING INDEX (value=?) (~7 rows) 
8 0 0 EXECUTE CORRELATED SCALAR SUBQUERY 9 
9 0 0 SEARCH TABLE k_ele USING COVERING INDEX idx_k_ele (fk=?) (~10 rows) 
+7

Bạn đã thử lặp lại con trỏ: 'cho hàng trong con trỏ: ...'? –

+0

Không có cách nào mà 'fetchone' (hoặc lặp lại) có thể làm cho nó chạy lại truy vấn mỗi lần. Đối tượng 'cursor' nói chung thậm chí không _know_ truy vấn mà nó chạy. Vì vậy, bất kể vấn đề của bạn là gì, đó không phải là nó. – abarnert

+0

Ngoài ra, như một lưu ý phụ: sử dụng 'if row is None:', không phải 'if row == None:'. Trong hầu hết các trường hợp, nó không thực sự tạo ra bất kỳ sự khác biệt nào, nhưng nó thành ngữ hơn (và nó cũng nhanh hơn một chút, và trong những dịp hiếm hoi khi nó _does_ tạo nên sự khác biệt, nó sẽ là thứ bạn muốn). – abarnert

Trả lời

3

tính SQLite hồ sơ kết quả trên bay. fetchone chậm vì nó phải thực thi tất cả các truy vấn con cho mỗi bản ghi trong r_ele. fetchall thậm chí còn chậm hơn vì chỉ mất chừng nào bạn đã thực hiện fetchone cho tất cả các bản ghi.

SQLite 3.7.13 ước tính rằng tất cả tra cứu trên cột value sẽ chậm khủng khiếp và do đó tạo chỉ mục tạm thời cho truy vấn này. Bạn nên tạo một chỉ số vĩnh viễn để nó có thể được sử dụng bởi SQLite 3.6.21:

CREATE INDEX idx_k_ele_value ON k_ele(value); 

Nếu điều đó không giúp đỡ, cập nhật cho một Python với một phiên bản mới hơn SQLite, hoặc sử dụng một thư viện cơ sở dữ liệu với một phiên bản mới hơn Phiên bản SQLite được cài sẵn, chẳng hạn như APSW.

+0

Đây không phải là một vấn đề về Python, nó thiếu một chỉ số thích hợp. Sau khi tạo chỉ mục trên, Python đã nhai tất cả 33.000 bản ghi trong khoảng 2,5 giây và thời gian thực thi trong SQlite Studio đã giảm từ 1,75 giây xuống còn 0,0006 giây. – IAmKale