WebSurfer's Home

トップ > Blog 1   |   ログイン
APMLフィルター

OleDb + JET 用で名前付きパラメータは使えるか?

by WebSurfer 2021年5月16日 12:39

ADO.NET + JET (または ACE) + OleDb プロバイダで Access を操作する場合、この記事の一番下に書いたサンプルコードのように SQL 文に用いるパラメータには疑問符 (?) で指定される位置パラメータマーカーを使用することになっています。そこを名前付きパラメータを使ったらどうなるかという話を書きます。

実行結果1

エラーになると思っていたのですが、そうはならず少なくとも見かけは期待通り動いている例がみられたので、ちょっと調べてみた次第です。

Microsoft のドキュメント「パラメーターおよびパラメーターのデータ型の構成 (ADO.NET)」に概略が書いてありますが、SQL 文をパラメータ化する場合、パラメータのプレースホルダーの構文はデータソースとプロバイダに依存するそうです。

ADO.NET で Access を操作する際、プロバイダには .NET Framework Data Provider for OLE DB を利用するのが普通と思います。

その場合、SQL 文には疑問符 (?) で指定される位置パラメータマーカーを使用するということになっています。Microsoft のドキュメント OleDbCommand.Parameters Property(日本語版は翻訳がアレです)に以下のように書いてあります。

"Remarks: The OLE DB .NET Provider does not support named parameters for passing parameters to an SQL statement or a stored procedure called by an OleDbCommand when CommandType is set to Text. In this case, the question mark (?) placeholder must be used."

"must" は書いてあることが仕様の絶対的要件であることを意味するそうです。

それに反して名前付きパラメータを使ったらどうなるか試してみました。下のコードの INSERT 文で、...VALUES (?, ?) のところを ...VALUES (Info1, Info2) に変えてみます。実行結果は上の画像と同様に問題なしでした。

しかし、...VALUES (Info, Info) とか ...VALUES (a, a) というように同じ名前を使うと、[Info1], [Info2] には最初のパラメータ(下のコード例では "Info1" と名付けた方)に代入した値のみしか INSERT されないという不可解な動きになります。その結果が下の画像です。

実行結果2

個人的には、疑問符 (?) が must ならそれに反することをしたら即エラーにするのが良いと思うのですがどうでしょう? エラーも出さないで不可解な結果になって、それに気が付かないということもあるでしょうから。

以下にこの記事を書くのに使った .NET Framework 4.8 のコンソールアプリのサンプルコードを載せておきます。

using System;
using System.Data.OleDb;

namespace ConsoleAccess
{
    class Program
    {
        static void Main(string[] args)
        {
            var path = @"C:\Users\surfe\...\Access.mdb";
            var connString = @"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + path;
            var sqlInsert = "INSERT INTO [XXX] ([Info1], [Info2]) VALUES (?, ?)";

            using (var connection = new OleDbConnection(connString))
            {
                connection.Open();
                using (var command = new OleDbCommand())
                {
                    command.Connection = connection;
                    command.CommandType = System.Data.CommandType.Text;
                    command.CommandText = sqlInsert;
                    
                    // パラメータ名を付けるのは以下のような使い方をする場合必要
                    command.Parameters.Add(new OleDbParameter("Info1", OleDbType.Char));
                    command.Parameters.Add(new OleDbParameter("Info2", OleDbType.Char));
                    
                    // コードの並び順を変えても名前で指定したパラメータに代入される
                    command.Parameters["Info2"].Value = "info2";
                    command.Parameters["Info1"].Value = "info1";

                    var i = command.ExecuteNonQuery();

                    Console.WriteLine($"Affected Rows: {i}");
                }

                int id = -1;
                using (var command = new OleDbCommand("SELECT @@IDENTITY", connection))
                {
                    object obj = command.ExecuteScalar();
                    if (obj != null && obj.GetType() != typeof(DBNull))
                    {
                        id = (int)obj;
                    }
                    Console.WriteLine($"New id: {id}");
                }

                var sqlSelect = "SELECT [ID], [Info1], [Info2] FROM [XXX] WHERE ID = ?";
                using (var command = new OleDbCommand())
                {
                    command.Connection = connection;
                    command.CommandType = System.Data.CommandType.Text;
                    command.CommandText = sqlSelect;

                    command.Parameters.Add(new OleDbParameter("ID", OleDbType.Integer));
                    command.Parameters["ID"].Value = id;

                    using (var reader = command.ExecuteReader())
                    {
                        while (reader.Read())
                        {
                            Console.WriteLine($"ID: {reader[0]}, " +
                                $"Info1: { reader[1]}, Info2; {reader[2]} ");
                        }
                    }
                }
            }
        }
    }
}

Tags: , ,

ADO.NET

ACE OleDb で Excel のブック作成

by WebSurfer 2012年1月26日 23:52

注意:
ダウンロードページ「Microsoft Access データベース エンジン 2010 再頒布可能コンポーネント」の「詳細」にある条件に "ファイル作成の手段としての使用" はできないと書いてあります。ということは、この記事に書いてあるような Excel のブックを作るようなことはライセンス的に許可されてないのかもしれませんので注意してください。

ADO.NET と ACE OleDb プロバイダを利用して Excel のブックを新規作成する話です。

備忘録として残しておきたかったのは、作成するブックが xls 形式の時は接続文字列の Extended Properties に "Excel 8.0" を、xlsx 形式の時は "Excel 12.0 Xml" を指定するということです。

それを書いただけではブログの記事としては寂しいので、以下を追加しました。(笑)

ACE OleDb プロバイダで作成した Excel ファイル

JET でも ACE でも、接続文字列の Data Source に存在しないファイル名を指定して、CREATE TABLE クエリを実行すると、そのファイル名でブックを新規作成できます。

ACE の場合は以下のような感じです。この例では、c:\workbooks というフォルダに test.xlsx という名前のファイルが作成されます。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.OleDb;
using System.Data;
using System.Data.SqlClient;

namespace OleDbExcel
{
  class Program
  {
    static void Main(string[] args)
    {
      DataTable table = CreateDataTable();
      string createQuery = CreateQuery(table);
      string insertQuery = InsertQuery(table);
      string connString = 
        "Provider=Microsoft.ACE.OLEDB.12.0;" + 
        "Data Source=C:\\workbooks\\test.xlsx;" + 
        "Extended Properties=\"Excel 12.0 Xml;HDR=YES\"";
            
      using (OleDbConnection connection = 
        new OleDbConnection(connString))
      {
        // Excel ブックを作成
        connection.Open();
        using (OleDbCommand command = 
          new OleDbCommand(createQuery, connection))
        {                    
          command.ExecuteNonQuery();
        }

        // Excel ブックにデータを INSERT
        using (OleDbCommand command = 
          new OleDbCommand(insertQuery, connection))
        {                    
          foreach (DataColumn column in table.Columns)
          {
            command.Parameters.Add(
              "@" + column.ColumnName, 
              OleDbType.VarWChar);                        
          }

          foreach (DataRow row in table.Rows)
          {
            foreach (DataColumn column in table.Columns)
            {
              command.
                Parameters["@" + column.ColumnName].
                Value = 
                  row[column.ColumnName];
            }
            command.ExecuteNonQuery();
          }
        }
      }
    }

    // Excel ブックを作る基になるデータを SQL Server の
    // サンプルデータベース Northwind の Customers テー
    // ブルより取得し DataTable を作る。
    protected static DataTable CreateDataTable()
    {
      DataTable dataTable = new DataTable();
      SqlDataAdapter adapter = new SqlDataAdapter();
      string connString = Properties.Settings.Default.MyDB;
      string query = 
        "SELECT CustomerID, CompanyName, ContactName, ContactTitle" + 
        " FROM Customers";
      SqlConnection connection = new SqlConnection(connString);
      SqlCommand command = new SqlCommand(query, connection);
      adapter.SelectCommand = command;
      adapter.Fill(dataTable);
      return dataTable;
    }

    // DataTable の DataColumn を基に CREATE TABLE クエリ
    // を作成する。この例では以下のようになる。
    // CREATE TABLE [MySheet](CustomerID char(255),
    // CompanyName char(255),ContactName char(255),
    // ContactTitle char(255))
    protected static String CreateQuery(DataTable table)
    {
      StringBuilder sb1 = new StringBuilder();
      sb1.Append("CREATE TABLE [MySheet](");
      foreach (DataColumn column in table.Columns)
      {
        sb1.Append(column.ColumnName + " char(255),");
      }
      sb1.Remove(sb1.Length - 1, 1);
      sb1.Append(")");
      return sb1.ToString();
    }

    // DataTable の DataColumn を基に INSERT クエリを作成
    // する。この例では以下のようになる。
    // INSERT INTO [MySheet] (CustomerID,CompanyName,
    // ContactName,ContactTitle) values (?,?,?,?)
    protected static String InsertQuery(DataTable table)
    {
      StringBuilder sb1 = new StringBuilder();
      StringBuilder sb2 = new StringBuilder();
      sb1.Append("INSERT INTO [MySheet] (");
      sb2.Append(" values (");
      foreach (DataColumn column in table.Columns)
      {
        sb1.Append(column.ColumnName + ",");
        sb2.Append("?,");
      }
      sb1.Remove(sb1.Length - 1, 1);
      sb2.Remove(sb2.Length - 1, 1);
      sb1.Append(")");
      sb2.Append(")");
      return sb1.ToString() + sb2.ToString();
    }
  }
}

作成したファイルを Excel アプリケーションで開いたのが上の画像です。クエリで指定したテーブル名のシート(例では、MySheet)が生成されています。

上のコードで、接続文字列の Extended Properties="Excel 12.0 Xml;HDR=YES" の Excel 12.0 Xml の部分はソースデータベースの種類の指定です。HDR=YES は、シートの 1 行目がヘッダであることを指定します。

ソースデータベースの種類は、作成するブックの形式によって、以下のように変わります。

ブック形式 指定する DB 種類
xls Excel 8.0
xlsx Excel 12.0 Xml
xlsb Excel 12.0
xlsm Excel 12.0 Macro

xls は Excel 2003 以前の形式のブック、その他は Excel 2007 以降の Microsoft Office Open XML 形式のブックです。

8.0 または 12.0 という数字は ISAM (Indexed Sequential Access Method) ドライバのバージョンです。

注意すべきは、xlsx 形式のブックを作る時は "Excel 12.0 Xml" のように Xml を付けなければならないところです。JET の場合のように単に "Excel 12.0" とすると、中身は xlsb 形式(バイナリ形式)になってしまいます。

Excel アプリケーションでそのファイル開こうとすると、"Excel でファイルを開くことができません。ファイル形式または・・・" という警告メッセージが出て開けません(拡張子を xlsb に変更すれば開けますが)。

ブックを作った後、SELECT, INSERT, UPDATE(Excel の場合、DELETE はできません)操作を行う場合は、Microsoft サポートの記事 Visual Basic .NET と ADO.NET を使用して Excel ブックのレコードの取得と変更を行う方法 が参考になると思います。

Tags: , ,

ADO.NET

About this blog

2010年5月にこのブログを立ち上げました。主に ASP.NET Web アプリ関係の記事です。

Calendar

<<  2024年4月  >>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

View posts in large calendar