タイトル : 血圧降下剤の処方数量 データ追加
更新日 : 2024-03-20
カテゴリ : プログラミング
タグ :
opendata   
python   
postgres   

EXCELファイルを読み込んで、データ追加SQLを作成

ダウンロードしたEXCELファイルです

$ ls *性年齢別薬効分類別数量*
2014_性年齢別薬効分類別数量.xlsx  2018_性年齢別薬効分類別数量.xlsx
2015_性年齢別薬効分類別数量.xlsx  2019_性年齢別薬効分類別数量.xlsx
2016_性年齢別薬効分類別数量.xlsx  2020_性年齢別薬効分類別数量.xlsx
2017_性年齢別薬効分類別数量.xlsx  2021_性年齢別薬効分類別数量.xlsx
$ 

openpyxlでEXCELファイルを読み込んで、データベースのテーブルにデータを追加するSQLファイルを作成します。データ追加はINSERT文ではなくCOPY文で行います。その方が速いみたい

以下がそのPythonスクリプトです

import os
import argparse
import re
from openpyxl import load_workbook

parser = argparse.ArgumentParser(description='NDBオープンデータのEXCELを読み込んでSQLファイルを作成する')
parser.add_argument('book_file', help='EXCELファイル')
args = parser.parse_args() 

# 年度を決める ファイル名は 2021_性年齢別薬効分類別数量.xlsx になっているとする
m = re.search("(\d+)_*", os.path.basename(args.book_file))
year = int(m.group(1))

# 出力SQLファイル
f_outs = {}
for key in ["category", "code", "quantity", "price"]:
    f_outs[key] = open(f"insert_{key}_{year}.sql", "w")

# TRUNCATE文の出力
f_outs["category"].write("truncate public.iy_category cascade;\n")
f_outs["code"].write("truncate public.iy_code cascade;\n")
# f_outs["quantity"].write("truncate public.iy_quantity;\n")
# f_outs["price"].write("truncate public.iy_price;\n")

# COPY文の出力
f_outs["category"].write(
    "COPY public.iy_category (id, name) FROM stdin;\n")
f_outs["code"].write(
    "COPY public.iy_code (id, iy_category_id, name) FROM stdin;\n")
# idはautoincrementで指定なし
f_outs["quantity"].write(
    "COPY public.iy_quantity (iy_code_id,year,quantity) FROM stdin;\n")
f_outs["price"].write(
    "COPY public.iy_price (iy_code_id,year,price,generic) FROM stdin;\n")

# ワークブックの読み込み
wb = load_workbook(args.book_file)

# ワークシートを取得
sheet_names = [sheet_name for sheet_name in wb]

# 最初のシートを使う
sheet = sheet_names[0]

# 薬効分類
iy_cat_code_col_name = "薬効\n分類"

iy_code_col_name = "医薬品\nコード"
col_names = {
    "name":"医薬品名",
    "code": "医薬品\nコード",
    "price": "薬価",
    "generic" : "後発品\n区分",
    "quantity" : "総計"
}
col_pos = dict()

# 薬効分類のコードパターン
iy_cat_code_pattern = re.compile(r'^\d{3}$')

iy_code_active = False
iy_category = -1

# 行のループ
for i_r, row in enumerate(sheet.iter_rows()):
    # A列
    col_a = str(row[0].value)

    # 薬効分類の処理
    if col_a and iy_cat_code_pattern.fullmatch(col_a):
        col_b = str(row[1].value)
        iy_category = int(col_a)
        f_outs["category"].write(f"{int(col_a)}\t{col_b}\n")

        if not iy_code_active:
            iy_code_active = True

    # 薬効分類の行
    if col_a == iy_cat_code_col_name:
        # 列の位置を求めておく
        for i_c, col in enumerate(row):
            for col_name in col_names:
                if str(col.value) == col_names[col_name]:
                    col_pos[col_name] = i_c

    # 医薬品コードの処理
    if iy_code_active:
        iy_name = str(row[col_pos["name"]].value)
        iy_code = str(row[col_pos["code"]].value)
        iy_price = str(row[col_pos["price"]].value)
        iy_generic_str = str(row[col_pos["generic"]].value)
        iy_quantity_str = str(row[col_pos["quantity"]].value)
        if iy_generic_str == "1":
            iy_generic = True
        else:
            iy_generic = False
        if iy_quantity_str == "-":
            iy_quantity = 0
        else:
            iy_quantity = int(float(iy_quantity_str))

        f_outs["code"].write(
            f"{iy_code}\t{iy_category}\t{iy_name}\n")
        f_outs["quantity"].write(
            f"{iy_code}\t{year}\t{iy_quantity}\n")
        f_outs["price"].write(
            f"{iy_code}\t{year}\t{iy_price}\t{iy_generic}\n")

# COPYの最後に \. を付加する
for key in f_outs:
    f_outs[key].write("\\.\n")

一部分だけだけど、以下のSQLファイルを出力します

$ head -5 insert_price_2021.sql
COPY public.iy_price (iy_code_id,year,price,generic) FROM stdin;
620049101	2021	5.1	True
620049901	2021	5.7	True
611170508	2021	7	False
610443047	2021	30.9	False
$

SQLファイルを使ってデータを追加しました。 薬効分類 112個、医薬品が6736ですね。(今更だけど、薬の種類ってもっとあるかと思ってました...1万に届かないのですね...)

root@73dd79b8e94d:/insert-sql# psql -U bpress bpress
psql (16.1 (Debian 16.1-1.pgdg120+1))
Type "help" for help.

bpress=# select count(id) from iy_category;
 count 
-------
   112
(1 row)

bpress=# select count(id) from iy_code;
 count 
-------
  6736
(1 row)

bpress=# select count(id) from iy_price;
 count 
-------
 36350
(1 row)

bpress=#