C#でExcelアドイン × PostgreSQL × VBA: ExcelでPostgreSQLを扱うTips

ExcelアドインでPostgreSQLに接続する実装について、より詳しく解説した記事を作成します。



# ExcelからPostgreSQLに接続するアドインの開発 - 実装詳解

## はじめに
ExcelからPostgreSQLデータベースに接続し、SQLクエリを実行して結果をExcelシートに書き込む方法を紹介します。ODBCドライバをインストールできない環境でも動作するよう、C#とExcel DNAライブラリを使ってExcelアドインを作成します。この記事では、特にC#の実装部分について詳しく解説します。

## 必要な環境とライブラリ

### 開発環境
- Visual Studio 2019以降
- .NET Framework 4.7.2以降
- Microsoft Excel (2013以降推奨)

### 必要なNuGetパッケージ
```
- ExcelDna.AddIn
- ExcelDna.Integration
- Npgsql
- Microsoft.Office.Interop.Excel
```

## 実装の詳細解説

### 1. 基本構造とクラス設計

```csharp
using System;
using System.Data;
using System.Runtime.InteropServices;
using Npgsql;
using ExcelDna.Integration;
using Excel = Microsoft.Office.Interop.Excel;

namespace PostgresExcelAddin
{
    public static class Functions
    {
        private static NpgsqlConnection _connection;
        // ...
    }
}
```

この部分で重要なポイント：
- `ExcelDna.Integration`名前空間：Excel DNAの機能を使用するために必要
- `Npgsql`：PostgreSQL接続用のライブラリ
- `Microsoft.Office.Interop.Excel`：ExcelのCOMインターフェース用
- `static NpgsqlConnection _connection`：データベース接続を保持する静的フィールド

### 2. データベース接続機能の実装

```csharp
[ExcelFunction(Description = "PostgreSQLに接続します")]
public static string ConnectDB(string host, string database, string username, string password, string port = "5432")
{
    try
    {
        var connectionString = $"Host={host};Port={port};Database={database};Username={username};Password={password}";
        
        // 既存の接続をクリーンアップ
        if (_connection != null && _connection.State == ConnectionState.Open)
        {
            _connection.Close();
            _connection.Dispose();
        }

        // 新しい接続を確立
        _connection = new NpgsqlConnection(connectionString);
        _connection.Open();
        return "接続成功";
    }
    catch (Exception ex)
    {
        return $"接続エラー: {ex.Message}";
    }
}
```

重要なポイント：
- `[ExcelFunction]`属性：この関数をExcelから呼び出し可能にする
- 接続文字列の構築：セキュリティ上の理由から、パラメータ化された方式を使用
- 既存接続の適切な破棄：メモリリークを防ぐ
- エラーハンドリング：例外をキャッチしてユーザーフレンドリーなメッセージを返す

### 3. データベース切断機能

```csharp
[ExcelFunction(Description = "PostgreSQLから切断します")]
public static string DisconnectDB()
{
    try
    {
        if (_connection != null)
        {
            if (_connection.State == ConnectionState.Open)
            {
                _connection.Close();
            }
            _connection.Dispose();
            _connection = null;
        }
        return "切断成功";
    }
    catch (Exception ex)
    {
        return $"切断エラー: {ex.Message}";
    }
}
```

実装のポイント：
- 接続状態のチェック
- リソースの適切な解放
- null参照の防止

### 4. クエリ実行機能

```csharp
[ExcelFunction(Description = "SQLクエリを実行してシートに結果を書き込みます")]
public static object ExecuteQuery(string query, string sheetName)
{
    try
    {
        // 接続チェック
        if (_connection == null || _connection.State != ConnectionState.Open)
        {
            return "データベースに接続されていません";
        }

        // Excel操作のための準備
        var app = ExcelDnaUtil.Application as Excel.Application;
        Excel.Worksheet sheet;

        // シートの取得または作成
        try
        {
            sheet = app.ActiveWorkbook.Worksheets[sheetName];
        }
        catch
        {
            sheet = app.ActiveWorkbook.Worksheets.Add();
            sheet.Name = sheetName;
        }

        // クエリ実行とデータ取得
        using (var cmd = new NpgsqlCommand(query, _connection))
        using (var reader = cmd.ExecuteReader())
        {
            var dataTable = new DataTable();
            dataTable.Load(reader);

            // シートのクリア
            sheet.UsedRange.Clear();

            // ヘッダーの書き込み
            for (int i = 0; i < dataTable.Columns.Count; i++)
            {
                sheet.Cells[1, i + 1].Value = dataTable.Columns[i].ColumnName;
            }

            // データの一括書き込み
            var data = new object[dataTable.Rows.Count, dataTable.Columns.Count];
            for (int row = 0; row < dataTable.Rows.Count; row++)
            {
                for (int col = 0; col < dataTable.Columns.Count; col++)
                {
                    data[row, col] = dataTable.Rows[row][col];
                }
            }

            if (dataTable.Rows.Count > 0)
            {
                var range = sheet.Range[
                    sheet.Cells[2, 1], 
                    sheet.Cells[dataTable.Rows.Count + 1, dataTable.Columns.Count]
                ];
                range.Value = data;
            }

            // 列幅の自動調整
            sheet.Columns.AutoFit();
            return $"{dataTable.Rows.Count}行のデータを取得しました";
        }
    }
    catch (Exception ex)
    {
        return $"エラー: {ex.Message}";
    }
}
```

実装の重要ポイント：
1. データ取得フロー
   - NpgsqlCommandの作成
   - DataReaderを使用したデータ読み取り
   - DataTableへのデータロード

2. Excel操作
   - シートの存在確認と作成
   - 既存データのクリア
   - ヘッダー行の設定
   - データの一括書き込み（パフォーマンス最適化）
   - 列幅の自動調整

3. エラーハンドリング
   - データベース接続状態の確認
   - Excel操作時の例外処理
   - ユーザーフレンドリーなエラーメッセージ

### 5. 更新系クエリ実行機能

```csharp
[ExcelFunction(Description = "UPDATE/INSERT/DELETEクエリを実行します")]
public static string ExecuteNonQuery(string query)
{
    try
    {
        if (_connection == null || _connection.State != ConnectionState.Open)
        {
            return "データベースに接続されていません";
        }

        using (var cmd = new NpgsqlCommand(query, _connection))
        {
            int result = cmd.ExecuteNonQuery();
            return $"{result}行が影響を受けました";
        }
    }
    catch (Exception ex)
    {
        return $"エラー: {ex.Message}";
    }
}
```

実装のポイント：
- 更新系クエリの実行には`ExecuteNonQuery`を使用
- 影響を受けた行数を返却
- 適切なリソース管理（usingステートメント）

## VBAからの呼び出し方法

ExcelのVBAからC#で実装した関数を呼び出す例：

```vb
' データベース接続
Sub ConnectToDatabase()
    Dim result As String
    result = Application.Run("ConnectDB", "localhost", "postgres", "username", "password", "5432")
    MsgBox result
End Sub

' クエリ実行
Sub ExecuteQueryExample()
    Dim queryResult As String
    queryResult = Application.Run("ExecuteQuery", "SELECT * FROM users", "Results")
    MsgBox queryResult
End Sub

' データベース更新
Sub UpdateDatabase()
    Dim nonQueryResult As String
    nonQueryResult = Application.Run("ExecuteNonQuery", "UPDATE users SET active = true WHERE id = 1")
    MsgBox nonQueryResult
End Sub

' 切断処理
Sub DisconnectFromDatabase()
    Dim disconnectResult As String
    disconnectResult = Application.Run("DisconnectDB")
    MsgBox disconnectResult
End Sub
```

## アドイン開発時の注意点

1. デバッグ方法
   - Visual StudioでExcelプロセスにアタッチして実行時デバッグが可能
   - デバッグプリント文は`Debug.WriteLine()`を使用

2. エラーハンドリング
   - すべての公開メソッドで適切な例外処理を実装
   - ユーザーフレンドリーなエラーメッセージを返す
   - デバッグ情報は開発時のみ表示

3. パフォーマンス最適化
   - 大量データ処理時は一括書き込みを使用
   - 接続のプーリングを考慮
   - 不要なシート操作を最小限に

4. セキュリティ考慮事項
   - 接続文字列の安全な管理
   - SQL injection対策
   - エラーメッセージでの機密情報漏洩防止

## まとめ

このExcelアドインの実装により、以下のメリットが得られます：
1. ODBCドライバ不要でPostgreSQLに接続可能
2. C#の型安全性とエラーハンドリングの恩恵
3. Excel DNAによる簡単な関数エクスポート
4. 効率的なデータ取得と書き込み

また、この実装は以下の点で拡張可能です：
- パラメータ化クエリのサポート
- トランザクション管理の追加
- 非同期処理への対応
- より詳細なエラーログ機能

このアドインは、ExcelユーザーがPostgreSQLデータベースを簡単に操作できるようにする強力なツールとなります。