- PR -

DataSet内のテーブル比較について

投稿者投稿内容
yang
会議室デビュー日: 2004/12/18
投稿数: 8
投稿日時: 2004-12-18 18:06
はじめて投稿させていただきます。
メモリ上に展開したDataSetの2つテーブルで、
比較してあるカラムの値が異なるレコードを抽出して新たにテーブルを作る方法が判らなくて投稿しました。
どなたか判る方、よろしくお願いします。

環境:Windows XP SP2
VisualStadio 2003 C#

背景:
あるシステムの状態を定期的に収集してメモリ上に展開したDataSetにテーブルを作成しています。
テーブルのスキーマは、code, word, bit, status となっており、code+word+bitで一意の1レコードを表します。status は状態。
テーブル例

table1
code word bit status
30 1 0 0
30 1 1 0
30 1 2 0
30 1 3 0



table2
code word bit status
30 1 0 1
30 1 1 0
30 1 2 1
30 1 3 0



ここで、前回集めていたtable1と新たに集めたtable2を比較しstatusの違うレコードを抽出し、新たにテーブルを作成したいのですが、レコードを抽出するためのSQL文の発行方法がわかりません。

結果のテーブル(抽出)
table3
code word bit status
30 1 0 1
30 1 2 1


DataAdaptを作成して、データソースにDataSetを指定すればいいのかと考えたのですが、OleDbDataAdapter(OleDbCommand)等に接続できそうなにないので、悩んでいます。

ちなみに、1つのDataSetに2種類のテーブルを作成する試験用のコンソールプログラムを
作ってみました。
ここから先に進めてなくず抽出するためのコードが作れません。

サンプルソース
-----------------
using System;
using System.Data;

namespace ConsoleDataSet
{
class Class1
{
[STAThread]
static void Main(string[] args)
{

Class2 data = new Class2();

DataSet ds = new DataSet("WordBits");
DataSet Notify_ds = new DataSet("NotifyWordBits");

data.Create(ds, "WordBits"); // テーブルを作成
data.Create(Notify_ds, "WordBits"); // テーブルを作成
Notify_ds.Tables["WordBits"].Rows[0]["status"] = 1; // 試しに発生したビットを作る
Notify_ds.Tables["WordBits"].Rows[3]["status"] = 1; // 試しに発生したビットを作る

Notify_ds.Tables["WordBits"].TableName = "NotifyWordBits"; // テーブルの名前を変えて名前の重複をさける
ds.Tables.Add(Notify_ds.Tables["NotifyWordBits"].Copy()); // テーブルを1つにする

data.Display(ds);

Console.Write("Please Return Key!\r\n>");
Console.ReadLine();
}
}
class Class2
{

// テーブルを作成
public void Create(DataSet ds, string TableName)
{

DataTable dt = ds.Tables.Add(TableName);
DataRow myRow;

dt.Columns.Add("code", typeof(string)); //局アドレス フィールド
dt.Columns.Add("word", typeof(int)); // Word フィールド
dt.Columns.Add("bit", typeof(int)); // Bit フィールド
dt.Columns.Add("status", typeof(int)); // Status フィールド

for(int word = 1; word < 3; word++)
{
for(int bit = 0; bit < 4; bit++)
{
myRow = dt.NewRow();
myRow["code"] = 30; // 局アドレス(任意)
myRow["word"] = word; // Word
myRow["bit"] = bit; // Bit
myRow["status"] = 0; // 状態
dt.Rows.Add(myRow);
}
}

}

// テーブルの内容を表示
public void Display(DataSet ds)
{
foreach(DataTable dt in ds.Tables)
{
Console.WriteLine("--- ワードビット一覧 ---");
Console.WriteLine(ds.DataSetName + ":" + dt.TableName);
foreach(DataRow dr in dt.Rows)
{
foreach(DataColumn dc in dt.Columns)
{
Console.Write(" " + dc.ColumnName + ":" + dr[dc]);
}
Console.Write("\r\n");
}
Console.Write("\r\n");
}
}
}
}


-------------------------
ちなみにMicrosoft Access でテーブルを作り、クエリーで表示したSQL文では、
下記のように書くとうまく抽出できます。

SELECT Notify.code, Notify.word, Notify.bit, Notify.status
FROM Notify INNER JOIN WordBits ON (Notify.bit = WordBits.bit) AND (Notify.word = WordBits.word) AND (Notify.code = WordBits.code)
WHERE ((Not (Notify.status)=[WordBits].[status]));


どなたかご教示していただければ、助かります。
よろしくお願いします。
ノリック
会議室デビュー日: 2004/12/16
投稿数: 8
投稿日時: 2004-12-20 08:29
こんにちは。
データセットにSQLを発行するということはできなかったと思います。
わたしだったら、Table2を作った後にTable1とTable2にRelationを張ってDatarowクラスのGetChildRowsメソッドを使って一レコードずつ比較していくのかなと思います。

試してないのでなんともいえませんが。
Os_
ベテラン
会議室デビュー日: 2003/04/16
投稿数: 77
投稿日時: 2004-12-20 10:31
メモリ上に初期状態のDataTableを維持する必要がなければ、
1つのDataTable上で処理する方法もあるかと思います。

ある時点での状態が登録されているDataTableをベースに収集した情報で
DataTableを更新&更新の抽出&更新の確定を繰り返します。

例だとstatusが更新されるこになるかと思いますが、
情報収集&更新が終わった時点で、
DataTable.GetChanges()メソッドで更新情報を抽出する。
次の収集に備えて、
DataTable.AcceptChanges()メソッドで更新を確定する。
_________________
Tips集Library集を公開
yang
会議室デビュー日: 2004/12/18
投稿数: 8
投稿日時: 2004-12-21 01:43
ノリックさん、Os_さんご返答ありがとうございました。

ノリックさん
ご指摘どおり、SQLを発行することは出来ないようですね。残念.
ADO.NETを使い始めたばかりだったので、
DataSetとは、仮想のメモリ上に展開されたDBのような認識をしていたため、
SQLが発行できるものと四苦八苦してしまいました。
Relationは、私も考えたのですが、結局、code,word,bitを合成して、
必ず、一意になるカラムを新たに作る必要があったため断念しました。
ちなみに table1.word --- table2.word のリレーションを張ると
一意のレコード通しの突き合わせにならないため、エラーとなります。
なので、[table1].[code+word+bit] --- [table2].[code+word+bit]とする必要がありました。

Os_さん
DataTableを更新&更新の抽出&更新という手段もスマートでいい考えですよね。
当初、この手法で進むもうかとしたんですが、DataTableを維持する必要性と、
レコード数が増えた場合に、nレコード×nレコードの比較となり、
効率が下がると考えてこのやり方も断念しました。

で、いろいろ考え込んだ結果、以下の方法で進めることにしました。

1.table1, table2 それぞれをcode,word,bitでソートを行う。
2.for文でそれぞれのレコードを突き合わせる。(比較回数が最小限になる)
3.statusが異なったものをtable3へ追加していく

バカ正直なシンプルなやり方ですが、横着(1発コマンドで抽出)せずに
このやり方がベストなんでしょうか。

蛇足.
今回、DataSetにこだわったのは、データ転送オブジェクトとして、
差分のDataSet Tableを他のシステムへ飛ばしたかった理由からです。

いろいろアイデアをいただきありがとうございました。
今後ともよろしくお願いします。
Os_
ベテラン
会議室デビュー日: 2003/04/16
投稿数: 77
投稿日時: 2004-12-21 08:29
Osです。

確認です。
>当初、この手法で進むもうかとしたんですが、DataTableを維持する必要性と、
>レコード数が増えた場合に、nレコード×nレコードの比較となり、
>効率が下がると考えてこのやり方も断念しました。

取得した結果を比較する為、table1(DataTable)は維持されるんですよね?
また、nレコード×nレコードの比較がわからないのですが、
code,word,bitをキーとしてFindメソッドを使うことで、該当レコードを抽出することも
可能ではないですか?
また、前の回答にも書きましたが、前回のステップからの更新のみ扱うのであれば、
レコード数の増加もそれほど多くはならないのではないですか?
むしろ比較のためにtable1、table2の両方を維持する方が扱うレコード数が多くなりそうですが、
いかがでしょうか?
_________________
Tips集Library集を公開
ぢゃん♪
大ベテラン
会議室デビュー日: 2003/06/12
投稿数: 208
お住まい・勤務地: 都内
投稿日時: 2004-12-21 08:47
引用:

yangさんの書き込み (2004-12-21 01:43) より:

蛇足.
今回、DataSetにこだわったのは、データ転送オブジェクトとして、
差分のDataSet Tableを他のシステムへ飛ばしたかった理由からです。

という目的なのに
引用:
DataTableを更新&更新の抽出&更新という手段もスマートでいい考えですよね。
当初、この手法で進むもうかとしたんですが、DataTableを維持する必要性と、
レコード数が増えた場合に、nレコード×nレコードの比較となり、
効率が下がると考えてこのやり方も断念しました。

と来るのが分からないんですが……。
Os_さんが既に触れているメソッドですが、
DataSet.GetChanges メソッド ()
DataTable.GetChanges メソッド ()
の「解説」に
引用:
前回 DataSet を読み取るか、 AcceptChanges を呼び出した以降にこのデータセットに対して行われたすべての変更が格納されているこのデータセットのコピーを取得します。このコピーは、元の DataSet にマージできるよう特別に設計されています。

引用:
前回 DataTable を読み込むか、 AcceptChanges を呼び出した以降にこのテーブルに対して行われたすべての変更が格納されているこのテーブルのコピーを取得します。このコピーは、元の DataTable にマージできるように考慮して設計されています。

とあるのですが、それを読んだ上でこのメソッドは使えないと判断したのですか?

[ メッセージ編集済み 編集者: ぢゃん♪ 編集日時 2004-12-21 08:48 ]
ちょこっと修正。

[ メッセージ編集済み 編集者: ぢゃん♪ 編集日時 2004-12-21 10:38 ]
yang
会議室デビュー日: 2004/12/18
投稿数: 8
投稿日時: 2004-12-21 10:41
Os_ さん、ぢゃん♪さん。
返信ありがとうございます。

>nレコード×nレコードの比較がわからないのですが、
>code,word,bitをキーとしてFindメソッドを使うことで、該当レコードを抽出することも
>可能ではないですか?

nレコード×nレコードの比較という表現は語弊がありました。すいません。
Findメソッドを使うにしても、DataSetがいかに優れたエンジンだとしても
数回の比較がかかると言う意味です。
ソートをかけて、for文で比較するのとどれだけ差があるかと考えるた場合、
nレコード文のFind文を発行するよりは、for文のほうが分があるだろうと考えました。
しかし、昨今のCPU能力からすれば、ほとんど論ずるに足らないのかもしれませんが。。。
(どちらが早いか計測したほうがいいのでしょうけれど。。。)

>Os_さんが既に触れているDataSet.GetChanges メソッド ()の「解説」に
最終的にDataSet.GetChangesで取り出せば、確かに一番いい方法かと思いますが、
GetChangesに至るまでには、Find文×n回、何度かのテーブルCopyを行う必要があると
考えました。

最終的には、以下の方法を考えていました。
(1) SQL文(仮にできたとして)で抽出する。 --> 単純に抽出できる。速度?(一番遅いかも)
(2) GetChanges --> 抽出するのが楽。オーバヘッドに難?
(3) for文 --> シンプルだけど、少々面倒。


単純な抽出方法、なおかつ速度的に一番有利な方法はどれかと
考えた挙げ句(3)ではないかと考えました。
実際に計測をしたわけではなく、感覚的な判断をしてしまってます。

当初、質問をあげていたSQL文の発行とずれた方法論をとってしまい
質問に相談に乗っていただいた方々には申し訳なく思っています。
この機会にDataSetの使い方やレコード更新に関する更新&抽出方法、
また、SQL文が発行できないことが分かったことは、非常に有意義でした。
ありがとうございました。

ぢゃん♪
大ベテラン
会議室デビュー日: 2003/06/12
投稿数: 208
お住まい・勤務地: 都内
投稿日時: 2004-12-21 10:51
引用:

yangさんの書き込み (2004-12-21 10:41) より:

GetChangesに至るまでには、Find文]×n回、何度かのテーブルCopyを行う必要があると考えました。

ここがよく分からないんですが。
新旧比較と差分取り出し(yangさんのFind文実行はこれでしょう)をやってくれるのがGetChagesメソッド、テーブルCopyをやってくれるのがMergeメソッドなのに……。
なぜFind文の実行が必要ですか?
引用:

yangさんの書き込み (2004-12-21 01:43) より:

蛇足.
今回、DataSetにこだわったのは、データ転送オブジェクトとして、
差分のDataSet Tableを他のシステムへ飛ばしたかった理由からです。

以外の目的も実はある、とかですか??

なぜFind文を使いたがるかのかが分からないです。

[ メッセージ編集済み 編集者: ぢゃん♪ 編集日時 2004-12-21 10:54 ]

[ メッセージ編集済み 編集者: ぢゃん♪ 編集日時 2004-12-21 11:05 ]

スキルアップ/キャリアアップ(JOB@IT)