SQLite
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> SQLite

Cảnh báo trong Python và SQLite

SQLite là một cơ sở dữ liệu quan hệ, phổ biến mà bạn nhúng vào ứng dụng của mình. Python đi kèm với các ràng buộc chính thức với SQLite. Bài viết này kiểm tra những lưu ý khi sử dụng SQLite trong Python. Nó trình bày các sự cố mà các phiên bản khác nhau của thư viện SQLite được liên kết có thể gây ra, cách datetime các đối tượng không được lưu trữ đúng cách và cách bạn cần hết sức cẩn thận khi dựa vào with connection của Python trình quản lý ngữ cảnh để xác nhận dữ liệu của bạn.

Giới thiệu

SQLite là một hệ thống cơ sở dữ liệu quan hệ (DB) phổ biến . Không giống như những người anh em lớn hơn, dựa trên máy chủ, chẳng hạn như MySQL, SQLite có thể được nhúng vào ứng dụng của bạn dưới dạng thư viện . Python chính thức hỗ trợ SQLite thông qua các ràng buộc (tài liệu chính thức). Tuy nhiên, làm việc với những ràng buộc đó không phải lúc nào cũng đơn giản. Ngoài những lưu ý chung về SQLite mà tôi đã thảo luận trước đây, có một số vấn đề cụ thể về Python mà chúng tôi sẽ xem xét trong bài viết này .

Phiên bản không tương thích với mục tiêu triển khai

Khá phổ biến là các nhà phát triển tạo và kiểm tra mã trên một máy (rất) khác với máy mà mã được triển khai, về hệ điều hành (OS) và phần cứng. Điều này gây ra ba loại vấn đề:

  • Ứng dụng hoạt động khác nhau do sự khác biệt về hệ điều hành hoặc phần cứng . Ví dụ:bạn có thể gặp sự cố về hiệu suất khi máy đích có ít bộ nhớ hơn máy của bạn. Hoặc SQLite có thể thực thi một số hoạt động trên một hệ điều hành chậm hơn so với các hệ điều hành khác vì các API hệ điều hành cấp thấp cơ bản mà nó sử dụng là khác nhau.
  • Phiên bản SQLite về mục tiêu triển khai khác với phiên bản của máy nhà phát triển . Điều này có thể gây ra sự cố theo cả hai hướng, vì các tính năng mới được thêm vào (và hành vi thay đổi) theo thời gian, hãy xem bảng thay đổi chính thức. Ví dụ:phiên bản SQLite được triển khai lỗi thời có thể thiếu các tính năng hoạt động tốt trong quá trình phát triển. Ngoài ra, phiên bản SQLite mới hơn trong triển khai có thể hoạt động khác với phiên bản cũ hơn mà bạn sử dụng trên máy phát triển của mình, ví dụ:khi nhóm SQLite thay đổi một số giá trị mặc định.
  • Các liên kết Python của SQLite hoặc thư viện C, có thể bị thiếu hoàn toàn trên mục tiêu triển khai . Đây là Linux -vấn đề phân phối cụ thể . Các bản phân phối Windows và macOS chính thức sẽ chứa một gói phiên bản của thư viện SQLite C. Trên Linux, thư viện SQLite là một gói riêng biệt. Nếu bạn tự biên dịch Python, ví dụ:bởi vì bạn sử dụng Debian / Raspbian / etc. phân phối có các phiên bản tính năng cổ, make của Python xây dựng tập lệnh sẽ chỉ xây dựng các liên kết SQLite của Python nếu thư viện SQLite C đã cài đặt đã được phát hiện trong quá trình biên dịch của Python . Nếu bạn tự biên dịch lại Python như vậy, thì bạn nên đảm bảo rằng thư viện SQLite C đã cài đặt là gần đây . Điều này, một lần nữa, không phải là trường hợp của Debian, v.v. khi cài đặt SQLite qua apt , vì vậy bạn cũng có thể phải tự xây dựng và cài đặt SQLite, trước đó để xây dựng Python.

Để tìm ra phiên bản nào của thư viện SQLite C được trình thông dịch Python của bạn sử dụng, hãy chạy lệnh sau:

python3 -c "import sqlite3; print(sqlite3.sqlite_version)"Code language: Bash (bash)

Thay thế sqlite3.sqlite_version với sqlite3.version sẽ cung cấp cho bạn phiên bản của Python’s SQLite ràng buộc .

Cập nhật thư viện SQLite C cơ bản

Nếu bạn muốn kiếm lợi từ các tính năng hoặc bản sửa lỗi của phiên bản SQLite mới nhất, thì bạn là người may mắn. Thư viện SQLite C thường được liên kết tại thời điểm chạy và do đó có thể được thay thế mà không có bất kỳ thay đổi nào đối với trình thông dịch Python đã cài đặt của bạn. Các bước cụ thể phụ thuộc vào hệ điều hành của bạn (được thử nghiệm cho Python 3.6+):

1) Windows: Tải xuống tệp nhị phân được biên dịch trước x86 hoặc x64 từ trang tải xuống SQLite và thay thế sqlite3.dll tệp được tìm thấy trong DLLs thư mục cài đặt Python của bạn với thư mục bạn vừa tải xuống.

2) Linux: từ trang tải xuống SQLite lấy autoconf nguồn, giải nén kho lưu trữ và chạy ./configure && make && make install sẽ cài đặt thư viện vào /usr/local/lib theo mặc định.
Sau đó, thêm dòng export LD_LIBRARY_PATH=/usr/local/lib ở đầu tập lệnh shell bắt đầu tập lệnh Python của bạn, điều này buộc trình thông dịch Python của bạn sử dụng thư viện tự tạo.

3) macOS: từ phân tích của tôi, có vẻ như thư viện SQLite C được biên dịch thành các ràng buộc của Python binary (_sqlite3.cpython-36m-darwin.so ). Nếu bạn muốn thay thế nó, bạn có thể sẽ cần lấy mã nguồn Python phù hợp với cài đặt Python đã cài đặt của bạn (ví dụ:3.7.6 hoặc bất kỳ phiên bản nào bạn sử dụng). Biên dịch Python từ nguồn, sử dụng tập lệnh xây dựng macOS. Tập lệnh này bao gồm tải xuống và xây dựng thư viện C của SQLite, vì vậy hãy đảm bảo chỉnh sửa tập lệnh để tham chiếu phiên bản SQLite mới nhất. Cuối cùng, sử dụng tệp ràng buộc đã biên dịch (ví dụ:_sqlite3.cpython-37m-darwin.so ), để thay thế cái lỗi thời.

Làm việc với datetime nhận biết múi giờ đối tượng

Hầu hết các nhà phát triển Python thường sử dụng datetime đối tượng khi làm việc với dấu thời gian. Có ngây thơ datetime các đối tượng không biết về múi giờ của chúng và không ngây thơ những cái, là múi giờ- nhận biết . Ai cũng biết rằng datetime của Python mô-đun khá kỳ quặc, khiến việc tạo datetime.datetime nhận biết múi giờ thậm chí còn khó khăn các đối tượng. Ví dụ:lệnh gọi datetime.datetime.utcnow() tạo ra một ngây thơ đối tượng, phản trực quan đối với các nhà phát triển mới sử dụng datetime API, mong đợi Python sử dụng múi giờ UTC! Thư viện của bên thứ ba, chẳng hạn như python-dateutil, hỗ trợ tác vụ này. Để tạo đối tượng nhận biết múi giờ, bạn có thể sử dụng mã như sau:

from dateutil.tz import tzutc
import datetime
timezone_aware_dt = datetime.datetime.now(tzutc())Code language: Python (python)

Thật không may, tài liệu Python chính thức của sqlite3 mô-đun bị sai lệch khi nói đến việc xử lý dấu thời gian. Như được mô tả ở đây, datetime các đối tượng được tự động chuyển đổi khi sử dụng PARSE_DECLTYPES (và khai báo TIMESTAMP cột). Mặc dù điều này đúng về mặt kỹ thuật, chuyển đổi sẽ mất múi giờ thông tin ! Do đó, nếu bạn thực sự đang sử dụng múi giờ- nhận biết datetime.datetime đối tượng, bạn phải đăng ký trình chuyển đổi của riêng mình , giữ lại thông tin múi giờ, như sau:

def convert_timestamp_to_tzaware(timestamp: bytes) -> datetime.datetime:
    # sqlite3 provides the timestamp as byte-string
    return dateutil.parser.parse(timestamp.decode("utf-8"))
 
def convert_timestamp_to_sqlite(dt: datetime.datetime) -> str:
    return dt.isoformat()  # includes the timezone information at the end of the string
 
sqlite3.register_converter("timestamp", convert_timestamp_to_tzaware)
sqlite3.register_adapter(datetime.datetime, convert_timestamp_to_sqlite)Code language: Python (python)

Như bạn có thể thấy, dấu thời gian chỉ được lưu trữ dưới dạng TEXT đến cuối cùng. Không có kiểu dữ liệu “ngày tháng” hoặc “ngày giờ” thực trong SQLite.

Giao dịch và tự động cam kết

sqlite3 của Python mô-đun không tự động xác nhận dữ liệu được sửa đổi bởi các truy vấn của bạn . Khi bạn thực hiện các truy vấn bằng cách nào đó thay đổi cơ sở dữ liệu, bạn phải phát hành một COMMIT rõ ràng tuyên bố hoặc bạn sử dụng kết nối làm trình quản lý ngữ cảnh đối tượng, như được hiển thị trong ví dụ sau:

with connection:  # this uses the connection as context manager
    # do something with it, e.g.
    connection.execute("SOME QUERY")Code language: Python (python)

Khi khối trên đã được thoát, sqlite3 ngầm gọi connection.commit() , nhưng chỉ làm như vậy nếu giao dịch đang diễn ra . Các câu lệnh DML (Ngôn ngữ sửa đổi dữ liệu) tự động bắt đầu một giao dịch, nhưng các truy vấn liên quan đến DROP hoặc CREATE TABLE / INDEX thì không, bởi vì chúng không được tính là DML theo tài liệu. Điều này là phản trực quan, bởi vì những tuyên bố này rõ ràng có sửa đổi dữ liệu.

Do đó, nếu bạn chạy bất kỳ DROP nào hoặc CREATE TABLE / INDEX các câu lệnh bên trong trình quản lý ngữ cảnh, nên thực thi một cách rõ ràng BEGIN TRANSACTION tuyên bố đầu tiên , để trình quản lý ngữ cảnh thực sự gọi connection.commit() cho bạn.

Xử lý số nguyên 64 bit

Trong một bài viết trước, tôi đã thảo luận rằng SQLite có vấn đề với các số nguyên lớn nhỏ hơn -2^63 , hoặc lớn hơn hoặc bằng 2^63 . Nếu bạn cố gắng sử dụng chúng trong các tham số truy vấn (với ? biểu tượng), sqlite3 của Python mô-đun sẽ tạo ra một OverflowError: Python int too large to convert to SQLite INTEGER , bảo vệ bạn khỏi việc vô tình mất dữ liệu.

Để xử lý đúng cách các số nguyên rất lớn, bạn phải:

  1. Sử dụng TEXT nhập cho cột bảng tương ứng,
  2. Chuyển đổi số thành str đã có trong Python , trước khi sử dụng nó như một tham số.
  3. Chuyển đổi các chuỗi trở lại int trong Python, khi SELECT nhập dữ liệu

Kết luận

sqlite3 chính thức của Python mô-đun là một liên kết tuyệt vời với SQLite. Tuy nhiên, các nhà phát triển mới sử dụng SQLite cần hiểu rằng có sự khác biệt giữa các ràng buộc Python và thư viện SQLite C bên dưới. Có nguy cơ ẩn nấp trong bóng tối, do sự khác biệt về phiên bản của SQLite. Những điều này có thể xảy ra ngay cả khi bạn chạy giống nhau Phiên bản Python trên hai máy khác nhau, vì thư viện SQLite C có thể vẫn đang sử dụng phiên bản khác. Tôi cũng thảo luận về các vấn đề khác như xử lý các đối tượng datetime và liên tục thay đổi dữ liệu bằng cách sử dụng các giao dịch. Bản thân tôi cũng không biết về chúng, điều này đã gây ra mất dữ liệu cho người dùng ứng dụng của tôi, vì vậy tôi hy vọng rằng bạn có thể tránh được những sai lầm tương tự như tôi đã mắc phải.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Xuất Toàn bộ Cơ sở dữ liệu SQLite sang Tệp SQL

  2. Mã hóa dữ liệu trong SQLite

  3. Có nên có một SQLiteOpenHelper cho mỗi bảng trong cơ sở dữ liệu không?

  4. android.database.CursorIndexOutOfBoundsException

  5. Trả về danh sách các khóa ngoại trong SQLite