【Python】Jupyter notebookでOracle Databaseからデータ取得してExcellに書き出す方法


## はじめに
Excelの自動生成&OracleDBのデータ自動出力の方法を探していたところ、過去に一度触ったことのあるJupyter notebookで簡単にできそうだったのでやってみました。

## 環境
Windows 10 Pro
Jupyter notebook 6.4.5
Python 3.9.7

## 環境構築
Anacondaと一緒に`Jupyter Notebook`をインストール。（下記記事参考にしました）

https://ai-inter1.com/jupyter-notebook/

###### Jupyter notebook
※ PythonなどをWebブラウザ上で記述・実行できる統合開発環境

---
`cx_Oracle` と `Oracle Instant Client` をインストール。
迷った箇所は公式ドキュメントを参照したらうまくできました。

https://cx-oracle.readthedocs.io/en/latest/user_guide/installation.html#installing-cx-oracle-on-windows


###### cx_Oracle
※ PythonのDB接続APに基づいて実装され、OracleDBに簡単にアクセスするためのモジュール

*Anaconda Prompt* で `python -m pip install cx_Oracle --upgrade` としてインストール。
![doc0001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631585/483975a0-e101-7bef-200a-2eac0e50e51f.png)



##### Oracle Instant Client
※ オラクル社が提供する無料かつ軽量なライブラリ及び SDK。ローカルのデータベースやリモートの Oracle データベースに接続できる

https://www.oracle.com/jp/database/technologies/instant-client/downloads.html

`Instant Client for Microsoft Windows (x64)` のタブから　
*Basic Package* `instantclient-basic-windows.x64-21.3.0.0.0.zip`をダウンロードして、適当な場所にディレクトリを作りunzip

一応、環境変数 PATHにOracle Instanct Clientを解凍したパスを追加する。

---
#### Jupyter Notebookで動作確認
```python
import cx_Oracle

cx_Oracle.init_oracle_client(lib_dir=r"C:\oracle\instantclient_19_11")

cx_Oracle.clientversion()
```
`"C:\oracle\instantclient_19_11"`の部分は上でunzipしたディレクトリを指定

正常にバージョン表示さればOK
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631585/bc759c48-56d8-1115-50d1-d47a3c7ad754.png)

## 実装（Jupyter Notebook）
**インポート**

```python
import openpyxl
import glob
import os
import cx_Oracle
cx_Oracle.init_oracle_client(lib_dir="C:\Oracle\instantclient_21_3")
```

**Excel作成**

```python
wb = openpyxl.Workbook()
sheet = ws = wb.active
# シート名修正
sheet.title = "oracle_data"
```

**OracleDBからデータを取得**

```python
# DB接続パラメータ設定
host = '******'
port = ****
sid = '******'
userId = '******'
pw = '*****'

# TNSサービス名を設定
tns = cx_Oracle.makedsn(host, port, sid)
# DBに接続
connect = cx_Oracle.connect(userId, pw, tns) 
# カーソル生成
cursor = connect.cursor() 
# SQL発行
cursor.execute('''select * from sample where id = :arg''', arg="00001")

# データ取得
rows = cursor.fetchall()
# 取得データ保持
target_data = rows 
# カラム名保持
target_data_description = [str[0] for str in cursor.description]

cursor.close() # カーソル解放
connect.close() # コネクションの解放
```

**取得したデータをExcelに書き出し**

```python
row_num = 1 # 行番号
col_num = 1 # 列番号

# Excelの一行目にカラム名書き出し
for description in target_data_description:
    cell = sheet.cell(row=row_num, column=col_num)
    cell.value = description
    col_num += 1

row_num += 1

# 取得データを二行目から順に書き出し
for tuple_data in target_data:
    col_num = 1
    
    for data in tuple_data:
        cell = sheet.cell(row=row_num, column=col_num)
        cell.value = data
        col_num += 1

    row_num += 1

# Excelを保存
wb.save('test.xlsx')
```

## 感想
最低限のやりたいことはできました。
実用化するにはもう少し手を加える必要がありますが、自由度高くやりたいことが実現できそうなので、継続して改良していこうと思います。

## おまけ
**SID(security identifier) とは**
ユーザー アカウント、グループ アカウント、およびコンピュータ アカウントを識別するデータ構造体。SID は、アクセス制御 (リソースを使用できる SID を指定するリソース セキュリティ記述子と SID を比較するプロセス) で使用される。

**DSN(Data Source Name)とは**
ODBCなどのデータベース接続インターフェースにおいて、プログラム側が操作対象のデータベースを指定するための識別名。この識別名を含む接続文字列のことを指してDSNという場合もある。

**TNS(Transparent Network Substrate) とは**
Oracle データベースが使用する通信層。 TNSサービス名は、ネットワーク上での Oracle データベース インスタンスの名前です。 TNSサービス名は、Oracle データベースへの接続を構成するときに割り当てます。 レプリケーションでは TNSサービス名を使用してパブリッシャーを識別し、接続を確立します。

## 参考記事
https://blog.mylab.jp/posts/2018051101/

https://qiita.com/kngsym2018/items/04514d623248a070adee

http://everything-you-do-is-practice.blogspot.com/2017/11/oracle-instant-client-windows.html

https://gammasoft.jp/support/how-to-use-openpyxl-for-excel-file/#file-create

https://qiita.com/sky_jokerxx/items/dc9d8827d946b467ba4b