【Oracle】カーソルのループ処理ではテーブル結合する方がしない方より速い

###【概要】
テーブル結合をする場合としない場合の処理速度を比較し、テーブル結合する方が速いことを確認します。

###【環境】
・Windows8.1
・Oracle 11.2.0
・SQL Developer バージョン4.1.5.21
※インストール方法については下記を参照してください
　　[[備忘録]vagrantでOracleインストール&SQL Developerで接続する](https://qiita.com/Nobu12/items/ce519e6db7d8d3b4d744)

###【テストデータ作成】
下記の記事で使用したテストデータと同じものを使用します。
　　[【Oracle実行計画】ネステッド・ループにおける結合順序のパフォーマンスの違い](https://qiita.com/Nobu12/items/7f69966f5918b09708a8#%E3%83%86%E3%82%B9%E3%83%88%E3%83%87%E3%83%BC%E3%82%BF%E4%BD%9C%E6%88%90)

###【テーブル結合での実行】
```sql
SET SERVEROUTPUT ON;

DECLARE
    CURSOR combin_cursor IS
                    SELECT CUSTOMER.PREFECTURE_ID FROM CUSTOMER, PREFECTURE WHERE CUSTOMER.PREFECTURE_ID = PREFECTURE.PREFECTURE_ID;

    prefecture_id CUSTOMER.PREFECTURE_ID%TYPE;
    start_time    NUMBER;
    end_time      NUMBER;
    result_count  NUMBER := 0;
BEGIN
    start_time := DBMS_UTILITY.GET_TIME;
    FOR v_rec_combin IN combin_cursor LOOP
        prefecture_id := v_rec_combin.PREFECTURE_ID;
        result_count := result_count + 1;
    END LOOP;
    DBMS_OUTPUT.PUT_LINE('ループ回数 ' || LTRIM(TO_CHAR(result_count, '99,999,999')) || '回');
    end_time := DBMS_UTILITY.GET_TIME;
    DBMS_OUTPUT.PUT_LINE('処理時間　 ' || LTRIM(TO_CHAR((end_time - start_time) / 100, '9990.99')) || '秒');
END;
/

EXIT;
```
■実行結果

```text
ループ回数 1,000,000回
処理時間　 0.65秒
```

###【テーブル結合しない場合の実行】

#####【①CUSTOMERテーブルに対してループし、PREFECTUREテーブルと紐づける】
```sql
SET SERVEROUTPUT ON;

DECLARE
    CURSOR customer_cursor IS
                    SELECT PREFECTURE_ID FROM CUSTOMER;

    prefecture_id PREFECTURE.PREFECTURE_ID%TYPE;
    start_time    NUMBER;
    end_time      NUMBER;
    result_count  NUMBER := 0;
BEGIN
    start_time := DBMS_UTILITY.GET_TIME;
    FOR v_rec_customer IN customer_cursor LOOP
        SELECT PREFECTURE.PREFECTURE_ID INTO prefecture_id FROM PREFECTURE WHERE v_rec_customer.PREFECTURE_ID = PREFECTURE.PREFECTURE_ID;
        result_count := result_count + 1;
    END LOOP;
    DBMS_OUTPUT.PUT_LINE('ループ回数 ' || LTRIM(TO_CHAR(result_count, '99,999,999')) || '回');
    end_time := DBMS_UTILITY.GET_TIME;
    DBMS_OUTPUT.PUT_LINE('処理時間　 ' || LTRIM(TO_CHAR((end_time - start_time) / 100, '9990.99')) || '秒');
END;
/

EXIT;
```
■実行結果

```text
ループ回数 1,000,000回
処理時間　 9.55秒
```

#####【②PREFECTUREテーブルに対してループし、CUSTOMERテーブルと紐づける】
```sql
SET SERVEROUTPUT ON;

DECLARE
    CURSOR prefecture_cursor IS
                    SELECT PREFECTURE_ID FROM PREFECTURE;
    CURSOR customer_cursor(wk_prefecture_id PREFECTURE.PREFECTURE_ID%TYPE) IS
                    SELECT CUSTOMER_NUM FROM CUSTOMER WHERE PREFECTURE_ID = wk_prefecture_id;

    prefecture_id CUSTOMER.PREFECTURE_ID%TYPE;
    start_time    NUMBER;
    end_time      NUMBER;
    result_count  NUMBER := 0;
BEGIN
    start_time := DBMS_UTILITY.GET_TIME;
    FOR v_rec_prefecture IN prefecture_cursor LOOP
        FOR v_rec_customer IN customer_cursor(v_rec_prefecture.PREFECTURE_ID) LOOP
            SELECT PREFECTURE_ID INTO prefecture_id FROM CUSTOMER WHERE CUSTOMER.CUSTOMER_NUM = v_rec_customer.CUSTOMER_NUM;
            result_count := result_count + 1;
        END LOOP;
    END LOOP;
    DBMS_OUTPUT.PUT_LINE('ループ回数 ' || LTRIM(TO_CHAR(result_count, '99,999,999')) || '回');
    end_time := DBMS_UTILITY.GET_TIME;
    DBMS_OUTPUT.PUT_LINE('処理時間　 ' || LTRIM(TO_CHAR((end_time - start_time) / 100, '9990.99')) || '秒');
END;
/

EXIT;
```
■実行結果

```text
ループ回数 1,000,000回
処理時間　 162.00秒
```

###【まとめ】
それぞれ3回ずつ実行しました。
テーブル結合が一番処理が速いことが分かります。

|  実行回数  | テーブル結合 | ①CUSTOERテーブル | ②PREFECTUREテーブル |
|:-----------|:----------:|:---------------:|:------------------:|
| 1回目      |   0.65秒   |      9.55秒      |    162.00秒      |
| 2回目      |   0.65秒   |     10.47秒      |    156.98秒      |
| 3回目      |   0.66秒   |     10.10秒      |    149.92秒      |

###【おまけ　～ちょっとつまづいたこと～】

実行結果が文字化けした("秒"が文字化けしている)

```text
10.06???
```
原因は環境変数`NLS_LANG`が空だったため、設定した

```text
[vagrant@ ~]$ echo $NLS_LANG
[vagrant@ ~]$ 
[vagrant@ ~]$ export NLS_LANG=Japanese_Japan.UTF8
[vagrant@ ~]$ echo $NLS_LANG
Japanese_Japan.UTF8
```