ADO.NET 2.0 OdbcCommandのパラメータ付きクエリ

ADO.NET 2.0のOdbcCommandにはパラメータ付きクエリがあるが、意外とわかりづらい部分があるので書き留めておく。


まず、SqlCommandでは、パラメータ付きクエリを実現するために以下のようにパラメータを「@」をつけて表現する。

SqlConnection conn = new SqlConnection("ODBC接続文字列");
conn.Open();

String insertSQL = "INSERT INTO users(id, name) VALUES(@id, @name);";
SqlCommand command = new SqlCommand(insertSQL, conn);
command.Parameters.AddWithValue("@id", 1);
command.Parameters.AddWithValue("@name", "Joe");
command.ExecuteNonQuery();


同じようにODBCでもうまく行くと思っていたので、PostgreSQLを用いて以下のようにパラメータとして「@」を用いてOdbcCommandでパラメータ付きクエリを実装したところ、うまくいかなかった。OdbcException例外がスローされてしまう。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Data;
using System.Data.Odbc;

class Program
{
    static void Main(string[] args)
    {
        String connStr = "ODBC接続文字列";
        OdbcConnection conn = new OdbcConnection(connStr);
        conn.Open();

        String insertSQL = "INSERT INTO users(id, name) " 
           + "VALUES(@id, @name);";
        OdbcCommand command = new OdbcCommand(insertSQL, conn);

        command.Parameters.AddWithValue("@id", 1);
        command.Parameters.AddWithValue("@name", "Joe");
        command.ExecuteNonQuery();
    }
}

command.ExecuteNonQueryで以下のようなOdbcExceptionが発生する。

ERROR [42703] ERROR: 列"id"は存在しません;
Error while executing the query


PostgreSQLのエラーコードを探してみると「42703」は「未定義列」となっている。(http://www.postgresql.jp/document/pg803doc/html/errcodes-appendix.html より)



さらに、PostgreSQLのログ(PostgreSQLのインストールディレクトリ\data\pg_logの下のpostgresql-yyyy-mm-dd_hhMMss.log)を見てみると、以下のようなエラーが出ていた。パラメータクエリがそのままの形でPostgreSQLに流れてしまっているようだ。

2010-05-04 10:51:29 JST ERROR:  列"id"は存在しません(文字位置 37)
2010-05-04 10:51:29 JST ステートメント:  INSERT INTO users(id, name) VALUES(@id, @name);


色々調べてみると、以下のコードのようにパラメータとして「?」を使うと上手くいくことがわかった。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Data;
using System.Data.Odbc;

class Program
{
    static void Main(string[] args)
    {
        String connStr = "ODBC接続文字列";
        OdbcConnection conn = new OdbcConnection(connStr);
        conn.Open();

        String insertSQL = "INSERT INTO users(id, name) " 
           + "VALUES(?, ?);";
        OdbcCommand command = new OdbcCommand(insertSQL, conn);

        command.Parameters.AddWithValue("@id", 1);
        command.Parameters.AddWithValue("@name", "Joe");
        command.ExecuteNonQuery();
    }
}


このINSERTクエリをOdbcDataAdapterとDataTableを用いると以下のようになる

using System;
using System.Collections.Generic;
using System.Linq;
using System.Data;
using System.Data.Odbc;

class Program
{
    static void Main(string[] args)
    {
        String connStr = "接続文字列";
        OdbcConnection conn = new OdbcConnection(connStr);
        OdbcDataAdapter da = new OdbcDataAdapter(connStr, conn);

        DataColumn idCol = new DataColumn("id", typeof(Int32));
        DataColumn nameCol = new DataColumn("name", typeof(String));
        
        DataTable table = new DataTable();
        table.Columns.Add(idCol);
        table.Columns.Add(nameCol);
        
        DataRow row = table.NewRow();
        row[idCol] = 1;
        row[nameCol] = "Smith";

        table.Rows.Add(row);
        
        da.InsertCommand = 
           new OdbcCommand("INSERT INTO users(id, name) VALUES(?, ?);", conn);
        da.InsertCommand.Parameters.Add("@id", OdbcType.Int);
        da.InsertCommand.Parameters.Add("@name", OdbcType.Text);
        da.Update(table);
    }
}

なんとかこれで解決。良かったです。






参考

プログラミングADO.NET2.0 David Sceppa (日経BPソフトプレス) http://terutomi.seesaa.net/article/14475071.htmlhttp://msdn.microsoft.com/ja-jp/library/system.data.odbc.odbccommand.commandtext.aspxhttp://www.postgresql.jp/document/pg803doc/html/errcodes-appendix.html