SQLiteで作成したテーブルを対象にしたPython圧縮モジュールの比較

# はじめに
Pythonの勉強として、SQLiteでテーブルを作成し、そのデータを圧縮してみます。

今回のポイントは以下の２つです。
- sqlite3 を使ってSQLiteのテーブルを作成し、データを格納する
- Pythonの圧縮モジュール（zlib, gzip, bz2, lzma）を比較し、圧縮率や速度を確認する

# SQLiteのテーブル作成
さっそく圧縮する対象のテーブルを作成します。
今回はidとdataという２つのカラムを持つテーブルを作成しました。
data列にはランダムな文字列を格納します。

```python
import sqlite3
from faker import Faker

db_name = "my_db.db"
table_name = "my_table"
rows = 100000

# Fakerインスタンスを作成
fake = Faker()


# 生成する文章の重複を防ぐ
def random_text() -> str:
    # fake = Faker()
    random_text = fake.text()
    return random_text


# データベースへの接続
con = sqlite3.connect(f"{db_name}")

# SQLクエリの結果を取得するためにデータベース・カーソルを使用する
cur = con.cursor()

try:
    # 既存のテーブルを削除
    cur.execute(f"DROP TABLE IF EXISTS {table_name}")
    print(f"テーブル {table_name} を削除しました。")

    # テーブルの作成
    cur.execute(
        f"""CREATE TABLE  IF NOT EXISTS {table_name}(
                id INTEGER PRIMARY KEY,
                data TEXT not null)
    """
    )
    print(f"テーブル {table_name} を作成しました。")

    # データの作成
    data = [(i, random_text()) for i in range(rows)]
    data_insert_sql = f"INSERT INTO {table_name} (id, data) VALUES (?, ?)"

    # テーブルにデータを挿入
    cur.executemany(data_insert_sql, data)

    # コミットして、DB接続を閉じる。
    con.commit()
finally:
    con.close()
    print("データベース接続を閉じました。")

```
作成したテーブルはこんな感じ。できてる！！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3831763/0bbea90f-fbdd-4d9f-81fe-c80447cdfb71.png)

# 圧縮モジュールの比較用コードの作成
今回は、以下の4つの圧縮モジュールを使って、SQLiteのデータを圧縮します
- zlib
- gzip
- bz2
- lzma

比較の観点は２つです。
- 圧縮にかかる時間
- 圧縮前のバイト数に対する圧縮後のバイト数の比

これらの圧縮モジュールを用いて、SQLiteのデータを実際に圧縮してみます。コードは以下のように書きました。
```python
import zlib
import gzip
import bz2
import lzma
import time

db_name = "my_db.db"

# ファイルをバイナリモードで開く
with open(db_name, "rb") as f:
    data = f.read()

# 圧縮前のDBのバイト数
size_before_compression = len(data)


# 圧縮後のバイト数を計算
def get_compressed_size(compress_func, data):
    compressed_data = compress_func(data)
    return len(compressed_data)


# 圧縮時間と圧縮の程度を計測する関数
def measure_time(compress_func, data, name):
    start = time.time()
    compressed = compress_func(data)
    end = time.time()
    # 圧縮にかかる時間
    compression_time = end - start
    # 圧縮後のバイト数
    size_after_compression = len(compressed)
    # 圧縮前のバイト数に対する圧縮後のバイト数の計算
    compression_ratio = size_after_compression / size_before_compression
    print(
        f"{name} 圧縮時間: {compression_time:.6f} 秒, 圧縮後のサイズ： {size_after_compression:.2f} byte, 圧縮率: {compression_ratio:.2f}"
    )


# 圧縮モジュール名と圧縮関数のマッピング
compression_methods = {
    "zlib": zlib.compress,
    "gzip": gzip.compress,
    "bz2": bz2.compress,
    "lzma": lzma.compress,
}

# 圧縮前のサイズを表示
print(f"圧縮前:{size_before_compression} byte")


# 各圧縮方法で計測を実施
for method, compress_func in compression_methods.items():
    measure_time(compress_func, data, method)

```

# 圧縮モジュールの比較用コードの実行結果
前節のコードを実行してみました。

```
圧縮前:16101376 byte
zlib 圧縮時間: 0.545227 秒, 圧縮後のサイズ： 6339633.00 byte, 圧縮率: 0.39
gzip 圧縮時間: 0.544726 秒, 圧縮後のサイズ： 6339511.00 byte, 圧縮率: 0.39
bz2 圧縮時間: 0.787286 秒, 圧縮後のサイズ： 4292200.00 byte, 圧縮率: 0.27
lzma 圧縮時間: 6.657628 秒, 圧縮後のサイズ： 5031192.00 byte, 圧縮率: 0.31
```

zlibとgzipは圧縮時間や圧縮率が非常に近い値ですね。
圧縮率に注目すると、bz2がいい感じっぽいですね。lzmaはすごい時間がかかっていますね。

こんなに違いが出るんですね。おどろきです。

# まとめ
速度重視であればzlibやgzipを使用し、データを少しでも小さくしたい時はbz2が良さそうでした。
ただ、これらの結果は圧縮する対象のデータの種類やサイズにも依存しそうですね。