SQLite
 sql >> база данни >  >> RDS >> SQLite

Предупреждения за Python и SQLite

SQLite е популярна релационна база данни, която вграждате във вашето приложение. Python идва с официални връзки към SQLite. Тази статия разглежда предупрежденията за използването на SQLite в Python. Той демонстрира проблеми, които различните версии на свързани SQLite библиотеки могат да причинят, как datetime обектите не се съхраняват правилно и как трябва да бъдете особено внимателни, когато разчитате на with connection на Python контекстен мениджър, за да зададете вашите данни.

Въведение

SQLite е популярна система за релационна база данни (DB) . За разлика от по-големите си братя, базирани на клиент-сървър, като MySQL, SQLite може да бъде вграден във вашето приложение като библиотека . Python официално поддържа SQLite чрез обвързване (официални документи). Работата с тези връзки обаче не винаги е лесна. Освен общите предупреждения за SQLite, които обсъдих по-рано, има няколко специфични за Python проблема, които ще разгледаме в тази статия .

Несъвместимост на версията с целта за внедряване

Доста често срещано е разработчиците да създават и тестват код на машина, която е (много) различна от тази, на която е разположен кодът, по отношение на операционната система (ОС) и хардуера. Това причинява три вида проблеми:

  • Приложението се държи различно поради разликите в операционната система или хардуера . Например, може да срещнете проблеми с производителността, когато целевата машина има по-малко памет от вашата. Или SQLite може да изпълнява някои операции по-бавно на една ОС, отколкото на други, тъй като основните операционни програмни интерфейси на ниско ниво, които използва, са различни.
  • Версията SQLite на целта за внедряване се различава от версията на машината за разработчицита . Това може да причини проблеми и в двете посоки, тъй като се добавят нови функции (и поведението се променя) с течение на времето, вижте официалния регистър на промените. Например, на остаряла внедрена версия на SQLite може да липсват функции, които работят добре при разработката. Също така, по-нова версия на SQLite при внедряване може да се държи по различен начин от по-стара версия, която използвате на вашата машина за разработка, напр. когато екипът на SQLite промени някои стойности по подразбиране.
  • Или връзките на Python на SQLite, или библиотеката C може да липсват изцяло на целта за внедряване . Това е Linux -специфичен за дистрибуцията проблем . Официалните дистрибуции на Windows и macOS ще съдържат пакет версия на библиотеката SQLite C. В Linux библиотеката SQLite е отделен пакет. Ако компилирате Python сами, напр. защото използвате Debian/Raspbian/и т.н. дистрибуция, която се доставя с древни версии на функции, make на Python build скрипт ще изгради само SQLite обвързванията на Python if инсталирана библиотека SQLite C беше открита по време на процеса на компилация на Python . Ако направите такава повторна компилация на Python сами, тогава трябва да се уверите, че инсталираната библиотека SQLite C е скорошна . Това отново не е така за Debian и т.н., когато инсталирате SQLite чрез apt , така че може да се наложи да изградите и инсталирате SQLite сами, преди за изграждане на Python.

За да разберете коя версия на библиотеката SQLite C се използва от вашия интерпретатор на Python, изпълнете тази команда:

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

Замяна на sqlite3.sqlite_version с sqlite3.version ще ви даде версията на връзките на SQLite на Python .

Актуализиране на основната библиотека SQLite C

Ако искате да се възползвате от функции или корекции на грешки в най-новата версия на SQLite, имате късмет. Библиотеката SQLite C обикновено се свързва по време на изпълнение и по този начин може да бъде заменена без никакви промени в инсталирания ви интерпретатор на Python. Конкретните стъпки зависят от вашата ОС (тествана за Python 3.6+):

1) Windows: Изтеглете предварително компилираните двоични файлове x86 или x64 от страницата за изтегляне на SQLite и заменете sqlite3.dll файл, намерен в DLLs папка на вашата инсталация на Python с тази, която току-що изтеглите.

2) Linux: от страницата за изтегляне на SQLite вземете autoconf източници, извлечете архива и стартирайте ./configure && make && make install което ще инсталира библиотеката в /usr/local/lib по подразбиране.
След това добавете реда export LD_LIBRARY_PATH=/usr/local/lib в началото на скрипта на обвивката, който стартира вашия скрипт на Python, което принуждава вашия интерпретатор на Python да използва самостоятелно изградената библиотека.

3) macOS: от моя анализ изглежда, че библиотеката SQLite C е компилирана в връзките на Python двоичен (_sqlite3.cpython-36m-darwin.so ). Ако искате да го замените, вероятно ще трябва да получите изходния код на Python, съответстващ на вашата инсталирана инсталация на Python (напр. 3.7.6 или каквато и версия да използвате). Компилирайте Python от източник, като използвате скрипта за изграждане на macOS. Този скрипт включва изтегляне и изграждане на библиотеката C на SQLite, така че не забравяйте да редактирате скрипта, за да препоръчате най-новата версия на SQLite. Накрая използвайте компилирания файл за свързване (напр. _sqlite3.cpython-37m-darwin.so ), за да замените остарялата.

Работа с часова зона datetime обекти

Повечето разработчици на Python обикновено използват datetime обекти при работа с времеви печати. Има наивни datetime обекти, които не знаят за своята часова зона и ненаивни такива, които са наясно за часовата зона . Добре известно е, че datetime на Python модулът е странен, което затруднява дори създаването на часова зона datetime.datetime обекти. Например, извикването datetime.datetime.utcnow() създава наивен обект, което е противоинтуитивно за разработчиците, които са нови в datetime API, очаквайки Python да използва часовата зона UTC! Библиотеките на трети страни, като python-dateutil, улесняват тази задача. За да създадете обект, съобразен с часовата зона, можете да използвате код като този:

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

За съжаление, официалната документация на Python за sqlite3 модулът е подвеждащ, когато става въпрос за обработка на времеви печати. Както е описано тук, datetime обектите се преобразуват автоматично при използване на PARSE_DECLTYPES (и деклариране на TIMESTAMP колона). Въпреки че това е технически правилно, преобразуването ще загуби часовата зона информацията ! Следователно, ако всъщност използвате часова зонанаясно datetime.datetime обекти, трябва да регистрирате свои собствени конвертори , които запазват информация за часовата зона, както следва:

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)

Както можете да видите, времевата марка просто се съхранява като TEXT в края. В SQLite няма реален тип данни „дата“ или „дата и час“.

Транзакции и автоматично ангажиране

sqlite3 на Python модулът не записва автоматично данни, които се променят от вашите заявки . Когато изпълнявате заявки, които по някакъв начин променят базата данни, трябва или да издадете изрично COMMIT изявление или използвате връзката като контекст мениджър обект, както е показано в следния пример:

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

След излизане от горния блок, sqlite3 имплицитно извиква connection.commit() , но го прави само ако транзакцията е в ход . Изявленията на DML (език за промяна на данни) автоматично стартират транзакция, но заявки, включващи DROP или CREATE TABLE / INDEX изявленията не го правят, защото не се считат за DML според документацията. Това е противоинтуитивно, защото тези твърдения очевидно променят данните.

По този начин, ако стартирате някакъв DROP или CREATE TABLE / INDEX изрази в контекстния мениджър, добра практика е изрично да се изпълни BEGIN TRANSACTION първо изявление , така че контекстният мениджър всъщност ще извика connection.commit() за теб.

Обработка на 64-битови цели числа

В предишна статия вече обсъдих, че SQLite има проблеми с големи цели числа, които са по-малки от -2^63 , или по-голямо или равно от 2^63 . Ако се опитате да ги използвате в параметрите на заявката (с ? символ), sqlite3 на Python модул ще издигне OverflowError: Python int too large to convert to SQLite INTEGER , предпазвайки ви от случайна загуба на данни.

За да боравите правилно с много големи цели числа, трябва:

  1. Използвайте TEXT тип за съответната колона на таблицата, и
  2. Преобразувайте числото в str вече в Python , преди да го използвате като параметър.
  3. Преобразувайте низовете обратно в int в Python, когато SELECT данни

Заключение

Официалният sqlite3 на Python модулът е отлично свързване към SQLite. Въпреки това, разработчиците, нови в SQLite, трябва да разберат, че има разлика между връзките на Python и основната библиотека на SQLite C. Има опасност, дебнеща в сенките, поради разликите във версиите на SQLite. Това може да се случи дори ако стартирате същото Версия на Python на две различни машини, тъй като библиотеката SQLite C все още може да използва различна версия. Обсъдих също и други проблеми като обработка на обекти дата и време и постоянна промяна на данни с помощта на транзакции. Аз самият не ги знаех, което доведе до загуба на данни за потребителите на моите приложения, така че се надявам, че можете да избегнете същите грешки, които направих аз.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Какво да избера - JSON или SQLite?

  2. Добавете външен ключ към съществуваща таблица в SQLite

  3. Не може да се отвори база данни на SQLite от SQLIte Helper Oncreate, когато OnCreate се задейства при отваряне на база данни за първи път

  4. SQLite Поръчайте по

  5. sqlLiteDatabase.query() за INNER JOIN