Python {Article039}

ようこそ「Python」へ...

Python SQLite3 超入門 (データベース作成▶テーブル作成▶レコードの追加・更新・削除・選択)

ここではPythonに標準で組み込まれているデータベースSQLite3の使い方について解説します。 データベースにはMicrosoftのAccess, SQL Server, MySQL, Oracleなどいろんな種類がありますが、 SQLite3は実際の業務で使用するのではなくSQLコマンドなどデータベースを学習するためのものです。

SQLite3ではデータベースファイル(*.db)を作成してテーブル、レコードを保存することができます。 さらに、実際のファイルを作成するのではなくメモリ上(ファイル名の代わりに「:memory:」を指定)にデータベースを作成することも可能です。 この機能はデータベースのSQLを学習するときにとても便利です。

ここではPythonの開発ツールにMicrosoftのVisual Studio Code(VSC)を使用します。 初心者の方はJupter Notebookを使うことも可能ですが、VSCを使うとPythonのExtensions(拡張機能・アドイン)を組み込むことができます。 たとえば、VSCの拡張機能「MySQL: Database Client for vscode by cweijan」をインストールすると、 MS-Accessのようにデータベースのテーブル・レコードを追加、更新、削除、選択(抽出)することができます。 詳しくは後述します。

ここではVisula Studio Code(VSC)の「Python Interactive window」 を使用してJupter Notebookのような環境で説明します。 VSCを通常の環境からインタラクティブな環境に切り換えるにはコードを記述するときコメント「# %%」を入力します。 詳しい、操作手順については「ここ」 を参照してください。 インタラクティブな環境では、Pythonの「print(), plt.show()」などを使う必要がないので掲載しているコードでは省略しています。 VSCで通常の環境で使用するときは、必要に応じて「print(), plt.show()」等を追加してください。

この記事では、Pandas、Matplotlibのライブラリを使用しますので 「記事(Article001) | 記事(Article002) | 記事(Article003) | 記事(Article004)」 を参照して事前にインストールしておいてください。 Pythonのコードを入力するときにMicrosoftのVisula Studio Codeを使用します。 まだ、インストールしていないときは「記事(Article001)」を参照してインストールしておいてください。

説明文の左側に図の画像が表示されていますが縮小されています。 画像を拡大するにはマウスを画像上に移動してクリックします。 画像が拡大表示されます。拡大された画像を閉じるには右上の[X]をクリックします。 画像の任意の場所をクリックして閉じることもできます。

click image to zoom!
Article Summary
click image to zoom!
Create Table
click image to zoom!
Add Records
click image to zoom!
SQLite Tool
click image to zoom!
SQLite Export/Import
click image to zoom!
DJIA Fundamentals


◆SQLite3のデータベース、テーブルを作成してレコードを追加、更新、削除、選択する

  1. SQLite3のデータベースとテーブルを作成する

    Visual Studio Code(VSC)を起動したら行1-33をコピペして実行します。 行2ではSQLite3のライブラリを取り込んでいます。 行5ではSQLite3のデータベース「employee.db」を接続しています。 employee.dbが存在しないときは自動的にデータベースを作成します。 実際のデータベースファイルを作成しないでメモリ上に作成するには行6のようにデータベース名を「:memory:」のように指定します。

    行8-15ではデータベースに作成されている全てのテーブルを削除しています。 データベースからテーブルを削除するにはSQLコマンド「DROP TABLE {table_name}」を使用します。 行17ではSQLite3のconnectionのcursor()メソッドでデータベースのカーソルを作成(データベースを開く)しています。

    行20-27ではテーブル「employee」を作成するSQLコマンドを定義しています。 SQLite3には6種類のデータ型(NULL, INTEGER, NUMERIC, REAL, TEXT, BLOB)があるだけですからとてもシンプルです。 ここで記述しているように標準のSQLコマンドもサポートしています。 SQLコマンドを複数行で定義するには「""" SQL Command """」のように「ダブルクオーテーション3個」で囲みます。 行28ではcursorのexecute()メソッドでSQLコマンドを実行しています。 ここで定義したSQLコマンドには「IF NOT EXISTS employee」の記述があるのでテーブル「employee」が存在しないときのみ作成します。
    # Import the libraries
    import sqlite3
    
    # Connect a sqllite database
    con = sqlite3.connect('sqlite/db/employee.db')  
    #con = sqlite3.connect(':memory:')  # use memory database => ':memory:'
    
    sql = "SELECT name FROM sqlite_master WHERE type='table';"
    res = con.execute(sql)
    for name in res.fetchall():
        table_name = name[0]
        print(table_name) 
        sql = f'DROP TABLE {table_name}'
        print(sql)
        con.execute(sql)
    
    c = con.cursor()
    
    # Create a table (id, first, last, pay)
    sql = """
        CREATE TABLE IF NOT EXISTS employee (
            id INTEGER PRIMARY KEY, 
            first VARCHAR(20), 
            last VARCHAR(20), 
            pay INTEGER
        )
    """
    c.execute(sql)
    
    # SQLite Datatypes
    # NULL, INTEGER (int), NUMERIC, REAL (floating), TEXT (string), BLOB
    
    # %%
    click image to zoom!
    図1
    図1はVisual Studio Code(VSC)の実行画面です。 右側のインタラクティブ・ウィンドウにprint()で出力したデータが表示されています。
  2. SQLite3のテーブルにレコードを追加する

    行2-3ではcusorのexecute()メソッドでレコードを追加するSQLコマンドを実行しています。 ここでは2件のレコードを追加しています。 行4ではcursorのcommit()メソッドでメモリ上のデータをテーブルに反映させています。 行7ではconnectionのclose()メソッドでSQLite3データベースを切断しています。 図2-1から図2-4では、VSCのExtensions(拡張機能)をインストールしてSQLite3のデータベース、テーブル、レコードを表示する手順を説明しています。
    # Add the rows
    c.execute("INSERT INTO employee VALUES (1, 'Taro', 'Kasai', 1000)")
    c.execute("INSERT INTO employee VALUES (2, 'Hanako', 'Kasai', 900)")
    con.commit()
    
    # Disconnect the database
    con.close()
    click image to zoom!
    図2-1
    VSCの左側のアイコンから「Extensions」を選択します。 次に「EXTENSIONS」の検索窓に「MySQL」を入力して検索します。 一覧が表示されたら「MySQL Database Client for vscode by cweijan」の[Install]をクリックしてインストールします。 この画面ではすでにインストールされているので「歯車」のアイコンが表示されています。 インストールが完了すると「Database」のアイコンが追加されます。


    click image to zoom!
    図2-2
    VSCの左側から[Database]のアイコンをクリックします。 次に「Database」のウィンドウから[+]をクリックします。 「Connect To Server」ウィンドウが表示されたら「Server Type」から[SQLite]をクリックして選択します。 「SQLite File Path」の右端から[Choose Database File]をクリックしてWindowsのダイアログを開きます。 ダイアログからSQLite3のデータベース「employee.db」を選択して[開く]をクリックします。


    click image to zoom!
    図2-3
    SQLite3のデータベース「employee.db」を選択したら[Connect]をクリックして接続します。
    click image to zoom!
    図2-4
    DATABASEのウィンドウにデータベース名、テーブル名が表示されます。 テーブル名をクリックすると列名が表示されます。 さらにテーブルウィンドウにテーブルのレコードが表示されます。 今回追加した2件のレコードが表示されています。 このテーブルウィンドウからは手動でレコードの追加、更新、削除を行うことができます。


  3. SQLite3のテーブルからレコードを抽出(選択)する

    行1-2ではデータベースを接続してデータベースを開いています。 行5-6ではテーブル「employee」の全レコードを抽出して表示しています。 行5ではcursorのexecute()メソッドを全レコードを抽出するSQLのSELECTコマンドを実行しています。 行6ではcursorのfetchall()メソッドで全レコードを取り込んで表示しています。

    行9-10ではSQLのSELECTコマンドに「WHERE」句を追加してレコードを絞り込んでいます。 ここでは「last='Kasai' AND first='Taro'」のレコードを絞り込んで抽出しています。 行10ではcursorのfetchone()メソッドで1件だけレコードを取り込んで表示しています。

    行13-14ではSQLのSELECTコマンドのWHERE句に位置指定パラメータ「?」を記述してレコードを絞り込んでいます。 WHERE句に「last='Kasai'」と記述する代わりに「last=?」のように記述して、位置指定パラメータに値「'Kasai'」を代入させます。 最後にコンマ「,」を記述することを忘れないでください。

    行17-18ではSQLのSELECTコマンドのWHERE句に名前指定パラメータ「:last」を記述してレコードを絞り込んでいます。 WHERE句に「last=?」と記述する代わりに「last=:last」のように記述して、名前指定パラメータに値「{'last': 'Kasai'}」を代入させます。 名前指定パラメータを使用すると複数の列名でレコードを絞り込むときに便利です。 基本的にレコードを絞り込むときは名前指定パラメータを使用するようにしてください。

    行20ではデータベースを切断しています。
    con = sqlite3.connect('sqlite/db/employee.db')
    c = con.cursor()
    
    # Select all the rows
    c.execute('SELECT * FROM employee')
    print(c.fetchall())
    
    # filter by full name
    c.execute("SELECT * FROM employee WHERE last='Kasai' AND first='Taro'")
    print(c.fetchone())
    
    # Filter by Positional Parameter
    c.execute('SELECT * FROM employee WHERE last=?', ('Kasai',))
    print(c.fetchall())
    
    # Filter by Named Parameter
    c.execute('SELECT * FROM employee WHERE last=:last', {'last': 'Kasai'})
    print(c.fetchall())
    
    con.close()
    click image to zoom!
    図3
    図3は実行結果です。VSCのインタラクティブ・ウィンドウにprint()で出力したレコードが表示されています。
  4. SQLite3のテーブルのレコードを更新する

    行7-8ではSQLのUPDATEコマンドでレコードを更新しています。 行7ではcursorのexecute()メソッドでSQLのUPDATEコマンドを実行しています。 さらにexecute()のrowcountプロパティから更新されたレコード件数を取得しています。 レコードを更新するときは「WHERE」句にテーブルの主キー「id」を指定してレコードを絞り込みます。 行8ではレコード件数を表示しています。 行4-5と行10-11ではレコードを更新する前と後のレコードの内容を表示しています。
    con = sqlite3.connect('sqlite/db/employee.db')
    c = con.cursor()
    
    c.execute('SELECT * FROM employee WHERE id = 1')
    print(c.fetchone())
    
    affected_rows = c.execute('UPDATE employee SET pay = 1100 WHERE id = 1').rowcount
    print('Affected Rows:', affected_rows)
    
    c.execute('SELECT * FROM employee WHERE id = 1')
    print(c.fetchone())
    
    con.commit()
    con.close()
    click image to zoom!
    図4
    図4は実行結果です。 VSCのインタラクティブ・ウィンドウにレコードの更新前後の内容が表示されています。 レコードの列「pay」の金額が「1000」から「1100」に変更されています。 また、rowcountプロパティから取得したレコード更新件数が「1」と表示されています。 つまり、レコードが1件更新されたことになります。


  5. SQLite3のテーブルからレコードを削除する

    行4-5ではSQLのDELETEコマンドでレコードを削除しています。 行4のSQLのDELETEコマンドの「WHERE」句には「id = 1」を指定しているのでidが「1」のレコードが削除されます。 行5では削除されたレコード件数を表示しています。

    行8-9ではテーブルの全レコードを抽出して表示しています。
    con = sqlite3.connect('sqlite/db/employee.db')
    c = con.cursor()
    
    affected_rows = c.execute('DELETE FROM employee WHERE id = 1').rowcount
    print('Affected Rows:', affected_rows)
    
    con.commit()
    c.execute('SELECT * FROM employee')
    print(c.fetchall())
    con.close()
    click image to zoom!
    図5
    図5は実行画面です。レコードがテーブルから1件削除されたことが表示されています。 そしてテーブルから全レコードを抽出した結果1件だけ表示されています。 つまり、「id=1」のレコードが削除されたことが確認できました。


  6. ここで解説したコードをまとめて掲載

    最後にここで解説したすべてのコードをまとめて掲載しましたので参考にしてください。
    
    # Import the libraries
    import sqlite3
    
    # Connect a sqllite database
    con = sqlite3.connect('sqlite/db/employee.db')  
    #con = sqlite3.connect(':memory:')  # use memory database => ':memory:'
    
    sql = "SELECT name FROM sqlite_master WHERE type='table';"
    res = con.execute(sql)
    for name in res.fetchall():
        table_name = name[0]
        print(table_name) 
        sql = f'DROP TABLE {table_name}'
        print(sql)
        con.execute(sql)
    
    c = con.cursor()
    
    # Create a table (id, first, last, pay)
    sql = """
        CREATE TABLE IF NOT EXISTS employee (
            id INTEGER PRIMARY KEY, 
            first VARCHAR(20), 
            last VARCHAR(20), 
            pay INTEGER
        )
    """
    c.execute(sql)
    
    # SQLite Datatypes
    # NULL, INTEGER (int), NUMERIC, REAL (floating), TEXT (string), BLOB
    
    # %%
    
    # Add the rows
    c.execute("INSERT INTO employee VALUES (1, 'Taro', 'Kasai', 1000)")
    c.execute("INSERT INTO employee VALUES (2, 'Hanako', 'Kasai', 900)")
    con.commit()
    
    # Disconnect the database
    con.close()
    
    # %%
    
    # Select a row or rows
    
    con = sqlite3.connect('sqlite/db/employee.db')
    c = con.cursor()
    
    # Select all the rows
    c.execute('SELECT * FROM employee')
    print(c.fetchall())
    
    # filter by full name
    c.execute("SELECT * FROM employee WHERE last='Kasai' AND first='Taro' ")
    print(c.fetchone())
    
    # Filter by Positional Parameter
    c.execute('SELECT * FROM employee WHERE last=?', ('Kasai',))
    print(c.fetchall())
    
    # Filter by Named Parameter
    c.execute('SELECT * FROM employee WHERE last=:last', {'last': 'Kasai'})
    print(c.fetchall())
    
    con.close()
    
    # %%
    
    # Update the rows
    con = sqlite3.connect('sqlite/db/employee.db')
    c = con.cursor()
    
    c.execute('SELECT * FROM employee WHERE id = 1')
    print(c.fetchone())
    
    affected_rows = c.execute('UPDATE employee SET pay = 1100 WHERE id = 1').rowcount
    print('Affected Rows:', affected_rows)
    
    c.execute('SELECT * FROM employee WHERE id = 1')
    print(c.fetchone())
    
    con.commit()
    con.close()
    
    # %%  
     
    # Delete the rows  
    
    con = sqlite3.connect('sqlite/db/employee.db')
    c = con.cursor()
    
    affected_rows = c.execute('DELETE FROM employee WHERE id = 1').rowcount
    print('Affected Rows:', affected_rows)
    
    con.commit()
    c.execute('SELECT * FROM employee')
    print(c.fetchall())
    con.close()
    
    # %%
    

◆テーブルのクラスを作成してレコードを追加、更新、削除、選択する

  1. テーブルのクラスを作成する

    Visual Studio Code(VSC)からサブフォルダ「sqlite」「lib」を作成します。 次に行1-17をコピペしたらサブフォルダ「sqlite/lib」に「employee.py」の名前で保存します。
    class Employee:
        def __init__(self, id, first, last, pay):
            self.id = id
            self.first = first
            self.last = last
            self.pay = pay
    
        @property
        def email(self):
            return '{}.{}@email.com'.format(self.first, self.last)
    
        @property
        def fullname(self):
            return '{} {}'.format(self.first, self.last)
    
        def __repr__(self):
            return "Employee({}, '{}', '{}', {})".format(self.id, self.first, self.last, self.pay)
    click image to zoom!
    図6
    図6はVSCの画面です。 サブフォルダ「sqlite/lib」にテーブルのクラス「employee.py」が保存されています。
  2. レコードを追加、更新、削除、選択する関数を定義する

    ここではSQLite3のテーブル「employee」のクラス「Employee」を使用して、 レコードを追加、更新、削除、選択(抽出)する関数を定義します。 行2ではSQLite3のライブラリを取り込んでいます。 行3ではテーブル「employee」のクラス「Employee」を取り込んでいます。 行5-15はSQLite3のデータベースをメモリ上に作成してテスト用のレコードを作成する関数です。 行17-24はテーブルから全レコードを抽出する関数です。 以降の関数については説明を省略します。
    # Import the libraries
    import sqlite3
    from  sqlite.lib.employee import Employee
    
    def load_data():
        con = sqlite3.connect(':memory:')  
        c = con.cursor()
        c.execute('CREATE TABLE employee (id INTEGER, first TEXT, last TEXT, pay INTEGER)')
        c.execute("INSERT INTO employee VALUES (1, 'Taro', 'Kasai', 1000)")
        c.execute("INSERT INTO employee VALUES (2, 'Hanako', 'Kasai', 900)")
        c.execute("INSERT INTO employee VALUES (3, 'Taro', 'Yamada', 2000)")
        c.execute("INSERT INTO employee VALUES (4, 'Hanako', 'Yamada', 1900)")
        con.commit()
        #conn.close()
        return con
    
    def get_all_emps():
        row_count = c.execute('SELECT * FROM employee').rowcount 
        emps  = []
        for row in c.fetchall():
            id, first, last, pay = row
            emp = Employee(id, first, last, pay)
            emps.append(emp)    
        return emps        
    
    def get_emp_by_id(id):
        row_count = c.execute('SELECT * FROM employee WHERE id=:id', {'id': id}).rowcount 
        id, first, last, pay = c.fetchone()
        emp = Employee(id, first, last, pay)    
        return emp    
    
    def get_count_by_id(id):
        row_count = c.execute('SELECT Count(*) AS rec_count FROM employee WHERE id=:id', {'id': id}).rowcount
        rec_count = c.fetchone()    # tuple (1,)    
        #print('rec_count=', rec_count)
        return rec_count[0]     
    
    def get_emps_by_lastname(lastname):
        row_count = c.execute('SELECT * FROM employee WHERE last=:last', {'last': lastname}) .rowcount
        emps  = []
        for row in c.fetchall():
            id, first, last, pay = row
            emp = Employee(id, first, last, pay)
            emps.append(emp)    
        return emps     
    
    def get_emps_by_fullname(firstname, lastname):
        row_count = c.execute('SELECT * FROM employee WHERE first=:first AND last=:last', 
            {'first': firstname, 'last': lastname}) .rowcount
        emps  = []
        for row in c.fetchall():
            id, first, last, pay = row
            emp = Employee(id, first, last, pay)
            emps.append(emp)    
        return emps        
    
    def insert_emp(emp):
        with con:
            affected_rows = c.execute('INSERT INTO employee VALUES (:id, :first, :last, :pay)',
                 {'id': emp.id,'first': emp.first, 'last': emp.last, 'pay': emp.pay}).rowcount
            return affected_rows
    
    def update_pay_by_id(id, pay):
        with con:
            affected_row = c.execute('UPDATE employee SET pay=:pay WHERE id=:id', 
                {'pay': pay, 'id': id}).rowcount
            return affected_row
    
    def remove_emp_by_id(id):
        with con:
            affected_row = c.execute('DELETE FROM employee WHERE id = :id', {'id': id}).rowcount
            return affected_row
    
    # %%
    click image to zoom!
    図7
    図7はVSCの画面です。
  3. テーブルからレコードを抽出する

    ### Select a row or rows
    
    con = load_data()
    c = con.cursor()
    
    # Select all the rows
    print('id\tfirst\tlast\tpay')
    print(('-'*30))
    for emp in get_all_emps():
        print(f'{emp.id}\t{emp.first}\t{emp.last}\t{emp.pay:,}')
    print(('-'*30))
    print()
    
    # Filter the row by id
    print('get_emp_by_id(1)▶', get_emp_by_id(1))
    
    # Filter the row by lastname
    print("get_emps_by_lastname('Kasai')▶", get_emps_by_lastname('Kasai'))
    
    # Filter the row by fullname
    print("get_emps_by_fullname('Taro', 'Kasai')▶", get_emps_by_fullname('Taro', 'Kasai'))
    
    con.close()
    click image to zoom!
    図8
    図8は実行画面です。
  4. テーブルにレコードを追加する

    ### Insert the row
    
    con = load_data()
    c = con.cursor()
    
    emp = Employee(9, 'Taro', 'Tanaka', 9999)
    print('insert_emp(emp)▶', insert_emp(emp))
    print('get_emp_by_id(9)▶', get_emp_by_id(9))
    
    con.commit()
    con.close()
    click image to zoom!
    図9
    図9は実行画面です。
  5. テーブルのレコードを更新する

    ### Update the row
    
    con = load_data()
    c = con.cursor()
    
    print('Before Update:', get_emp_by_id(1))
    
    print('update_pay_by_id(1, 9999)▶', update_pay_by_id(1, 9999))
    
    print('After Update:', get_emp_by_id(1))
    
    con.commit()
    con.close()
    click image to zoom!
    図10
    図10は実行画面です。
  6. テーブルからレコードを削除する

    ### Delete the row  
    
    con = load_data()
    c = con.cursor()
    
    print('Before Remove▶', get_count_by_id(1))
    
    print('remove_emp_by_id(1)▶', remove_emp_by_id(1))
    
    print('After Remove▶', get_count_by_id(1))
    
    con.commit()
    con.close()
    click image to zoom!
    図11
    図11は実行画面です。
  7. ここで解説したコードをまとめて掲載

    最後にここで解説したすべてのコードをまとめて掲載しましたので参考にしてください。
    
    # Import the libraries
    import sqlite3
    from  sqlite.lib.employee import Employee
    
    def load_data():
        con = sqlite3.connect(':memory:')  
        c = con.cursor()
        c.execute('CREATE TABLE employee (id INTEGER, first TEXT, last TEXT, pay INTEGER)')
        c.execute("INSERT INTO employee VALUES (1, 'Taro', 'Kasai', 1000)")
        c.execute("INSERT INTO employee VALUES (2, 'Hanako', 'Kasai', 900)")
        c.execute("INSERT INTO employee VALUES (3, 'Taro', 'Yamada', 2000)")
        c.execute("INSERT INTO employee VALUES (4, 'Hanako', 'Yamada', 1900)")
        con.commit()
        #conn.close()
        return con
    
    def get_all_emps():
        row_count = c.execute('SELECT * FROM employee').rowcount 
        emps  = []
        for row in c.fetchall():
            id, first, last, pay = row
            emp = Employee(id, first, last, pay)
            emps.append(emp)    
        return emps        
    
    def get_emp_by_id(id):
        row_count = c.execute('SELECT * FROM employee WHERE id=:id', {'id': id}).rowcount 
        id, first, last, pay = c.fetchone()
        emp = Employee(id, first, last, pay)    
        return emp    
    
    def get_count_by_id(id):
        row_count = c.execute('SELECT Count(*) AS rec_count FROM employee WHERE id=:id', {'id': id}).rowcount
        rec_count = c.fetchone()    # tuple (1,)    
        #print('rec_count=', rec_count)
        return rec_count[0]     
    
    def get_emps_by_lastname(lastname):
        row_count = c.execute('SELECT * FROM employee WHERE last=:last', {'last': lastname}) .rowcount
        emps  = []
        for row in c.fetchall():
            id, first, last, pay = row
            emp = Employee(id, first, last, pay)
            emps.append(emp)    
        return emps     
    
    def get_emps_by_fullname(firstname, lastname):
        row_count = c.execute('SELECT * FROM employee WHERE first=:first AND last=:last', 
            {'first': firstname, 'last': lastname}) .rowcount
        emps  = []
        for row in c.fetchall():
            id, first, last, pay = row
            emp = Employee(id, first, last, pay)
            emps.append(emp)    
        return emps        
    
    def insert_emp(emp):
        with con:
            affected_rows = c.execute('INSERT INTO employee VALUES (:id, :first, :last, :pay)',
                 {'id': emp.id,'first': emp.first, 'last': emp.last, 'pay': emp.pay}).rowcount
            return affected_rows
    
    def update_pay_by_id(id, pay):
        with con:
            affected_row = c.execute('UPDATE employee SET pay=:pay WHERE id=:id', 
                {'pay': pay, 'id': id}).rowcount
            return affected_row
    
    def remove_emp_by_id(id):
        with con:
            affected_row = c.execute('DELETE FROM employee WHERE id = :id', {'id': id}).rowcount
            return affected_row
    
    # %%
    
    ### Select a row or rows
    
    con = load_data()
    c = con.cursor()
    
    # Select all the rows
    print('id\tfirst\tlast\tpay')
    print(('-'*30))
    for emp in get_all_emps():
        print(f'{emp.id}\t{emp.first}\t{emp.last}\t{emp.pay:,}')
    print(('-'*30))
    print()
    
    # Filter the row by id
    print('get_emp_by_id(1)▶', get_emp_by_id(1))
    
    # Filter the row by lastname
    print("get_emps_by_lastname('Kasai')▶", get_emps_by_lastname('Kasai'))
    
    # Filter the row by fullname
    print("get_emps_by_fullname('Taro', 'Kasai')▶", get_emps_by_fullname('Taro', 'Kasai'))
    
    con.close()
    
    # %%
    
    ### Insert the row
    
    con = load_data()
    c = con.cursor()
    
    emp = Employee(9, 'Taro', 'Tanaka', 9999)
    print('insert_emp(emp)▶', insert_emp(emp))
    print('get_emp_by_id(9)▶', get_emp_by_id(9))
    
    # print('get_count_by_id(9)▶', get_count_by_id(9))
    # print('id\tfirst\tlast\tpay')
    # print(('-'*30))
    # for emp in get_all_emps():
    #     print(f'{emp.id}\t{emp.first}\t{emp.last}\t{emp.pay:,}')
    
    con.commit()
    con.close()
    
    # %%
    
    ### Update the row
    
    con = load_data()
    c = con.cursor()
    
    print('Before Update:', get_emp_by_id(1))
    
    print('update_pay_by_id(1, 9999)▶', update_pay_by_id(1, 9999))
    
    print('After Update:', get_emp_by_id(1))
    
    con.commit()
    con.close()
    
    # %%  
    
    ### Delete the row  
    
    con = load_data()
    c = con.cursor()
    
    print('Before Remove▶', get_count_by_id(1))
    
    print('remove_emp_by_id(1)▶', remove_emp_by_id(1))
    
    print('After Remove▶', get_count_by_id(1))
    
    con.commit()
    con.close()
    
    # %%
    

◆PandasのDataFrameとSQLite3のテーブル間でデータを搬出・搬入する

  1. PandasのDataFrameにデータをロードする

    # Import the libraries
    import pandas as pd
    import sqlite3
    
    # Add Rows One By One To Data
    columns = ['category_name', 'page_view', 'exclude_me', 'date_added']
    data = []
    data.append(['JavaScript', 100, False, '2021-11-11'])
    data.append(['jQuery', 150, True, '2021-11-12'])
    data.append(['Python', 900, False, '2021-11-13'])
    data.append(['C++', 600, False, '2020-11-14'])
    data.append(['C#', 700, False, '2019-11-15'])
    
    # Import Data into the Pandas DatFrame
    df =pd.DataFrame(data, columns=columns)  
    df['date_added'] = pd.to_datetime(df['date_added'], format='%Y-%m-%d') # Convert string to datetime format
    #print(df)
    click image to zoom!
    図12
    図12は実行画面です。 VSCのインタラクティブ・ウィンドウにDataFrameの列名と内容が表示されています。
  2. PandasのDataFrameをSQLite3に搬出する

    # Write the DataFrame to a new SQLite table
    con = sqlite3.connect('sqlite/db/stats.db')  
    df.to_sql('stats', con, if_exists='replace', index=False)
    con.close()
    click image to zoom!
    図13-1
    VSCの左側のアイコンから[Database]▶[+]▶[SQLite]▶[Choose Database File]▶[Connect]の手順で、 行3で搬出したSQLite3のデータベース「stats.db」を表示します。


    click image to zoom!
    図13-2
    SQLite3のテーブル「stats」をクリックしてテーブルのレコードを表示します。 テーブルのウィンドウにstatsテーブルのレコードが表示されました。 これでPandasのDataFrameがSQLite3に搬出されたことが確認できました。


  3. SQLite3からPandasのDataFrameにデータを取り込む

    # Read sqlite query results into a pandas DataFrame
    con = sqlite3.connect('sqlite/db/stats.db')  
    df = pd.read_sql_query('SELECT * FROM stats', con)
    df['date_added'] = pd.to_datetime(df['date_added'], format='%Y-%m-%d') # Convert string to datetime format
    
    # Verify that result of SQL query is stored in the dataframe
    df.sort_values('page_view', ascending=False, inplace=True)
    df.style.background_gradient(subset='page_view', cmap='Greens')
    #df.style.background_gradient(subset='page_view', cmap='Reds')
    #df.style.background_gradient(subset='page_view', cmap='Blues')
    
    #con.close()    # DO NOT CLOSE
    click image to zoom!
    図14
    図14は実行結果です。 VSCのインタラクティブ・ウィンドウにPandasのDataFrameの内容が表示されています。 これでSQLite3のテーブルがPandasのDataFrameに取り込まれたことが確認できました。
  4. ここで解説したコードをまとめて掲載

    最後にここで解説したすべてのコードをまとめて掲載しましたので参考にしてください。
    
    # Import the libraries
    import pandas as pd
    import sqlite3
    
    # Add Rows One By One To Data
    columns = ['category_name', 'page_view', 'exclude_me', 'date_added']
    data = []
    data.append(['JavaScript', 100, False, '2021-11-11'])
    data.append(['jQuery', 150, True, '2021-11-12'])
    data.append(['Python', 900, False, '2021-11-13'])
    data.append(['C++', 600, False, '2020-11-14'])
    data.append(['C#', 700, False, '2019-11-15'])
    
    # Import Data into the Pandas DatFrame
    df =pd.DataFrame(data, columns=columns)  
    df['date_added'] = pd.to_datetime(df['date_added'], format='%Y-%m-%d') # Convert string to datetime format
    #print(df)
    
    # %%
    
    # Write the DataFrame to a new SQLite table
    con = sqlite3.connect('sqlite/db/stats.db')  
    df.to_sql('stats', con, if_exists='replace', index=False)
    con.close()
    
    # %%
    
    # Read sqlite query results into a pandas DataFrame
    con = sqlite3.connect('sqlite/db/stats.db')  
    df = pd.read_sql_query('SELECT * FROM stats', con)
    df['date_added'] = pd.to_datetime(df['date_added'], format='%Y-%m-%d') # Convert string to datetime format
    
    # Verify that result of SQL query is stored in the dataframe
    df.sort_values('page_view', ascending=False, inplace=True)
    df.style.background_gradient(subset='page_view', cmap='Greens')
    #df.style.background_gradient(subset='page_view', cmap='Reds')
    #df.style.background_gradient(subset='page_view', cmap='Blues')
    
    #con.close()    # DO NOT CLOSE
    
    # %%
    

◆SQLite3に格納されている「ダウ ファンダメンタルズ」をStreamlitでWebページに表示する

  1. Yahoo!Financeからダウを構成する企業のファンダメンタルズを取得してSQLite3に保存する

    Visual Studio Code(VSC)を起動したら行1-82をコピペして実行します。 行2-6ではPythonのライブラリを取り込んでいます。 ライブラリをまだインストールしていないときは「pip install」でインストールしてください。 行8-9ではCSVファイル「DJIA.csv」が存在するかどうか調べています。 存在するときは「get_tickers」に「False」が設定されます。 存在しないときは「get_tickers」に「True」が設定されます。

    行11-30ではダウを構成する企業のファンダメンタルズを取得してCSVファイル「DJIA.csv」に保存しています。 行12-14ではWikiPediaのWebサイトからダウを構成する企業のTicker(Symbol)を取得しています。 行17-19のようなTickerが取得できます。 詳しい説明は図15-1を参照してください。

    行21-28ではYahoo!FinanceのAPIを使用してダウを構成する企業(Tickerを指定する)のファンダメンタルズを取得してPandasのDataFrameに格納しています。 行30ではPandasのDataFrameをCSVファイル「DJIA.csv」に保存しています。

    行32ではCSVファイルをPandasのDataFrameに取り込んでいます。 DataFrameは行35-67のような列から構成されます。 行69-77ではDataFrameから特定の列のみ抽出してdfxのDataFrameに格納しています。 図15-2にdfxのDataFrameを表示しています。 行80-82ではdfxのDataFrameをSQLite3のデータベース「Fundamentals.db」に搬出しています。 図15-3にSQLite3のデータベースの内容を表示しています。
    # Import the libraries
    import os
    from numpy import fabs
    import yfinance as yf       # pip install yfinance
    import pandas as pd
    import sqlite3
    
    csv_file = 'data/csv/article039/DJIA.csv'   # Dow Jones Industrial Average (DJIA)
    get_tickers = not os.path.exists(csv_file)  # True => False, Fale => True
    
    if get_tickers:
        url = 'https://en.wikipedia.org/wiki/Dow_Jones_Industrial_Average'
        tickers = pd.read_html(url)[1]
        tickers = tickers.Symbol.to_list()
        #print(tickers)
    
        #tickers = ['MMM', 'AXP', 'AMGN', 'AAPL', 'BA', 'CAT', 'CVX', 'CSCO', 'KO', 'DOW', 
        # 'GS', 'HD', 'HON', 'IBM', 'INTC', 'JNJ', 'JPM', 'MCD', 'MRK', 'MSFT', 'NKE', 'PG', 
        # 'CRM', 'TRV', 'UNH', 'VZ', 'V', 'WBA', 'WMT', 'DIS']
    
        df = pd.DataFrame()
        pd.set_option('display.max.columns', 154)
        pd.set_option('display.max.rows', 50)
    
        for ticker in tickers:
            var = yf.Ticker(ticker).info
            frame = pd.DataFrame([var])
            df = df.append(frame)
    
        df.to_csv(csv_file, index=False)    # overwrite the file by default
    
    df = pd.read_csv(csv_file)
    
    ### df.columns:
    # zip	sector * fullTimeEmployees	longBusinessSummary	city	phone	
    # state	country	companyOfficers	website	maxAge	address1	industry	
    # ebitdaMargins	profitMargins *	grossMargins	operatingCashflow *	revenueGrowth	
    # operatingMargins	ebitda	targetLowPrice	recommendationKey	grossProfits *	
    # freeCashflow *	targetMedianPrice	currentPrice	earningsGrowth	currentRatio	
    # returnOnAssets	numberOfAnalystOpinions	targetMeanPrice	debtToEquity	
    # returnOnEquity	targetHighPrice	totalCash	totalDebt *	totalRevenue *	
    # totalCashPerShare *	financialCurrency	revenuePerShare	quickRatio	
    # recommendationMean	exchange	shortName	longName	exchangeTimezoneName	
    # exchangeTimezoneShortName	isEsgPopulated	gmtOffSetMilliseconds	quoteType	
    # symbol *	messageBoardId	market	annualHoldingsTurnover	enterpriseToRevenue	
    # beta3Year	enterpriseToEbitda	52WeekChange	morningStarRiskRating	forwardEps	
    # revenueQuarterlyGrowth	sharesOutstanding	fundInceptionDate	
    # annualReportExpenseRatio	totalAssets	bookValue	sharesShort	
    # sharesPercentSharesOut	fundFamily	lastFiscalYearEnd	heldPercentInstitutions	
    # netIncomeToCommon	trailingEps	lastDividendValue	SandP52WeekChange	
    # riceToBook	heldPercentInsiders	nextFiscalYearEnd	yield	mostRecentQuarter	
    # shortRatio	sharesShortPreviousMonthDate	floatShares	beta	enterpriseValue	
    # priceHint	threeYearAverageReturn	lastSplitDate	lastSplitFactor	legalType	
    # lastDividendDate	morningStarOverallRating	earningsQuarterlyGrowth	
    # priceToSalesTrailing12Months	dateShortInterest	pegRatio	ytdReturn *	
    # forwardPE	lastCapGain	shortPercentOfFloat	sharesShortPriorMonth	
    # impliedSharesOutstanding	category	fiveYearAverageReturn	previousClose	
    # regularMarketOpen	twoHundredDayAverage	trailingAnnualDividendYield	payoutRatio	
    # volume24Hr	regularMarketDayHigh	navPrice	averageDailyVolume10Day	
    # regularMarketPreviousClose	fiftyDayAverage	trailingAnnualDividendRate	
    # open	toCurrency	averageVolume10days	expireDate	algorithm	dividendRate	
    # exDividendDate	circulatingSupply	startDate	regularMarketDayLow	currency	
    # trailingPE *	regularMarketVolume	lastMarket	maxSupply	openInterest	
    # marketCap	volumeAllCurrencies	strikePrice	averageVolume	dayLow	ask	askSize	
    # volume	fiftyTwoWeekHigh	fromCurrency	fiveYearAvgDividendYield	
    # fiftyTwoWeekLow	bid	tradeable	dividendYield	bidSize	dayHigh	
    # regularMarketPrice	preMarketPrice	logo_url	fax	address2
    
    dfx = df[
        [   
            'sector', 'symbol', 
            'profitMargins', 'operatingCashflow', 'freeCashflow',
            'totalDebt', 'totalRevenue', 'totalCashPerShare',
            'dividendYield', 'shortRatio', 'ytdReturn',
            'forwardPE', 'trailingPE'
        ]
    ]
    
    # Write the DataFrame to a new SQLite table
    con = sqlite3.connect('sqlite/db/Fundamentals.db')  
    dfx.to_sql('FundamentalsTable', con, if_exists='replace', index=False)
    con.close()
    click image to zoom!
    図15-1
    図15-1はWikiPediaのWebページの画像です。 行13のPandasのread_html()メソッドでは、このWikiPediaのWebサイトから表(HTMLのtableタグ)を取得します。 このサイトには2個の表(HTMLのtableタグ)が表示されているのでここでは2番目の表を取得しています。

    行14ではPandasのDataFrameから列「Symbol」のデータを取得してlist型で変数「tickers」に格納します。 この変数には行17-19のようなTickerが格納されます。 WikiPediaのWebサイトの表の「Symbol=Ticker」が取得できたことになります。


    click image to zoom!
    図15-2
    図15-2はVisual Studio Code(VSC)の画面です。 ここでは行32のPandasのread_csv()メソッドで取り込んだデータから特定の列のみ絞り込んでいます。 画面にはdfxのDataFrameの列名が表示されています。 次のステップではこのdfxをSQLite3に搬出します。


    click image to zoom!
    図15-3
    図15-3はVisual Studio Code(VSC)の画像です。 ここではCSVのExtensions「MySQL」を使用してSQLite3のデータベース「Fundamentals.db」のテーブル「FundamentalsTable」を表示しています。 PandasのDataFrameが正常にSQLite3に搬出されたことが確認できました。


  2. SQLite3に格納されているダウのファンダメンタルズをWebページに表示する

    Visual Studio Code(VSC)から行1-30をコピペしたら「Article039_Run.py」の名前で保存します。 ここで保存したPythonのファイルはWebアプリなのでVSCの「TERMINAL」もしくはWindowsのコマンドプロンプトから実行します。 ここではVSCのTEMINALから「streamlit run Article039_Run.py」のコマンドを入力して実行します。

    ブラウザにダウのファンダメンタルズが表示されます。 操作手順は図16-1から図16-3で説明します。
    # Import the libraries
    import streamlit as st
    import pandas as pd
    import sqlite3
    
    def load_data(con):
        df = pd.read_sql_query('SELECT * FROM FundamentalsTable', con)
        df.set_index('symbol', inplace=True)
        return df
    
    con = sqlite3.connect('sqlite/db/Fundamentals.db') 
    
    df = load_data(con)
    
    st.title('DJIA Fundamentals Comparison')
    st.header('ダウ ファンダメンタルズ比較')
    
    dropdownI = st.selectbox('Choose your sector (セクター選択):', df.sector.unique())
    # numpy.ndarry
    
    dropdownII = st.selectbox('Choose your metrics (メトリクス選択):', df.columns[df.columns != 'sector'])
    # df.columns[df.columns != 'sector'] => Pandas multiple indexdes
    
    dfx = df[df.sector == dropdownI][[dropdownII]]
    
    st.bar_chart(dfx)    # Draw a bar chart
    
    con.close()
    
    # Run from TERMINAL: streamlit run Article039_Run.py
    click image to zoom!
    図16-1
    図16-1はVisual Studio Code(VSC)の画面です。 ここではVSCのTERMINALから「streamlit run Article039_Run.py」のコマンドを入力してアプリを実行させています。


    click image to zoom!
    図16-2
    図16-2はブラウザの画面です。 ブラウザに「DJIA Fundamentals Comparison」が表示されています。 このWebページからは「セクター」と「メトリクス」を選択して棒グラフを表示させることができます。 ここでは「セクターの選択」から「Technology」、「メトリクス」から「profitMargins」を選択して棒グラフを表示させています。

    Apple, IBMなどのProfit Marginsが表示されています。
    click image to zoom!
    図16-3
    ここでは「セクターの選択」から「Technology」、「メトリクス」から「operatingCashflow」を選択して棒グラフを表示させています。

    Apple, IBMなどのOperating Cashflowが表示されています。
  3. ここで解説したコードをまとめて掲載

    最後にここで解説したすべてのコードをまとめて掲載しましたので参考にしてください。
    
    Article039_Load_Data.py:
    
    # Import the libraries
    import os
    from numpy import fabs
    import yfinance as yf       # pip install yfinance
    import pandas as pd
    import sqlite3
    
    csv_file = 'data/csv/article039/DJIA.csv'   # Dow Jones Industrial Average (DJIA)
    get_tickers = not os.path.exists(csv_file)  # True => False, Fale => True
    
    if get_tickers:
        url = 'https://en.wikipedia.org/wiki/Dow_Jones_Industrial_Average'
        tickers = pd.read_html(url)[1]
        tickers = tickers.Symbol.to_list()
        #print(tickers)
    
        #tickers = ['MMM', 'AXP', 'AMGN', 'AAPL', 'BA', 'CAT', 'CVX', 'CSCO', 'KO', 'DOW', 
        # 'GS', 'HD', 'HON', 'IBM', 'INTC', 'JNJ', 'JPM', 'MCD', 'MRK', 'MSFT', 'NKE', 'PG', 
        # 'CRM', 'TRV', 'UNH', 'VZ', 'V', 'WBA', 'WMT', 'DIS']
    
        df = pd.DataFrame()
        pd.set_option('display.max.columns', 154)
        pd.set_option('display.max.rows', 50)
    
        for ticker in tickers:
            var = yf.Ticker(ticker).info
            frame = pd.DataFrame([var])
            df = df.append(frame)
    
        df.to_csv(csv_file, index=False)    # overwrite the file by default
    
    df = pd.read_csv(csv_file)
    
    ### df.columns:
    # zip	sector * fullTimeEmployees	longBusinessSummary	city	phone	
    # state	country	companyOfficers	website	maxAge	address1	industry	
    # ebitdaMargins	profitMargins *	grossMargins	operatingCashflow *	revenueGrowth	
    # operatingMargins	ebitda	targetLowPrice	recommendationKey	grossProfits *	
    # freeCashflow *	targetMedianPrice	currentPrice	earningsGrowth	currentRatio	
    # returnOnAssets	numberOfAnalystOpinions	targetMeanPrice	debtToEquity	
    # returnOnEquity	targetHighPrice	totalCash	totalDebt *	totalRevenue *	
    # totalCashPerShare *	financialCurrency	revenuePerShare	quickRatio	
    # recommendationMean	exchange	shortName	longName	exchangeTimezoneName	
    # exchangeTimezoneShortName	isEsgPopulated	gmtOffSetMilliseconds	quoteType	
    # symbol *	messageBoardId	market	annualHoldingsTurnover	enterpriseToRevenue	
    # beta3Year	enterpriseToEbitda	52WeekChange	morningStarRiskRating	forwardEps	
    # revenueQuarterlyGrowth	sharesOutstanding	fundInceptionDate	
    # annualReportExpenseRatio	totalAssets	bookValue	sharesShort	
    # sharesPercentSharesOut	fundFamily	lastFiscalYearEnd	heldPercentInstitutions	
    # netIncomeToCommon	trailingEps	lastDividendValue	SandP52WeekChange	
    # riceToBook	heldPercentInsiders	nextFiscalYearEnd	yield	mostRecentQuarter	
    # shortRatio	sharesShortPreviousMonthDate	floatShares	beta	enterpriseValue	
    # priceHint	threeYearAverageReturn	lastSplitDate	lastSplitFactor	legalType	
    # lastDividendDate	morningStarOverallRating	earningsQuarterlyGrowth	
    # priceToSalesTrailing12Months	dateShortInterest	pegRatio	ytdReturn *	
    # forwardPE	lastCapGain	shortPercentOfFloat	sharesShortPriorMonth	
    # impliedSharesOutstanding	category	fiveYearAverageReturn	previousClose	
    # regularMarketOpen	twoHundredDayAverage	trailingAnnualDividendYield	payoutRatio	
    # volume24Hr	regularMarketDayHigh	navPrice	averageDailyVolume10Day	
    # regularMarketPreviousClose	fiftyDayAverage	trailingAnnualDividendRate	
    # open	toCurrency	averageVolume10days	expireDate	algorithm	dividendRate	
    # exDividendDate	circulatingSupply	startDate	regularMarketDayLow	currency	
    # trailingPE *	regularMarketVolume	lastMarket	maxSupply	openInterest	
    # marketCap	volumeAllCurrencies	strikePrice	averageVolume	dayLow	ask	askSize	
    # volume	fiftyTwoWeekHigh	fromCurrency	fiveYearAvgDividendYield	
    # fiftyTwoWeekLow	bid	tradeable	dividendYield	bidSize	dayHigh	
    # regularMarketPrice	preMarketPrice	logo_url	fax	address2
    
    dfx = df[
        [   
            'sector', 'symbol', 
            'profitMargins', 'operatingCashflow', 'freeCashflow',
            'totalDebt', 'totalRevenue', 'totalCashPerShare',
            'dividendYield', 'shortRatio', 'ytdReturn',
            'forwardPE', 'trailingPE'
        ]
    ]
    
    # Write the DataFrame to a new SQLite table
    con = sqlite3.connect('sqlite/db/Fundamentals.db')  
    dfx.to_sql('FundamentalsTable', con, if_exists='replace', index=False)
    con.close()
    
    # %%
    
    # # Read sqlite query results into a pandas DataFrame
    # con = sqlite3.connect('sqlite/db/Fundamentals.db') 
    # dfx = pd.read_sql_query('SELECT * FROM FundamentalsTable', con)
    # dfx.set_index('symbol', inplace=True)
    # # Verify that result of SQL query is stored in the dataframe
    # print(dfx.head())
    
    # con.close()
    
    # %%
    
    
    Article039_Run.py:
    
    # Import the libraries
    import streamlit as st
    import pandas as pd
    import sqlite3
    
    st.markdown(
        """
        <style>
        .my-css {
            background-color: #F5F5F5;
            color: red;
        }
        </style>
        """,
        unsafe_allow_html=True
    )
    
    #@st.cache
    #@st.cache(hash_funcs={sqlite3.Connection: id})
    #@st.cache(hash_funcs={pd.DataFrame: lambda _: None})
    def load_data(con):
        #con = sqlite3.connect('sqlite/db/Fundamentals.db') 
        df = pd.read_sql_query('SELECT * FROM FundamentalsTable', con)
        df.set_index('symbol', inplace=True)
        return df
    
    con = sqlite3.connect('sqlite/db/Fundamentals.db') 
    
    df = load_data(con)
    
    st.title('DJIA Fundamentals Comparison')
    st.header('ダウ ファンダメンタルズ比較')
    
    dropdownI = st.selectbox('Choose your sector (セクター選択):', df.sector.unique())
    # numpy.ndarry
    
    dropdownII = st.selectbox('Choose your metrics (メトリクス選択):', df.columns[df.columns != 'sector'])
    # df.columns[df.columns != 'sector'] => Pandas multiple indexdes
    
    dfx = df[df.sector == dropdownI][[dropdownII]]
    
    st.bar_chart(dfx)    # Draw a bar chart
    
    con.close()
    
    # Run from TERMINAL: streamlit run Article039_Run.py