SQL Server Migration Assistant for Oracle  による Oracle Database から Azure SQL Database への移行検証～Oracle 例外処理編～


## 概要

### 本記事の位置付けについて

Oracle Database から Azure SQL Database へ SQL Server Migration Assistant for Oracle （SSMA）によるプログラム移行検証結果の一部である。次の記事にて個別機能別記事へのリンクを整理している。

- [SQL Server Migration Assistant for Oracle による Oracle Database から Azure SQL Database への移行検証記事のまとめ - Qiita](https://qiita.com/manabian/items/0ec96b66033a7bd03a25)


### 例外処理 について

Oracle Database ドキュメントにて、次のように記載されている。

> PL/SQLランタイム・エラーの処理

引用元：[PL/SQLのエラー処理 (oracle.com)](https://docs.oracle.com/cd/F19136_01/lnpls/plsql-error-handling.html#GUID-0502DC1A-F0A5-4180-A912-6A5CDC855F56)

事前定義の例外が次のドキュメントにて整理されている。すべての例外を取得する方法として、OTHERS 例外ハンドラを利用する方法もある。

- [11.4 事前定義の例外](https://docs.oracle.com/cd/F19136_01/lnpls/plsql-error-handling.html#GUID-8C327B4A-71FA-4CFB-8BC9-4550A23734D6)

### 参考リンク

- ホワイトペーパー
    - Guide to Migrating from Oracle to SQL Server 2014 and Azure SQL Database

## Azure SQL Database への移行

### SSMA による変換

Oracle Database の 例外機能のほぼすべてをエミュレーションすることが可能である。

詳細については、次のホワイトペーパーに記載されている。

- Guide to Migrating from Oracle to SQL Server 2014 and Azure SQL Database

### 移行方針

エラー出力など一部の機能が Oracle Database の仕様とは異なるプログラムに変換される場合があるため、検証を行いながら移行する必要がある。

### 変換例

#### 変換例 1 EXCEPTION 句の利用

##### Oracle Database のコード

```sql
DECLARE
    id NUMBER(3,0);
BEGIN
    SELECT
        EMP_ID
    INTO id
    FROM (
        SELECT 1 AS EMP_ID FROM DUAL
        UNION ALL
        SELECT 2 AS EMP_ID FROM DUAL        
    )
        ;
EXCEPTION
    WHEN too_many_rows THEN
        dbms_output.put_line('行が多すぎます。');
END;

```

##### Azure SQL Database への変換

SSMA により次のように変換される。

```sql
BEGIN

   DECLARE
      @ID numeric(3, 0)

   BEGIN TRY
      SELECT @ID = fci.EMP_ID
      FROM 
         (
            SELECT 1 AS EMP_ID
             UNION ALL
            SELECT 2 AS EMP_ID
         )  AS fci
   END TRY

   BEGIN CATCH

      DECLARE
         @errornumber int

      SET @errornumber = ERROR_NUMBER()

      DECLARE
         @errormessage nvarchar(4000)

      SET @errormessage = ERROR_MESSAGE()

      DECLARE
         @exceptionidentifier nvarchar(4000)

      SELECT @exceptionidentifier = ssma_oracle.db_error_get_oracle_exception_id(@errormessage, @errornumber)

      IF (@exceptionidentifier LIKE N'ORA-01422' + '%')
         PRINT '行が多すぎます。'
      ELSE 
        THROW 

   END CATCH

END
GO
```

#### 変換例 2 `raise_application_error`の利用

##### Oracle Database のコード

```sql
DECLARE
    emp_id NUMBER(3,0);
BEGIN
    emp_id := 999;
    IF emp_id > 200 THEN
        raise_application_error(-20001, 'emp_id の指定値に誤りがあります');
    END IF;
END;
```

##### Azure SQL Database への変換

SSMA により次のように変換される。

```sql
BEGIN

   DECLARE
      @EMP_ID numeric(3, 0)

   SET @EMP_ID = 999

   IF @EMP_ID > 200
      BEGIN

         DECLARE
            @db_raise_application_error_message nvarchar(4000)

         SET @db_raise_application_error_message = N'ORA' + CAST(-20001 AS nvarchar) + N': ' + N'emp_id の指定値に誤りがあります'
; 
         THROW 59998, @db_raise_application_error_message, 1

      END

END
GO
```

#### 変換例 3 `sqlcode`と`sqlerrm`の利用

##### Oracle Database のコード

```sql
DECLARE
    id NUMBER(3,0);
BEGIN
    id := 1234;
EXCEPTION
    WHEN OTHERS THEN
        dbms_output.put_line(sqlcode);
        dbms_output.put_line(sqlerrm);
END;
```

##### Azure SQL Database への変換

SSMA により次のように変換されるが、Oracle Database の同等の動作とならない。

```sql
BEGIN

   DECLARE
      @ID numeric(3, 0)

   BEGIN TRY
      SET @ID = 1234
   END TRY

   BEGIN CATCH

      DECLARE
         @errornumber int

      SET @errornumber = ERROR_NUMBER()

      DECLARE
         @errormessage nvarchar(4000)

      SET @errormessage = ERROR_MESSAGE()

      DECLARE
         @exceptionidentifier nvarchar(4000)

      SELECT @exceptionidentifier = ssma_oracle.db_error_get_oracle_exception_id(@errormessage, @errornumber)

      BEGIN

         PRINT ssma_oracle.db_error_sqlcode(@exceptionidentifier, @errornumber)

         PRINT ssma_oracle.db_error_sqlerrm_0(@exceptionidentifier, @errornumber)

      END

   END CATCH

END
GO
```

#### 変換例 4 ユーザー定義例外の利用

##### Oracle Database のコード

```sql
DECLARE
    test_excep EXCEPTION;
BEGIN
    RAISE test_excep;
EXCEPTION
    WHEN test_excep THEN
        dbms_output.put_line('ユーザー定義例外が発生');
END;
```

##### Azure SQL Database への変換

次のように変換される。

```sql
BEGIN

   DECLARE
      @TEST_EXCEP$exception nvarchar(1000)

   BEGIN TRY

      SET @TEST_EXCEP$exception = N'local:oracle:{HR|00000001#Statement}:000:TEST_EXCEP'

      DECLARE
         @temp nvarchar(4000)

      SET @temp = N'SSMA Oracle exception emulation for [' + @TEST_EXCEP$exception + ']'
; 
      THROW 59999, @temp, 1

   END TRY

   BEGIN CATCH

      DECLARE
         @errornumber int

      SET @errornumber = ERROR_NUMBER()

      DECLARE
         @errormessage nvarchar(4000)

      SET @errormessage = ERROR_MESSAGE()

      DECLARE
         @exceptionidentifier nvarchar(4000)

      SELECT @exceptionidentifier = ssma_oracle.db_error_get_oracle_exception_id(@errormessage, @errornumber)

      IF (@exceptionidentifier LIKE @TEST_EXCEP$exception + '%')
         PRINT 'ユーザー定義例外が発生'
      ELSE 
         THROW 

   END CATCH

END
GO
```