はじめてのAutonomous Databaseへのデータロード（Object Storage経由の場合）

**■■■Oracle Cloudのウェビナーシリーズは→[こちら](https://bit.ly/2VJS8pU)■■■**

本投稿は **[Oracle Cloud Infrastructure Advent Calendar 2019](https://adventar.org/calendars/4516)** の 2日目(12/2)として書いています。

CSVファイルのデータを、Autonomous Databaseへロード（Object Storage経由）する、簡単なサンプルをご紹介します。Case.1でDBMS_CLOUD.COPY_DATAで**そのままロードする方法**、Case.2はDBMS_CLOUD.CREATE_EXTERNAL_TABLEを使って**データを変換しながらロードする方法**を扱います。

Object Storage経由で行うこの方法は、ファイルサイズが大きい場合にも対応可能であること、定期的にデータロードが発生するような場合でもコマンドベースでの実行が可能であること、外部表を使う場合はデータを変換しながらロードできることが利点です。

最後に、うまくいかないときの切り分けに知っておくと役に立つ(かもしれない)機能やポイントを、いくつかご紹介します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/297987/4b82a6f2-cc5a-f3af-53ff-842a6ff535f9.png)

「はじめての」と言いながら、少し長くなってしまいました。が、手順通りに実施すれば、最後の「うまくいかないときの切り分けに知っておくと役に立つ(かもしれない)ポイント」は読まなくて良いので、必要になった際に読んでいただければと思います。


# 扱う内容

Object Storage上のテキストファイル(CSV)を、Autonomous Databaseへロード

- DBMS_CLOUD.CREATE_CREDENTIAL
- DBMS_CLOUD.COPY_DATA
- DBMS_CLOUD.LIST_OBJECTS
- DBMS_CLOUD.CREATE_EXTERNAL_TABLE

Object Storageに関する設定

- バケットの設定(Private/Public)
- 事前承認済みリクエスト


# 今回の環境

- **customer.csv** を **CUSTOMER表**にロードしたい
- Autonomous Database上のユーザ名: **stage**
- Object Storageのバケット名: **bucket_stage** 
- データロード用に、OCI IAMユーザ：**appuser** を作成し、そのユーザの認証トークンを作成する
    - このユーザ名・認証トークンのペアを、Autonomous Database上では **APPUSER_CRED**というクレデンシャル名で登録する


# 準備

## CSVファイル
### CSVファイルを準備

今回使用する**customer.csv**は、1行目がヘッダ、2行目以降にデータがカンマ区切りで書かれたCSVファイルを想定。

※ジェネレータツール：[なんちゃって個人情報](http://kazina.com/dummy/) を使用して生成・加工した、架空のデータです。

以下の内容(全31行)をテキストエディタにコピーし、customer.csvとしてローカルのPC上に保存します。この後の手順の都合で、保存時に **文字コードを「UTF-8」、改行コードを「LF」** にするとスムーズです。

```:customer.csv
顧客ID,名前,ふりがな,メールアドレス,性別,生年月日,婚姻,都道府県,電話番号,職業
C10001,百瀬 丈史,ももせ たけし,momose_takeshi@example.com,男,1938/09/25,既婚,石川県,000-4101-2921,営業
C10002,津田 まみ,つだ まみ,tsuda_mami@example.com,女,1938/09/27,既婚,岡山県,000-2148-4441,建築
C10003,小寺 優,こでら ゆう,kodera_yuu@example.com,女,1938/10/8,既婚,京都府,000-5128-4436,事務
C10004,横川 遥,よこかわ はるか,yokokawa_haruka@example.com,女,1938/11/15,既婚,東京都,000-8588-7054,技能工
C10005,荻原 陽子,はぎわら ようこ,hagiwara_youko@example.com,女,1938/12/18,既婚,神奈川県,000-4449-6819,自営業
C10006,大橋 陽子,おおはし ようこ,oohashi_youko@example.com,女,1939/01/31,既婚,高知県,000-9016-2328,営業
C10007,菅沼 なつみ,すがぬま なつみ,suganuma_natsumi@example.com,女,1939/02/26,既婚,山形県,000-4527-6536,営業
C10008,外山 砂羽,そとやま さわ,sotoyama_sawa@example.com,女,1939/04/07,既婚,熊本県,000-9216-7220,登録なし
C10009,藤原 千佳子,ふじわら ちかこ,fujiwara_chikako@example.com,女,1939/05/04,既婚,東京都,000-3076-4029,登録なし
C10010,北村 広司,きたむら こうじ,kitamura_kouji@example.com,男,1939/05/20,既婚,広島県,000-6004-3842,自営業
C10011,武井 瑠璃亜,たけい るりあ,takei_ruria@example.com,女,1939/05/29,既婚,佐賀県,000-8683-3579,自営業
C10012,白川 美佳,しらかわ みか,shirakawa_mika@example.com,女,1939/06/06,既婚,宮城県,000-2328-4569,自営業
C10013,岩谷 ヒロ,いわたに ひろ,iwatani_hiro@example.com,男,1939/07/15,既婚,神奈川県,000-9885-4766,建築
C10014,佐川 雅彦,さがわ まさひこ,sagawa_masahiko@example.com,男,1939/08/21,既婚,兵庫県,000-2687-7932,営業
C10015,上条 聖陽,かみじょう まさあき,kamijou_masaaki@example.com,男,1939/09/19,既婚,富山県,000-1432-7322,教育
C10016,小松 まひる,こまつ まひる,komatsu_mahiru@example.com,女,1939/10/28,既婚,福岡県,000-8134-3716,サービス業
C10017,豊田 倫子,とよた のりこ,toyota_noriko@example.com,女,1939/12/14,既婚,広島県,000-2424-5328,事務
C10018,山野 ひとみ,やまの ひとみ,yamano_hitomi@example.com,女,1940/03/13,既婚,山梨県,000-4565-7877,クリエイター
C10019,及川 健,おいかわ けん,oikawa_ken@example.com,男,1940/03/14,既婚,埼玉県,000-1283-2386,営業
C10020,安藤 敦,あんどう あつし,anndou_atsushi@example.com,男,1940/04/28,既婚,山形県,000-7495-8133,技能工
C10021,竹中 徹,たけなか とおる,takenaka_tooru@example.com,男,1980/07/01,既婚,岐阜県,000-1554-3673,営業
C10022,湊 浩介,みなと こうすけ,minato_kousuke@example.com,男,1940/09/15,既婚,兵庫県,000-0371-0676,サービス業
C10023,緒形 夏空,おがた そら,ogata_sora@example.com,女,1980/09/16,既婚,滋賀県,000-7475-5465,教育
C10024,小泉 一恵,こいずみ かづえ,koizumi_kadue@example.com,女,1940/11/07,既婚,茨城県,000-8759-9978,士業
C10025,竹田 友香,たけだ ともか,takeda_tomoka@example.com,女,1980/12/23,既婚,秋田県,000-0724-9546,エンジニア
C10026,前田 鉄二,まえだ てつじ,maeda_tetsuji@example.com,男,1981/01/06,既婚,富山県,000-4564-5797,事務
C10027,安倍 美紀,あべ みき,abe_miki@example.com,女,1941/02/17,既婚,東京都,000-5651-8006,サービス業
C10028,松山 進,まつやま すすむ,matsuyama_susumu@example.com,男,1981/02/19,既婚,大分県,000-8141-9928,技能工
C10029,橘 佳乃,たちばな よしの,tachibana_yoshino@example.com,女,1941/02/24,既婚,静岡県,000-9000-1834,営業
C10030,関口 美帆,せきぐち みほ,sekiguchi_miho@example.com,女,1981/05/06,既婚,兵庫県,000-8345-2548,自営業
```


## Object Storageや、OCI IAMユーザ関連の準備

### OCIユーザとクレデンシャル（Object Storageにアクセスするための資格証明）を作成

#### OCIユーザと、そのユーザの認証トークン(Auth Token)を作成

Object Storageにアクセスするための資格証明として、OCIユーザと、そのユーザの認証トークンが必要です。権限管理の観点で、データロード用に新しくOCIユーザ**appuser**を作成し、認証トークンを生成することにします。

左上の「三」(ハンバーガーメニュー）をクリックし、「アイディンティティ」→「ユーザー」で、**appuser**を作成

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/297987/0271ce10-8449-69c1-0ce5-7fe75da53482.png)

作成したユーザ名をクリックすると、そのユーザ(appuser)の詳細画面になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/297987/f57b0907-5e2b-f534-6d75-757a7a0167c5.png)

その画面の左下に有る「認証トークン」をクリックして発行。トークンが一度限り表示されるのでメモしておきます。後でパスワードとして利用します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/297987/5e5cc9e3-15d9-93d1-7c37-2ccd1b7c467d.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/297987/af75529c-8e95-a1cc-30f4-bc4388b61222.png)

#### 作成した OCIユーザを適切なグループに追加する

OCIでは、OCI IAMユーザに対して直接アクセス権を付与するのではなく、グループへの所属を通じてリソースへのアクセス権が付与されます。
（もう少し細かく書くと、許可する操作をポリシーに記述しポリシーをグループに関連付けます( `allow group <グループ名> to <どんな操作を> <どのリソースに> in <どこの>`)。適切なポリシーが割り当てられたグループにユーザを所属させることで、そのユーザはリソースへのアクセス権が付与された状態になります。）

操作範囲を絞ったグループを適切に設計をすることが望ましいですが、ここでは簡単のため、今作成した **appuser** を 最初から存在する Administorators グループに追加しました。

左上の「三」＞「アイデンティ」 ＞ 「グループ」 から追加できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/297987/21dad526-875f-a5bc-ad31-ebe2c5862710.png)

### Object Storageのバケットを作成

バケット **bucket_stage** を作成します。

OCIコンソール画面のオブジェクト・ストレージ から「バケットの作成」で、バケット名に **bucket_stage** を入力し、あとはデフォルトのまま「バケットの作成」を押下。

### CSVファイルをバケットにアップロードし、URLパス(URI)を確認

CSVファイルをバケットにアップロードする方法はOCI CLIや[PythonSDK](https://qiita.com/mikika/items/ca63d02b0ad510f8b8ea)など、いくつかありますが、ここでは簡単のためブラウザ経由（OCIコンソール画面から）実行します。

**bucket_stage** バケットのページで「オブジェクトのアップロード」からファイルを選択しアップロード。

アップロード後、customer.csv の行の右端を右クリック「オブジェクトの詳細」から、**URLパス(URI)** を確認。
**https://<リージョン名を含むパス>/n/<ネームスペース名>/b/<バケット名>/o/<ファイル名>** の形式になっていることが分かります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/297987/64bd6ca0-2706-7a7a-41d2-35e82a3332a0.png)


## Autonomous Databaseでの操作

### Autonomous Database上のユーザ（stageユーザ）の作成

Autonomous Databaseを作成後、adminユーザで接続し、今回の作業用ユーザとしてSTAGEユーザを作成。**今回のデータロードでは DBMS_CLOUDパッケージのEXECUTE権限が必要ですが、DWROLEにはこの権限を含むので、ここではDWROLEをgrantしています。**

```
-- adminユーザで実行。データベースユーザを作成し、権限付与
CREATE USER STAGE IDENTIFIED BY Tiger123456##;  -- ユーザ名とパスワードを指定
GRANT DWROLE to STAGE;  
GRANT UNLIMITED TABLESPACE to STAGE;
```

ロール：DWROLEの説明や、マニュアルの該当ページは、[Oracle Cloud Infrastructure の Autonomous Databaseで使える SQL Developer Web (ブラウザベース)](https://qiita.com/mikika/items/c16842e19aed0e1b90d9) の「**追記**」の章を参照。

**※これ以降、全てのSQLは、STAGEユーザで実行する。**

### Autonomous Database -> Object Storageにアクセスできるようクレデンシャルを登録

データベースユーザ **STAGE** で接続し、
以下のSQLで、Object Storageにアクセスするためのクレデンシャル（資格証明）を、Autonomous Databaseに格納します。
**ここでつけたクレデンシャルの名前(例では APPUSER_CRED)を、後で使用する。**

```
BEGIN
  DBMS_CLOUD.CREATE_CREDENTIAL(
    credential_name => 'APPUSER_CRED',  -- クレデンシャルの名前(任意の文字列)
    username => 'appuser', -- OCIユーザー名
    password => 'XXXXXXXXXXXXXXXXXX'    -- 生成したAuth Token
  );
END;
/
```

### ロード先の表となるCUSTOMERS表を作成

CSVファイルの内容を元に、以下のSQLでCUSTOMERS表を作成。

```:customer.ddl
CREATE TABLE CUSTOMER (     
    CUSTOMER_ID  VARCHAR2(26),
    NAME VARCHAR2(128),
    FURIGANA VARCHAR2(128),
    EMAIL VARCHAR2(128),
    GENDAR VARCHAR2(26),
    BIRTHDAY DATE,
    MARITAL_STATUS VARCHAR2(26),
    PREFECTURE VARCHAR2(26),
    MOBILE_NO VARCHAR2(26),
    OCCUPATION VARCHAR2(26)
);
```


# データをロードする

## Case1. DBMS_CLOUD.COPY_DATAを使ったデータロード

DBMS_CLOUD.COPY_DATAを使ってCSVファイルの内容をAutonomous Database上の既存のテーブルにコピーします。CSVファイルのデータをそのままコピーしたいときに使用します。

**ロード先のテーブル名(CUSTOMER)、クレデンシャル名(APPUSER_CRED)、CSVファイルのURLパス、フォーマット(CSVであること等)をそれぞれ指定。**

**※file_uri_list => は、CSVアップロード時に確認したご自身の環境のURIに置き換えてください。**

```
begin
    dbms_cloud.copy_data(
        table_name => 'CUSTOMER',
        credential_name => 'APPUSER_CRED',
        file_uri_list => 'https://objectstorage.us-phoenix-1.oraclecloud.com/n/axxxx/b/bucket_stage/o/customer.csv',
        format => json_object('type' value 'csv','ignoremissingcolumns' value 'true', 'dateformat' value 'YYYY/MM/DD', 'skipheaders' value '1')
    );
end;
/
```

format => json_object(... で、諸々の属性(形式)をJSON形式で指定しています。

[Oracle Autonomous Data Warehouseの使用 - DBMS_CLOUDパッケージ・フォーマット・オプション](https://docs.oracle.com/cd/E83857_01/paas/autonomous-data-warehouse-cloud/user/format-options.html#GUID-08C44CDA-7C81-481A-BA0A-F7346473B703) の 「表A-1 DBMS_CLOUD形式オプション」に一覧がありますが、よく使うものとして、ここでの指定内容を例に取ると：

- **'type' value 'csv'** ：CSV形式 (csv without embedded (囲み文字ナシ))
- **'dateformat' value 'YYYY/MM/DD'**：DATE型に対応する文字列のフォーマットを指定している
- **'skipheaders' value '1'**：スキップするヘッダの行数。これは1行目をスキップしての意味


今回は扱いませんが、file_uri_list => でのURL指定に * で複数ファイルも指定可能です。例えば customer1.csvとcustomer2.csvの2つを同じバケット上にアップロード済みの場合、 
https://<PATH>/n/axxxx/b/bucket_stage/o/customer*.csv 指定で、2つのファイルからデータロードします。

## Case2. DBMS_CLOUD.CREATE_EXTERNAL_TABLEを使ったデータロード

### 外部表とは？ 外部表を使ったデータロードとは？

EXTERNAL TABLE とは文字通り外部表です。外部表の機能自体は古くから(Oracle Database 9i～)存在し、Oracle Databaseの外にある、例えばOS上のCSVファイルを、あたかもデータベース上の表のように見せるための機能です。Autonomous Databaseの場合は、Object Storage上のファイルを外部表として参照・定義するのにDBMS_CLOUD.CREATE_EXTERNAL_TABLEを使用します。

CSVファイルを外部表として定義して、

- **CREATE TABLE <新規表> AS SELECT * from <外部表>;**
- または **INSERT INTO TABLE <既存表> SELECT * from <外部表>;**

のように、外部表のデータを参照して実表にデータをコピーする、というのが、外部表を使用したデータロードになります。

（テキストファイルからのデータロードといえばSQL*Loaderが有名ですが、SQL*Loaderを使うよりもメリットの多い、外部表を使ったデータロードをこれまでも好んで使ってきました。）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/297987/c5266dce-9b48-72dc-c8f4-c0307bbd60eb.png)

### 外部表を使ってデータロードするのはどんなとき？

代表例は ETL処理でよく見られる **「値を変換しながらデータロードしたい」**ときです。

- Yes/No 列を 「Yes」→「1」、「No」→「0」に変換したい
- GENDER列を 「男」→「MALE」、「女」→「FEMALE」に変換したい

先の CREATE TABLE ... AS SELECT... のSELECT句に、CASE式などを使って実装します。これにより、ETL処理のT(Transform)とL(Load)をいっぺんに行うことが出来ます。

SQLで処理できるので、SQLで出来ることならなんでも？！...、例えば、集計しながらロード、条件に一致する行のみロード(WHERE句で絞る)、生年月日から30代、40代、などの年代列を追加する、など、様々な前処理が実現できます。


### 外部表を定義する

先の**customer.csv** を、**CUSTMER_EXT表**として参照できるよう、外部表として定義します。
外部表の定義には、DBMS_CLOUD.CREATE_EXTERNAL_TABLEを使用します。

table_name =>にCUSTMER_EXT、column_list => に列名のリストを指定していること以外は、DBMS_CLOUD.COPY_DATAで実行した内容と同じですね。

```
BEGIN
   DBMS_CLOUD.CREATE_EXTERNAL_TABLE(
    table_name =>'CUSTOMER_EXT',
    credential_name =>'APPUSER_CRED',
    file_uri_list =>'https://objectstorage.us-phoenix-1.oraclecloud.com/n/xxxxx/b/bucket_stage/o/customer.csv',
    format => json_object('type' value 'csv','ignoremissingcolumns' value 'true', 'dateformat' value 'YYYY/MM/DD', 'skipheaders' value '1'),
    column_list => 'CUSTOMER_ID  VARCHAR2(26),
    NAME VARCHAR2(128),
    FURIGANA VARCHAR2(128),
    EMAIL VARCHAR2(128),
    GENDAR VARCHAR2(26),
    BIRTHDAY DATE,
    MARITAL_STATUS VARCHAR2(26),
    PREFECTURE VARCHAR2(26),
    MOBILE_NO VARCHAR2(26),
    OCCUPATION VARCHAR2(26)'
  );
END;
/
```

これで、**CUSTOMER_EXT表**として参照できるはずです。
**SELECT * FROM CUSTOMER_EXT;** や、**SELECT count(*) FROM CUSTOMER_EXT;** で、外部表の中身や件数が確認できることを試してみて下さい。CSVファイルの中身はロードされておらず、CUSTOMER_EXT表としてSELECTされるたびに、Object Storage上のファイルを参照しています。

## 追記：DBMS_CLOUD.VALIDATE_EXTERNAL_TABLE

外部表の検証には、DBMS_CLOUD.VALIDATE_EXTERNAL_TABLEが利用できます。
外部表のソース・ファイルを検証し、ログ情報を生成して、外部表に指定されたフォーマット・オプションに一致しない行をAutonomous Databaseのbadfile表に格納します。

https://docs.oracle.com/cd/E83857_01/paas/autonomous-database/adbdu/dbms_cloud-validate_external_table-procedure.html#GUID-7D1CFD80-08EF-42C8-A503-0241EF1559D3

```
BEGIN
    DBMS_CLOUD.VALIDATE_EXTERNAL_TABLE (table_name => '外部表の名前');
END;
/
```

### 外部表を参照し、データを変換しながら実表にロードする

今回は、CUSTOMER_EXT表のデータを以下のように変換・追加しながら、CUSTOMER_NEW表にCTASでロードします。

- GENDAR列をcase式を使って「男」→「MALE」、「女」→「FEMALE」、それ以外→「UNKNOWN」に変換
- AGE列を追加（BIRTHDAY列から、年齢を計算）
- NENDAI列を追加（BIRTHDAY列から、30代、40代、などの年代を計算）

※ここでは年齢や年代の計算は、簡単のため 2019年12月31日を基準に計算しています。

```
-- データを変換・追加しながらロード
CREATE TABLE CUSTOMER_NEW
AS
SELECT 
    CUSTOMER_ID, 
    NAME, 
    FURIGANA, 
    EMAIL, 
    CASE GENDAR 
        WHEN '男' THEN 'MALE' 
        WHEN '女' THEN 'FEMALE'
        ELSE 'UNKNOWN' 
    END as GENDAR,
    BIRTHDAY, 
    2019 - to_number(to_char(BIRTHDAY, 'YYYY')) as AGE,
    trunc((2019 - to_number(to_char(BIRTHDAY, 'YYYY'))),-1) as NENDAI,
    MARITAL_STATUS, 
    PREFECTURE, 
    MOBILE_NO, 
    OCCUPATION
FROM CUSTOMER_EXT;
```

# うまくいかないときの切り分けに試したい機能など

手順通りに実施すればうまくいくはずですが、もしもエラーが発生する場合のために、いくつか切り分けに役立つ(かもしれない)機能や、ポイントを書いてみます。

**認証系のエラー（ORA-20401: Authorization failed for URI）や、オブジェクトが見つからない（ORA-20404: Object not found）場合を想定して、書いています。**

## エラーが発生するタイミング

投稿時点で簡単に試した範囲では、クレデンシャル登録(DBMS_CLOUD.CREATE_CREDENTIAL)や、外部表定義(DBMS_CLOUD.CREATE_EXTERNAL_TABLE)は、構文チェックがOKであれば成功するようです。（前者はpassword => にわざと間違ったAuth Tokenを指定する、後者はcredential_name => にわざと間違ったAuth Tokenを指定したクレデンシャル名を指定する、どちらも成功しました。）

従って、**エラー発生がdbms_cloud.copy_data実行時や、外部表をSELECTしたときであっても、その手前の手順がエラーの原因である可能性があります。** （← 実は、これが今日一番書きたかったこと）

余談ですが、古くから存在するOracle Databaseの外部表機能でも、私の知る範囲では外部表の定義時は構文チェックのみ。アクセス権限がない・ファイルが存在しない場合にエラーが発生するのは、その後実際にファイルにアクセスしようとする時、つまり外部表をSELECTした時でした。なのでこの挙動はなんとなく想像通りでした。





## dbms_cloud.list_objectsでバケット内のオブジェクトを表示

とりあえず特定のバケット上のファイルが見えるかどうかを確認したいときに。
DBMS_CLOUD.LIST_OBJECTSは、指定したバケット内のオブジェクトを表示します。(**URIはファイル名まで書かず、<バケット名>/o/ までを指定**)

```
SELECT * FROM DBMS_CLOUD.LIST_OBJECTS('APPUSER_CRED', 'https://objectstorage.us-phoenix-1.oraclecloud.com/n/xxxxx/b/bucket_stage/o/');

-- 実行結果の例（ここではbucket内に2つのファイルがある）
OBJECT_NAME    BYTES   CHECKSUM                           CREATED   LAST_MODIFIED                         
customer.csv     12845 fb9a271bab8775c4fafa7a35cc54bde6             22-NOV-19 12.38.40.221000000 PM UTC 
orders.csv        4077 f6e8a8649ca69ef567a82a700e5623ac             22-NOV-19 01.57.22.491000000 PM UTC  
```

## クレデンシャルの再作成や名前表示

認証系のエラーでうまくいかない場合は、DBMS_CLOUD.CREATE_CREDENTIALで指定しているOCIユーザ名とパスワード(Auth Token)が正しいかどうかを確認します。

- select * from user_credentials; で 作成済みのクレデンシャルの名前を表示（Auth Tokenの内容は表示されない）
- クレデンシャルの再作成は DBMS_CLOUD.DROP_CREDENTIALしてからDBMS_CLOUD.CREATE_CREDENTIAL

## Object Storageのバケットの設定（PrivateとPublic）

バケットは、「可視性：」で確認できますが現在「プライベート」で作成されています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/297987/9dc40616-7b1b-fa68-0ea6-6859f0c60dfb.png)

パブリックなバケットに変更するには、「可視性の編集」をクリックします。

今回の手順では bucket_stage には特に機密情報は置いていないので、このバケットの設定を「パブリック」に変更してみます。(**バケットがPublicになり、bucket_stageバケット内のファイルは全て認証情報無しでインターネット上で公開された状態になります**。利用には十分にご注意下さい）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/297987/b1e62e17-32a5-b53a-6a0b-df124f852432.png)

パブリックなバケットに変更した後は、dbms_cloudパッケージで、**credential_name => を指定せずに**ファイルにアクセスできます。

```
-- dbms_cloud.copy_data を実行。PUBLICなバケットなので「credential_name => 」指定は不要
begin
    dbms_cloud.copy_data(
        table_name => 'CUSTOMER',
        file_uri_list => 'https://objectstorage.us-phoenix-1.oraclecloud.com/n/xxxxx/b/bucket_stage/o/customer.csv',
        format => json_object('type' value 'csv','ignoremissingcolumns' value 'true', 'dateformat' value 'YYYY/MM/DD', 'skipheaders' value '1')
    );
end;
```

「パブリック」設定する上の図で「ユーザーにこのバケットのオブジェクトのリスト表示を許可」にチェックを付けていた場合は、DBMS_CLOUD.LIST_OBJECTSもcredential_name =>なしにアクセスできます。

```
-- PUBLICなバケットに対し、DBMS_CLOUD.LIST_OBJECTS を実行。
SELECT * FROM DBMS_CLOUD.LIST_OBJECTS(location_uri => 'https://objectstorage.us-phoenix-1.oraclecloud.com/n/xxxxx/b/bucket_stage/o/');
```

## 事前認証済みリクエストを作成して試す

認証がうまくいくかどうかの切り分けのもう一つの方法として、事前認証済みリクエストでのアクセスがあります。事前認証済みリクエストは有効期限を持つ特別な文字列からなるURLで、このURLを使うとクレデンシャル無しにアクセスができます。オブジェクト単位で設定できます。（バケット単位の設定も可能）
※URLを知る人なら認証なしにアクセスできる、インターネットに公開された状態になりますのでご利用時には留意ください。

バケットの設定をプライベートに戻して、事前認証済みリクエストを作成してみます。
customer.csv の 右端をクリックして、

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/297987/68aeb4d7-3fdb-099e-c172-8dab35c05924.png)

内容を読んで「事前認証済みリクエストの作成」をクリックすると、URLが発行されます。URLは一度きりしか表示されないので、メモしておきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/297987/ce73bf44-a0a5-c9b5-5d66-f69f976e1ef9.png)

このURLをfile_uri_list =>に指定することで、パブリックバケットで試した内容と同じくcredential_name => を指定することなくCSVにアクセスできます。

```
begin
    dbms_cloud.copy_data(
        table_name => 'CUSTOMER',
        file_uri_list => 'https://objectstorage.us-phoenix-1.oraclecloud.com/p/P1************************************/customer.csv',
        format => json_object('type' value 'csv','ignoremissingcolumns' value 'true', 'dateformat' value 'YYYY/MM/DD', 'skipheaders' value '1')
    );
end;
/
```

パブリックなバケットの設定や、事前認証済みリクエストについては、
[【総まとめ】Oracle Cloud Infrastructure オブジェクト・ストレージ・サービスの機能概要](https://qiita.com/ksonoda/items/dc12792c99a962f932f9) が分かりやすいです。(Public Bucket や Pre-Authentication Request(PAR) 表記で、詳しく書かれています。)

## ファイルに無事アクセスできるようになったら

認証系のエラー（ORA-20401: Authorization failed for URI）や、オブジェクトが見つからない（ORA-20404: Object not found）が解消され、無事にファイルにアクセスできるようになったら、**あとはデータの問題なのでケースバイケースで頑張ろう！**な状態かと思います。
ORAエラーメッセージの内容を読んで、ひとつづつ確認・対処していくことになろうかと思います。

たくさんのケースを挙げるのは難しいですが、例をいくつか挙げます。

- DDLで定義したデータ型より文字列が大きい → **DDL(列定義)を修正する**
    - 例：VARCHAR2(4 byte)定義の列に、5バイト以上の文字列を挿入しようとしている
- 囲み文字と同じ文字を、データのなかに含む → **CSVファイルを修正する**
    - 例："が囲み文字なのに、ある列の値が "日替わり"スペシャル"定食" のように、列値の中に"を含む

format => json_object(...) で、諸々の書式オプションをJSON形式で指定していますが、そのデフォルト値を確認することも、手がかりになるかもしれません。書式オプションとデフォルト値は
[Oracle Autonomous Data Warehouseの使用 - DBMS_CLOUDパッケージ・フォーマット・オプション](https://docs.oracle.com/cd/E83857_01/paas/autonomous-data-warehouse-cloud/user/format-options.html#GUID-08C44CDA-7C81-481A-BA0A-F7346473B703) の 「表A-1 DBMS_CLOUD形式オプション」にあります。

- 改行コードの指定は正しいか？→ **recorddelimiter**
- 囲み文字の有りなし指定は正しいか？ → **type または quote**
- 文字化けする(CSVファイルの文字コード指定は？) → **characterset**　
- 日付フォーマットの指定 → **dateformat**
- エラーを何レコードまで許容するか？ → **rejectlimit**

[フォーラム](https://cloudcustomerconnect.oracle.com/resources/32a53f8587/summary)で質問してみるのも良いかもしれません。

# 参考

- [Oracle Autonomous Data Warehouseの使用 - A パッケージDBMS_CLOUDおよびDBMS_CLOUD_ADMIN](https://docs.oracle.com/cd/E83857_01/paas/autonomous-data-warehouse-cloud/user/dbmscloud-reference.html#GUID-52C9974C-D95E-4E41-AFBD-0FC4065C029A)
    - ↑リンク切れのためこちら　https://docs.oracle.com/en/cloud/paas/autonomous-database/adbsa/format-options.html#GUID-08C44CDA-7C81-481A-BA0A-F7346473B703
- [Autonomous Data Warehouse Cloud 様々なデータロードガイド](https://speakerdeck.com/oracle4engineer/autonomous-data-warehouse-cloud-yang-nadetarodogaido)