IBM Cloud Object StorageファイルをDB2へロードする(1)

# 1.目的

IBM Cloud SQL Query では、IBM Cloud Object StorageからIBM Db2　on Cloud へのデータの転送および変換の自動化がサポートされています。当文書及び次編ではWatsonStudio(PackforData)/Notebookを使いICOS上のFileからDB2テーブル・ロードを実施して、手順の確認及び性能測定テスト/チューニングを実施しました。参考までにDB2(Native)clientでも同様の内容を実施して比較検証しております。

IBM Cloud Object StorageファイルをDB2へロードする(1) :本文書
[IBM Cloud Object StorageファイルをDB2へロードする(2)~SQL Query 性能チューニングについて](https://qiita.com/nori0315/items/5612b3b5721f13de5607)


![スクリーンショット 2022-05-18 19.07.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132988/ce895a4d-0ae6-7ee1-1517-906f3b4ed25b.png)

# 2.作業手順　
## 準備
### 前提条件
1. WatsonStudioインスタンスの起動/Project作成/Notebookの準備
2. DB2インスタンス作成/CRN(Cloud Resource Name)情報入手
3. SQL Queryインスタンス作成/CRN(Cloud Resource Name)情報入手

まずはサービス・インスタンス接続のための権限設定を行います。
### APIkeyの作成
IAMでまずサービスIDを作成し、アクセスポリシーの設定をします。Cloud Portalの最上部メニュー[管理]-[アクセス(IAM)]-サービスIDを選択して新規作成のボタンを押します。ここではICOS,SQL Query,DB2の管理者アクセス権限を付与します。
![スクリーンショット 2022-05-10 12.29.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132988/abdec165-18cf-a61f-c24c-94829ccb9d27.png)

APIキーのタブでAPIキーを作成して、APIキーをメモしておきます。
![スクリーンショット 2022-05-10 12.29.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132988/e271a226-235a-9d84-b505-efc7c6151c27.png)

### DB2新規資格情報を作成
DB2では新規資格情報を作成します。PortalのDB2初期画面の左メニューよりサービス資格情報を選択して新規資格情報ボタンを押して接続に必要になるusername/password/host/port等の情報を入手しておきます。
![スクリーンショット 2022-05-10 12.42.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132988/7c5a95e2-1ab3-78a7-27b8-d0db5b6936dd.png)

## SQL Queryを利用したPythonCoding
Pythonのコードを入力していきます。WatsonStudioのNotebookを開きます。前提となるSWを導入します。

![スクリーンショット 2022-05-09 11.41.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132988/b6ffef7b-c8ae-0c9d-6099-9cb148f164a8.png)
　　　　　　　　　　　中途省略
![スクリーンショット 2022-05-09 11.42.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132988/6c83a32c-3c20-a77b-e90e-036e13509d62.png)
```
!pip install --upgrade ibmcloudsql
import ibmcloudsql
```

ibmcloudsqlというのは、SQL Queryを実行するためのPythonクライアントです。以下のページに詳細説明があります。

https://github.com/IBM-Cloud/sql-query-clients/blob/master/Python/README.md

先ほど入手したAPIキー、SQLQueryインスタンスCRNを入力します。
sql_cos_endpointには、SQL Queryの実行結果を保管するObject Storageの場所を指定します。以下フォーマットになります。
cos://< エンドポイント>/< バケット名>/< エイリアス名またはファイル名>

![スクリーンショット 2022-05-09 11.43.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132988/d34922cc-6d20-ee0c-bd47-e50fe4759a3a.png)

```
#cloud_api_key 入力　(ICOS,SQLQuery,DB2権限)
cloud_api_key = "*************"

#SQLQuery Instance crn 入力 
sql_crn = "crn:v1:bluemix:public:sql-query:us-south:a/039dbe6794084c7cb514a276dd2345da:82c3edab-8a45-4f0d-b598-922cafef8b83::"

#SQLQuery Job log 出力先
sql_cos_endpoint = "cos://jp-tok/dbload/result/"

sqlClient = ibmcloudsql.SQLQuery(cloud_api_key,sql_crn,sql_cos_endpoint)
```
次にSQL Query文を入力します。
FROM cos://< エンドポイント>/< バケット名>/< ファイル名>
into < DB2サービスCRN名>/< スキーマ名>.< テーブル名>
の形式で指定します。スキーマ名を省略するとusernameが自動付与されます。

![スクリーンショット 2022-05-09 12.13.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132988/a1766bc1-1e87-c6cd-7248-6fdc6a607644.png)

```
#SQL文の入力　(from: ICOS CSV名, into: DB2 table名)
query = """SELECT *
FROM cos://jp-tok/dbload/data10.csv STORED AS CSV
into crn:v1:bluemix:public:dashdb-for-transactions:jp-tok:a/039dbe6794084c7cb514a276dd2345da:4299a8bc-68c6-4080-895f-cfa4e3cf78fe::/employee30"""

#SQL文の実行
kekka = sqlClient.run_sql(query)

```
結果はIBM Cloud Portalで標準提供されるDB2コンソールやSQLQueryコンソール等から確認できます。

![スクリーンショット 2022-05-09 12.31.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132988/91dd8c56-027f-42d8-cf7a-ee853ce1d3fa.png)

## DB2clientを利用したPythonCoding
ICOSにあるCSVファイルを一度Pandasデータフレームとして取り込み、それをibmdbpyを使ってDb2 on Cloudの表に書き出します。
ICOSやDb2 on Cloudに接続するために必要な認証情報は、手動でコードを記述することもできますが、予めWatson StudioのプロジェクトにICOS上のファイルをデータ・アセットとして、DB2接続をデータアクセスとして登録しておけば、認証情報をNotebookのセルに自動で取り込むことができ、Coding負荷が大幅に軽減されます。

![スクリーンショット 2022-05-10 16.14.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132988/2558d9fa-7ad5-1fa8-f7b4-f84ddbd99a5c.png)
![スクリーンショット 2022-05-10 16.20.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132988/949d9232-c768-483d-f3d1-e2648a34743c.png)

Notebookの画面右の方にある"1010"アイコン（"Find and add data"アイコン）をクリックしてデータ・アセット一覧を表示、事前に作成しておいたデータアセットemploy_icos(CSVファイル)の下に表示されている"コードに挿入"をクリック、さらに"Insert pandas DataFrame”を選択すると、そのCSVファイルをPadasデータフレームとして取り込むコードが自動入力されます。

![スクリーンショット 2022-05-09 13.36.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132988/defb3e88-0eda-78f4-8c46-8ae56f826afb.png)

```
import types
import pandas as pd
import ibm_boto3
from botocore.client import Config

def __iter__(self): return 0

# @hidden_cell
# The following code accesses a file in your IBM Cloud Object Storage. It includes your credentials.
# You might want to remove those credentials before you share your notebook.

ICOS_client = ibm_boto3.client(
    service_name='s3',
    ibm_api_key_id='xxxxxxxxx',
    ibm_service_instance_id='crn:v1:bluemix:public:cloud-object-storage:global:a/039dbe6794084c7cb514a276dd2345da:78ee3380-6b51-4cc5-b61f-f0bd9d3e0fbd::',
    ibm_auth_endpoint='https://iam.cloud.ibm.com/identity/token',
    config=Config(signature_version='oauth'),
    endpoint_url='https://s3.jp-tok.cloud-object-storage.appdomain.cloud'
)

body = ICOS_client.get_object(Bucket='dbload', Key='data10.csv')['Body']
# add missing __iter__ method, so pandas accepts body as file-like object
if not hasattr(body, "__iter__"): body.__iter__ = types.MethodType( __iter__, body )

df_data_1 = pd.read_csv(body)
df_data_1.head()
```
Notebookの画面右の方にある"1010"アイコン（"Find and add data"アイコン）をクリックして今度は”接続"とある方を選択、登録済みのdb2connectの下に表示されている"コードに挿入"をクリック、さらに"資格情報”を選択すると、接続のために必要な認証情報についての変数定義がセルに自動入力されます。（下記例におけるcredential_1の部分）
Db2への接続はIdaDatabaseによって作成されます。
![スクリーンショット 2022-05-09 13.37.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132988/4531cb1f-b0b0-a682-26c9-a684710456b7.png)

```
# @hidden_cell
# The following code contains the credentials for a connection in your Project.
# You might want to remove those credentials before you share your notebook.
credentials_1 = {
    'username': 'uuuuuuuuu',
    'password': """ppppppppp""",
    'database': 'bludb',
    'host': '4299a8bc-68c6-4080-895f-cfa4e3cf78fe.bs2ipmbt0ifld8rdc9u0.databases.appdomain.cloud',
    'port': '30450',
}

dsn = 'DASHDB;Database={};Hostname={};Port={};PROTOCOL=TCPIP;UID={};PWD={};SECURITY=SSL'.format(
    credentials_1['database'],
    credentials_1['host'],
    credentials_1['port'],
    credentials_1['username'],
    credentials_1['password']
)

!pip install ibmdbpy
from ibmdbpy import IdaDataBase, IdaDataFrame

idadb = IdaDataBase(dsn)
```

作成した表EMPLOYEE30を参照するIDAデータフレームida_dfを定義し、Pandasデータフレームdf_data_1の内容を書き出してから、コミット処理をしています。最後にDb2への接続を切断するためにはclose()を実行します。

![スクリーンショット 2022-05-10 13.35.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132988/85d10f48-fb22-c216-db5d-86f47beeb52a.png)

```
ida_df = IdaDataFrame(idadb, 'EMPLOYEE30')
idadb.append(ida_df, df_data_1)

idadb.commit

idadb.close()
```

## DB2 client vs SQL Queryとの性能比較
### ケース1 CSV FileのDB2ロード時間
1つのCSV FileをDB2にロードして時間を測定したところ以下のようになりました。

| Fileサイズ | SQL Query | DB2Client |
|:-:|:-:|:-:|
| 22Kbyte  | 21.98秒  | 4.35秒 |
| 11.2Mbyte  | 49.6秒  | 6.15秒 |
| 100.8Mbyte  | 173.56秒  | 18.19秒  |

WatsonStudio Notebookリソース: 1vCPU,4GB RAM

### ケース2 複数ParquetFileのDB2ロード時間

業務プロトタイプ作成して7*Parquet Fileから3columnを抽出してDB2にLoadする時間を測定

Parquet File: 1.2~2.4MB x 7 File,約3,200万行
Notebook: (2vCPU, 8GB MEM)

|  | SQL Query | DB2Client |
|:-:|:-:|:-:|
| 処理時間  | 59分55秒 | 39分40秒  |

*5回実施平均値
*SQLQueryはIBM Cloud SQLQueryコンソール上での処理時間,WatsonStudioより実行するとさらに時間かかると考えられる。

いずれのケースもDB2Clientの方が処理速度は速いという結論が得られた。
ICOS上でpartition化されたファイルを処理するのと異なり、DB2に１つのテーブルを作成するだけなのでSparkQueryの並列処理のメリットが生かされないと考えられます。

# 3.まとめ
SQLQueryは、ICOS上の大量データ(サイズ,数)のDataLakeのPipeLine処理全体に適用するのは有効であるが、単独でDB2テーブルにLoadするのであれば、DB2 Nativeで実施するのに比較して　

(1)パフォーマンス劣化
(2)現時点ではDB2のテーブル新規作成のみ可能、既存に同一テーブルがある場合は削除後に新規にテーブルを作成(文字列型はVARCHAR(32000)にマップ)、したがいデータ形式check等は別途実施する必要ある、
(3)WatsonStudioのデータ資産としてSQLQueryの接続を登録しても認証情報などのコード自動挿入はサポートされない(現時点ではICOSデータからの結果表示/ICOS出力する時のみサポート)

等の制約があり、これらを踏まえた上で利用を検討するのがベターです。
なおSQLQueryにはDB2Load時に複数の並列データベース接続を開き処理することがサポートされており次回これを検証予定です。

[IBM Cloud Object StorageファイルをDB2へロードする[2]~SQL Query Parallelismについて]


![スクリーンショット 2022-05-18 12.49.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132988/cfec7e2a-1315-3859-c0f7-f824ff125ede.png)

# 4.サンプルプログラム
上記作成したNotebook/Pythonプログラムを添付します。
SQL Query
https://github.com/saka0315/220502icos2db2/blob/main/sqlquery1.ipynb

db2client
https://github.com/saka0315/220502icos2db2/blob/main/db2client.ipynb

# 5.関連リンク
[IBM Cloud Object StorageファイルをDB2へロードする(2)~SQL Query 性能チューニングについて](https://qiita.com/nori0315/items/5612b3b5721f13de5607)

dbResultClause
https://cloud.ibm.com/docs/sql-query?topic=sql-query-sql-#dbResultClause

Database locations
https://cloud.ibm.com/docs/sql-query?topic=sql-query-overview#crn-uri-location

IBM Cloud SQL Query Python Library (ibmcloudsql)
https://dataplatform.cloud.ibm.com/exchange/public/entry/view/4a9bb1c816fb1e0f31fec5d580e4e14d

Automate Serverless Data Pipelines for Your Data Warehouse or Data Lakes
https://www.ibm.com/cloud/blog/announcements/automate-serverless-data-pipelines-for-your-data-warehouse-or-data-lakes