今日はなにの日。

気になったこと勉強になったことのメモ。

今日は、PythonでSQL 結果を綺麗に表示するパッケージ作り始めたの日。

目次

とある日

Python で、DB に SQL クエリの結果を受け取るプログラムを作成していた。

SQL クエリの結果を毎回、Print 関数を使用して確認していた。

def MySQLDBConnect():
    conn = False
    try:
        conn = pymysql.connect(
                            host= 'localhost',
                            user='test',
                            password='test',
                            db='test',
                            charset='utf8mb4',
                            cursorclass=pymysql.cursors.DictCursor)
    except Exception as identifier:
        conn = False
    return conn

def mysqltest():
try:
conn = MySQLDBConnect()
print(conn)
with conn.cursor() as cursor:
sql = "SELECT user_id , create_at FROM users order by user_id limit 10;"
cursor.execute(sql)
result = cursor.fetchall()
print(result)
except Exception as identifier:
print(identifier)

if **name** == "**main**":
mysqltest()

このSQLの結果をPrint関数を使用して表示する。

[{'user_id': 1, 'create_at': datetime.datetime(2020, 10, 8, 15, 8, 43)}, {'user_id': 2, 'create_at': datetime.datetime(2020, 10, 8, 15, 8, 50)}, {'user_id': 3, 'create_at': datetime.datetime(2020, 10, 8, 15, 9, 10)}, {'user_id': 4, 'create_at': datetime.datetime(2020, 10, 8, 15, 13, 45)}, {'user_id': 5, 'create_at': datetime.datetime(2020, 10, 8, 15, 17, 59)}, {'user_id': 6, 'create_at': datetime.datetime(2020, 10, 8, 15, 18, 3)}, {'user_id': 7, 'create_at': datetime.datetime(2020, 10, 8, 15, 18, 6)}, {'user_id': 8, 'create_at': datetime.datetime(2020, 10, 8, 15, 18, 9)}, {'user_id': 9, 'create_at': datetime.datetime(2020, 10, 8, 15, 18, 11)}, {'user_id': 10, 'create_at': datetime.datetime(2020, 10, 8, 15, 18, 14)}]

横一行に出力される。

同じ結果を pprint ライブラリ1使用して表示する。

[{'create_at': datetime.datetime(2020, 10, 8, 15, 8, 43), 'user_id': 1},
 {'create_at': datetime.datetime(2020, 10, 8, 15, 8, 50), 'user_id': 2},
 {'create_at': datetime.datetime(2020, 10, 8, 15, 9, 10), 'user_id': 3},
 {'create_at': datetime.datetime(2020, 10, 8, 15, 13, 45), 'user_id': 4},
 {'create_at': datetime.datetime(2020, 10, 8, 15, 17, 59), 'user_id': 5},
 {'create_at': datetime.datetime(2020, 10, 8, 15, 18, 3), 'user_id': 6},
 {'create_at': datetime.datetime(2020, 10, 8, 15, 18, 6), 'user_id': 7},
 {'create_at': datetime.datetime(2020, 10, 8, 15, 18, 9), 'user_id': 8},
 {'create_at': datetime.datetime(2020, 10, 8, 15, 18, 11), 'user_id': 9},
 {'create_at': datetime.datetime(2020, 10, 8, 15, 18, 14), 'user_id': 10}]

いい感じに結果を整形してくれて見やすくなっている。

いやでも、DBeaverやCUIみたいにテーブル形式での出力がいいな。

-- DBeaver text
user_id|create_at          |
-------|-------------------|
      1|2020-10-08 15:08:43|
      2|2020-10-08 15:08:50|
      3|2020-10-08 15:09:10|
      4|2020-10-08 15:13:45|
      5|2020-10-08 15:17:59|
      6|2020-10-08 15:18:03|
      7|2020-10-08 15:18:06|
      8|2020-10-08 15:18:09|
      9|2020-10-08 15:18:11|
     10|2020-10-08 15:18:14|
-- MySQL-shell
+---------+---------------------+
| user_id | create_at           |
+---------+---------------------+
|       1 | 2020-10-08 15:08:43 |
|       2 | 2020-10-08 15:08:50 |
|       3 | 2020-10-08 15:09:10 |
|       4 | 2020-10-08 15:13:45 |
|       5 | 2020-10-08 15:17:59 |
|       6 | 2020-10-08 15:18:03 |
|       7 | 2020-10-08 15:18:06 |
|       8 | 2020-10-08 15:18:09 |
|       9 | 2020-10-08 15:18:11 |
|      10 | 2020-10-08 15:18:14 |
+---------+---------------------+
10 rows in set (0.00 sec)

こうやって表示してほしい。

今後の使いそうだし、自分で作ってみるか。

ってことで、作り始めた。

GitHub

github.com

そもそも表形式出力ないの?

答えは、あります

tabulate2ってやつです。

ただ、引数のデータ形式がちょっと求めてるものと違う。

table = [["Sun",696000,1989100000],["Earth",6371,5973.6], ... ["Moon",1737,73.5],["Mars",3390,641.85]]

tabulateは、上記の形式での引数を受け取るようになっている。

ほかにも、あるらしいが・・・。

自分が求めているものは、SQLColumn name あり結果のそのまま引数として投げれるもの。

tabulateは、リファレンスを見た感じできなさそう。

TPRINT 現在作成途中

GitHubリポジトリ公開してます。

https://github.com/air-flow/Python-tprint

table 形式の出力なので tprint って名付けました。

現在は、Oracle 形式の出力を作成中。

MySQL 形式の出力は、テスト用だが完成している。

MySQL 形式の出力はこのプログラムを実行すると表示される。はず。

+--------------------------------------------+
| id|  name| crrent_datetime_date_time_today_|
+--------------------------------------------+
|  1|  abcd|                       2020/10/28|
|  2|    ab|                       2020/10/28|
|  3|  abcd|                       2020/10/28|
|  4|  abcd|                       2020/10/28|
|  5|  abcd|                       2020/10/28|
|  6| abcde|                       2020/10/28|
|  7|   abc|                       2020/10/28|
|  8|    ab|                       2020/10/28|
|  9| abcde|                       2020/10/28|
| 10|     a|                       2020/10/28|
+--------------------------------------------+

右寄せで最大文字数に合わせて行の長さが決まる。

実際に SQL クエリの結果を取得するのは、環境を用意したりするのが大変なので Python 上で自動生成している。

追加したい機能や、対応する型はmemoに入力していっている。

MySQL 形式出力プログラム

現在、テスト用で出力だけ完成している MySQL 形式出力プログラムだけ少し解説。

1.CreateData(データ生成)

def CreateData():
    result = []
    for i in range(1,11,1):
        temp = {"id":i,"name":"","crrent_datetime_date_time_today_":datetime.date.today().strftime('%Y/%m/%d')}
        name = ["a","ab","abc","abcd","abcde"]
        temp["name"] = random.choices(name,k=1)[0]
        result.append(temp)
    return result

SQL から取得した結果データを生成する関数。

id:数値データのテスト用

name:ランダムな文字数での対応テスト用

crrentdatetime_date_time_todayカラム名が最大文字数のテスト用、date 形式のテスト用

それぞれのデータに意味をもたせて生成している。

2.CreateColumnDict(カラムデータ切り出し)

def CreateColumnDict(data):
    max_len = dict(zip(list(data[0].keys()),GetColumnValue(data)))
    return max_len

引数の data には、SQL の結果データ形式を想定している。

各カラムの最大文字数を探すためにカラム名

3.GetColumnValue({カラム名:最大文字数}取得)

def GetColumnValue(data):
    key_list = data[0].keys()
    result = []
    for i in key_list:
        temp = GetValueList(i,data)
        temp.append(i)
        result.append(MoreStrLne(temp))
    return result

Column それぞれを key とし、Column 名とその値リストの中から最大文字数を value する dict を生成する。

4.GetValueList(指定した key を持つ値を取得する)

def GetValueList(key,data):
    temp = [i[key] for i in data]
    return temp

必要な key が持つ value を取得する。

5.MoreStrLne(最大文字数値を取得する)

def MoreStrLne(data):
    return len(max(map(str,data),key=len)) + 1

Column 名と Column data のリストから文字数が最大のものを取得する。

数値データも存在するのですべてのデータを String 型 2 統一するため map 関数を使用している。

6.PrintHyphen(区切り文字のハイフンを出力する)

def PrintHyphen(key_list):
    print("+"+"-"*(int(len(key_list))-1+sum(list(key_list.values())))+"+")

一行の区切り文字をハイフンで区切るため、全ての Column 最大文字数を足した値と、全て値の出力前にはスペースがあるのでそれの数を合わせた数出力する。

7.PrintColumnName(カラム名を出力する)

def PrintColumnName(key_list):
        temp = []
        for key,value in key_list.items():
            temp.append(str(key).rjust(key_list[key], " "))

Column 名の一行の出力するための関数。

右詰めを最大文字数の分を行っている。

8.tprint(出力する)

def tprint(data):
    key_len = CreateColumnDict(data)
    PrintHyphen(key_len)
    PrintColumnName(key_len)
    PrintHyphen(key_len)
    for i in data:
        temp = []
        for key,value in i.items():
            temp.append(str(value).rjust(key_len[key], " "))
        print("|"+"|".join(temp)+"|")
    PrintHyphen(key_len)

上記の全てを呼び出している。

今後の開発進め方

まずは、表示形式と設定項目を目標にそって開発を行う予定。

今後も自分で使用することにしているので、使いやすくわかりやすく作りたいと思っている。

Oracle 形式と PostgreSQL 形式の表出力をチュートリアルで作成していく。

次にオプションを、設定できるようにする。

DBMS で出力時のオプションを設定できる、それをtprintパッケージでも使用できるようにすると直感的に操作可能となり、操作性が向上すると思う。

理想なのは、どの接続ライブラリーを使用しているかを自動で判定すればもっと楽に使用することができるかなと。

色々とやることが多い気がしますが、ゆっくり自分の学習のためにやっていこうかなと。

型を定義したり、普段使ったことないような機能とか使いながら Python の理解度を深めたい。