情報科学屋さんを目指す人のメモ(FC2ブログ版)

何かのやり方や、問題の解決方法をどんどんメモするブログ。そんな大学院生の活動「キャッシュ」に誰かがヒットしてくれることを祈って。

ブログ内検索

スポンサーサイト このエントリーを含むはてなブックマーク

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
スポンサー広告 | 編集

DataReaderオブジェクトに、結果の行数を表すプロパティがない理由 このエントリーを含むはてなブックマーク

System.Data.SQLiteを利用していて、"SELECT DISTINCT"を利用して、格納されている値にどのようなものがあるのかを調べようと思いました。

using (SQLiteConnection cnn = new SQLiteConnection("Data Source=data.db"))
using (SQLiteCommand cmd = cnn.CreateCommand())
{
    cnn.Open();

    cmd.CommandText =
        "SELECT DISTINCT ATTRIBUTE_NAME FROM TABLE_NAME";

    using (SQLiteDataReader reader = cmd.ExedcuteReader())
    {
        result = new string[###  謎  ###];
        int i = 0;
        while (reader.Read())
        {
            result[i] = reader[0];
            i++;
        }
    }
}

return result;

コードは以上のようになります。data.dbというSQLiteデータベースファイルのTABLE_NAMEテーブルのATTRIBUTE_NAME属性に含まれている値の一覧を、重複を除いて取得しようとしています。一覧は、SQLiteDataReader型の、readerオブジェクトから得られます。

ここで、結果を配列に格納したいので、まず配列のサイズを決定する必要が出てきます。

すると、readerオブジェクトのLengthプロパティがあるのだろうと思ったりしますが、Lengthはおろか、行数を表すプロパティは一切ありません。つまり、配列のサイズを決定することができないのです。

このことについて調べていると、「Visual C# で OleDbDataReader クラスまたは SqlDataReader クラスを使用するときに、フェッチされるレコード数を示す RecordCount プロパティが存在しない 」というページを発見し、結果の行数をreaderオブジェクトから取得することはできないということが分かりました。

そのページには、SELECT COUNT(*)を利用するようにと記述されています。そこで、その通りに、上記のプログラムを書き直すと、以下のようになります。

using (SQLiteConnection cnn = new SQLiteConnection("Data Source=data.db"))
using (SQLiteCommand cmd = cnn.CreateCommand())
{
    cnn.Open();

    cmd.CommandText =
        "SELECT COUNT(*)" +
        "FROM (" +
        "SELECT DISTINCT ATTRIBUTE_NAME FROM TABLE_NAME" +
        ")";

    int resultCount = int.Parse(cmd.ExecuteScalar().ToString()); 

    cmd.CommandText =
        "SELECT DISTINCT ATTRIBUTE_NAME FROM TABLE_NAME";

    using (SQLiteDataReader reader = cmd.ExedcuteReader())
    {
        result = new string[resultCount];
        int i = 0;
        while (reader.Read())
        {
            result[i] = reader[0];
            i++;
        }
    }
}

return result;

ちなみに、DataReaderの、FieldCountは、行数ではなく、結果の列数を表すプロパティです。今回の例では、必ず1になっているはずです。

スポンサーサイト
データベース | コメント:0 | トラックバック:0 | 編集

System.Data.SQLiteをLINQ to SQLで利用しよう(失敗編) このエントリーを含むはてなブックマーク

今回は、失敗の記録です。失敗の記録も、失敗としてここにメモしておきます。

やりたかったのは、System.Data.SQLiteで扱っているデータベースを、LINQを使って扱えるようにすることです。 以下、今回行って手順を、失敗するまで紹介します。

1.データベース作成

とりあえず、LINQを使わず、地道にC#からSQLを実行するなりして、SQLiteデータベースファイルを作成する。

2.サーバエクスプローラにデータベースを追加

サーバエクスプローラのデータ接続を右クリックして、接続の追加(A)..から、手順1で作成したデータベースファイルを追加する。

3..LINQ to SQLクラスの追加

ソリューションエクスプローラ上で、プロジェクトを右クリックして、「追加(D)>新しい項目(W)」から、「LINQ to SQL クラス」を追加します。すると、*.dbmlファイルが作成されます。.dbmlファイルを元に、LINQ to SQLで利用するクラス群が生成されます

4.GUIで.dbmlファイルを自動作成する

「オブジェクト リレーショナル デザイナを使用すると、コードのデータ クラスをグラフィックスで表示できます。 データ クラスを作成するには、項目を サーバ エクスプローラ または ツールボックス からこのデザインサーフェイスにドラッグします。」と表示されているところへ、手順2で追加したデータベースのTablesの中から、LINQ to SQLで接続したいテーブルをドラッグアンドドロップします。

5.エラーメッセージ→失敗

「選択されたオブジェクトにはサポート外のデータ プロバイダが使用されています。」と表示されてしまいました。失敗です。

失敗してしまったわけですが

LINQ to SQLを利用したかったのですが、残念ながら失敗してしまいました。LINQと同様の機能を持つ、ADO.NET Entity Frameworkを本当は利用したいのですが、情報が少なく、なかなか利用する気になれません。ということで、とりあえずあきらめ、現状で我慢します。

.NET開発テクノロジ入門~.NETの基礎からクラウドテクノロジ Windows Azureまで
プログラミングMicrosoft ADO.NET2.0
プログラミングMicrosoft ASP.NET 3.5

データベース | コメント:0 | トラックバック:0 | 編集

データベースから度数分布表を作る このエントリーを含むはてなブックマーク

SQLite データベースから、C#/.NETからSQLを利用して度数分布表を作成する方法について考えます。

環境

C#(.NET)において、System.Data.SQLiteを利用してSQLiteデータベースを構築済み。SQLiteでなくても、リレーショナルデータベースなら、基本は同じはず

目標

度数分布表とは、成績を扱うデータなら、0-9点が5人、10-19点が14人、20-29点が20人…という情報を表にまとめたものです。

その度数分布表の元となる配列(どの範囲に、いくつのデータがあるか)を、作成済みのデータベース(テーブル)から、SQLを用いて作成する。また、利用言語はC#

対象データベース

コードを掲載する前に、対象となるデータベースを紹介します。

  • データベース名:"seiseki.db"
  • テーブル名:"test1"
  • テーブルのスキーマ(属性):
    • NAME CHAR(20)
    • TENSUU INTEGER
  • 行(エンティティ)の例:TANAKA | 60

方法

では、まず度数分布を求めるためのコードを掲載します。

// 度数分布表の区切りを表す配列 boundary
int[] boundary = new int[] { 10, 20, 30, 40, 50, 60, 70, 80, 90 };

// ある区切りまでの累積(10以下、20以下…)数を記録する配列 cumulative
int[] cumulative = new int[boundary.Length + 1];

// 求めたい度数分布の配列 frequency
int[] frequency = new int[boundary.Length + 1];

// 以下、SQLを利用したDBへのアクセス
using (SQLiteConnection cnn = new SQLiteConnection("Data Source=seiseki.db"))
using (SQLiteCommand cmd = cnn.CreateCommand())
{
    cnn.Open();

    // 各区切り値までの累積数を求める
    for (int i = 0; i < boundary.Length; i++)
    {
        cmd.CommandText =
            "SELECT COUNT(*)" +
            "FROM test1" +
            "WHERE TENSUU <= " + boundary[i];
        object o = cmd.ExecuteScalar();
        cumulative[i] = int.Parse(o.ToString());
    }

    // 行数(= 累積数の最後)を求める
    cmd.CommandText =
        "SELECT COUNT(*)" +
        "FROM test1";
    object o = cmd.ExecuteScalar();
    cumulative[cumulative.Length - 1] = int.Parse(o.ToString());

    // 累積数から、度数分布を求める
    frequency[0] = cumulative[0]; // 1つ目は、度数分布と累積数が一緒
    for (int i = 1; i < frequency.Length; i++)
    {
        // 累積数の差から、度数分布を求める
        frequency[i] = cumulative[i] - cumulative[i-1];
    }
}

return frequency;

以上のようにすることで、度数分布表を表すfrequency配列が取得できます。

ポイントとしては、

  1. COUNT関数
  2. ExecuteScalarメソッドの返値をどのように扱うか
の2点が挙げられます。

COUNT関数 は、WHERE以下の条件を満たす行(エンティティ)がいくつあるかを数えてくれる関数です。

ExecuteScalar()を実行すると、COUNT関数が実行され、その結果が返ってきます。

ExecuteScalarのmsdn(System.Data.SQLiteではなく、System.Data.OleDbの)を見てみると、C#でのコード例として、

CommandText = "select count(*) from region";
Int32 count = (int32) EsecuteScalar();
というコードが掲載されていますが、実際にこのようにやってみると、ExecuteScalar関数の結果をキャストすることができませんでした。ちなみに、System.Data.SQLiteのヘルプは詳しくないので使い物になりません

そこで、ほかのキャストも試してみましたが、どうもうまくいきません。結局うまくいったのは上記の通り、まず、ToStringメソッドで文字列に変換し、その文字列をint.Parseメソッドで、int型に変換という方法です。今回の一番のポイントは、これです。

まとめ

以上のようにすることで、度数分布がデータベースから取得できます。もっと効率のよい方法がいくらでもありそうですが、とりあえずこれでうまくできたのでメモしておきました。あくまで参考なので、状況に応じて、改変してください。

訂正ログ

20010/04/10 o.String() の「()」が抜けていた部分を修正。(fx993さんの指摘により修正)

SQLite入門
プログラマのためのSQL
現場で使えるSQL
基礎からのMySQL

データベース | コメント:2 | トラックバック:0 | 編集
 | HOME | 
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。