sql >> データベース >  >> RDS >> SQLite

PythonとSQLiteの警告

    SQLiteは、アプリケーションに埋め込む人気のあるリレーショナルデータベースです。 PythonにはSQLiteへの公式バインディングが付属しています。この記事では、PythonでSQLiteを使用する際の注意事項について説明します。リンクされたSQLiteライブラリのさまざまなバージョンが引き起こす可能性のある問題、datetime オブジェクトが適切に保存されていないため、Pythonのwith connectionに依存する場合は特に注意が必要です。 データをコミットするコンテキストマネージャー。

    はじめに

    SQLiteは人気のあるリレーショナルデータベース(DB)システムです 。 MySQLなどのより大きなクライアントサーバーベースの兄弟とは異なり、SQLiteはライブラリとしてアプリケーションに埋め込むことができます 。 Pythonは、バインディングを介してSQLiteを公式にサポートしています(公式ドキュメント)。ただし、これらのバインディングの操作は必ずしも簡単ではありません。以前に説明した一般的なSQLiteの警告とは別に、この記事で検討するPython固有の問題がいくつかあります

    バージョンとデプロイメントターゲットとの非互換性

    開発者が、オペレーティングシステム(OS)とハードウェアの点で、コードが展開されているマシンとは(非常に)異なるマシンでコードを作成してテストすることは非常に一般的です。これにより、次の3種類の問題が発生します。

    • アプリケーションはOSまたはハードウェアの違いにより動作が異なります 。たとえば、ターゲットマシンのメモリがマシンより少ない場合、パフォーマンスの問題が発生する可能性があります。または、SQLiteは、使用する基盤となる低レベルのOS APIが異なるため、あるOSでは他のOSよりも実行速度が遅い場合があります。
    • SQLiteバージョン デプロイメントターゲットは、開発者のマシンのバージョンとは異なります 。これにより、双方向で問題が発生する可能性があります。時間の経過とともに新しい機能が追加される(および動作が変更される)ためです。公式の変更ログを参照してください。たとえば、古くなったデプロイ済みのSQLiteバージョンには、開発で正常に機能する機能がない場合があります。また、展開中の新しいSQLiteバージョンは、開発マシンで使用している古いバージョンとは異なる動作をする可能性があります。 SQLiteチームがいくつかのデフォルト値を変更したとき。
    • SQLiteのPythonバインディング、またはCライブラリのいずれかが、デプロイメントターゲットで完全に欠落している可能性がありますこれはLinux -配布固有の問題 。公式のWindowsおよびmacOSディストリビューションには、バンドルが含まれます。 SQLiteCライブラリのバージョン。 Linuxでは、SQLiteライブラリは別のパッケージです。 Pythonを自分でコンパイルする場合、たとえばDebian / Raspbian/etcを使用しているためです。古代の機能バージョン、 Python makeに同梱されているディストリビューション ビルドスクリプトは、PythonのSQLiteバインディングをの場合にのみビルドします インストールされたSQLiteCライブラリがPythonのコンパイルプロセス中に検出されました 。このようなPythonの再コンパイルを自分で行う場合は、インストールされているSQLiteCライブラリが最近であることを確認する必要があります。 。これも、aptを介してSQLiteをインストールする場合のDebianなどには当てはまりません。 、そのため、SQLiteを自分でビルドしてインストールする必要がある場合もあります。 Pythonの構築に。

    Pythonインタープリターで使用されているSQLiteCライブラリのバージョンを確認するには、次のコマンドを実行します。

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

    sqlite3.sqlite_versionを置き換える sqlite3.versionを使用 PythonのSQLiteバインディングのバージョンを提供します 。

    基盤となるSQLiteCライブラリの更新

    最新のSQLiteバージョンの機能やバグ修正から利益を得たい場合は、幸運です。 SQLite Cライブラリは通常、実行時にリンクされるため、インストールされているPythonインタープリターを変更せずに置き換えることができます。具体的な手順は、OSによって異なります(Python 3.6以降でテスト済み):

    1)ウィンドウ: SQLiteダウンロードページからx86またはx64でプリコンパイルされたバイナリをダウンロードし、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: 私の分析によると、SQLiteCライブラリはPythonのバインディングにコンパイルされているようです。 バイナリ(_sqlite3.cpython-36m-darwin.so )。置き換える場合は、インストールされているPythonインストールに一致するPythonソースコードを取得する必要があります(例:3.7.6 または使用するバージョン)。 macOSビルドスクリプトを使用して、ソースからPythonをコンパイルします。このスクリプトにはSQLiteのCライブラリのダウンロードとビルドが含まれているため、スクリプトを編集して最新のSQLiteバージョンを参照するようにしてください。最後に、コンパイルされたバインディングファイルを使用します(例:_sqlite3.cpython-37m-darwin.so )、古いものを置き換える。

    タイムゾーン対応のdatetimeの操作 オブジェクト

    ほとんどのPython開発者は通常、datetimeを使用します タイムスタンプを操作するときのオブジェクト。 ナイーブがあります datetime タイムゾーンを知らないオブジェクト、およびナイーブでない タイムゾーンであるもの-認識 。 Pythonのdatetimeはよく知られています モジュールは風変わりで、タイムゾーン対応の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)

    残念ながら、sqlite3の公式Pythonドキュメント モジュールは、タイムスタンプの処理に関して誤解を招く可能性があります。ここで説明するように、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には実際の「date」または「datetime」データ型はありません。

    トランザクションと自動コミット

    Pythonのsqlite3 モジュールは、クエリによって変更されたデータを自動的にコミットしません 。データベースを何らかの形で変更するクエリを実行する場合は、明示的な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以上 。それらをクエリパラメータで使用しようとした場合(?を使用) シンボル)、Pythonのsqlite3 モジュールはOverflowError: Python int too large to convert to SQLite INTEGERを発生させます 、偶発的なデータ損失からあなたを守ります。

    非常に大きな整数を適切に処理するには、次のことを行う必要があります。

    1. TEXTを使用する 対応するテーブル列のおよびを入力します
    2. 数値をstrに変換します すでにPythonで 、パラメータとして使用する前に。
    3. 文字列をintに変換し直します Pythonでは、SELECT ingデータ

    結論

    Pythonの公式sqlite3 モジュールはSQLiteへの優れたバインディングです。ただし、SQLiteを初めて使用する開発者は、Pythonバインディングと基盤となるSQLiteCライブラリに違いがあることを理解する必要があります。 SQLiteのバージョンの違いにより、影に潜む危険があります。これらは、同じを実行した場合でも発生する可能性があります SQLite Cライブラリがまだ異なるバージョンを使用している可能性があるため、2つの異なるマシンでのPythonバージョン。また、日時オブジェクトの処理やトランザクションを使用したデータの永続的な変更など、その他の問題についても説明しました。私自身はそれらに気づかなかったため、アプリケーションのユーザーのデータが失われたため、私が犯したのと同じ間違いを回避できることを願っています。


    1. Oracleのvarchar2PL/ SQLサブプログラム引数のサイズ制限は何ですか?

    2. MySQLで完全外部結合を行うにはどうすればよいですか?

    3. PostgreSQL、最大IDからのSELECT

    4. SQL Serverデータベースメール(T-SQL)から送信された電子メールのリストを返す