none
datatable からLINQ以外の方法で、重複の取得や連結して不一致データの取得はできますか。

    質問

  • vb2010、初心者です。

    csvデータをDataRowでDatatbleに書き込み、Datatable.select() で抽出まではできました。

    連結するには ParentRelations や ChildRelations を使用するようなのですが、いまいちわかりません。

    また、DataAdapterのクエリビルダで作成したFillなどが使えると楽なのにと思うのですが、実行しても空行しか抽出できません。

    よろしくお願いいたします。

    2012年6月23日 5:14

回答

  • 簡単ですがVBアプリでしたいことは、約20万件のCSVデータを、DataGridView で表示するさいに、全件表示や抽出、不一致などがしたいです。表示だけで編集などはしませんし、CSVデータも使い捨てで DB にも残しません。

    CSV データを使い捨てにし、編集もしないのであれば、DataTable を使わなくてもいいと思います。
    Windows2000で操作させるとなると、.NETFramework は2.0一択になると思われますので
    Linq は無理でしょうが、オブジェクトのコレクションなら以下のように DataSource に設定可能です。

    あとは List クラスのメソッドを使って、抽出や一致検索を行えばいいと思います。

    Imports System.Collections.Generic
    
    Public Class Form1
        Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    
            Dim collection As New List(Of Person)
            For i As Int32 = 0 To 100000
                collection.Add(New Person() With {
                        .Id = i,
                        .Name = "山田太郎",
                        .Address = "神奈川県横浜市",
                        .Age = 18})
            Next
            Me.DataGridView1.DataSource = collection
        End Sub
    End Class
    
    Public Class Person
        Private _Id As Int32
        Public Property Id() As Int32
            <DebuggerNonUserCode()>
            Get
                Return _Id
            End Get
            Set(value As Int32)
                _Id = value
            End Set
        End Property
    
        Private _Name As String
        Public Property Name() As String
            <DebuggerNonUserCode()>
            Get
                Return _Name
            End Get
            Set(value As String)
                _Name = value
            End Set
        End Property
    
        Private _Address As String
        Public Property Address() As String
            <DebuggerNonUserCode()>
            Get
                Return _Address
            End Get
            Set(value As String)
                _Address = value
            End Set
        End Property
    
        Private _Age As Int32
        Public Property Age() As Int32
            <DebuggerNonUserCode()>
            Get
                Return _Age
            End Get
            Set(value As Int32)
                _Age = value
            End Set
        End Property
    End Class


    ひらぽん http://d.hatena.ne.jp/hilapon/

    2012年7月4日 9:22
    モデレータ

すべての返信

  • 「重複の取得」「連結して不一致データの取得」では何をやりたいのかわかりません。
    どこまでできていて、どこがわからないのか、例えばコードを挙げてみるなどしてもっと具体的に示さないと他の人も回答のしようがありません。
    vb は初心者でも構いませんが質問の初心者は早く脱しましょう。

    2012年6月23日 10:13
  • 「DataAdapterのクエリビルダで作成したFillなどが使えると楽なのにと思うのですが、実行しても空行しか抽出できません。」 とありますが、Visual Basic で データベースを扱うなら、遠回りになりますが、一度ちゃんとデータベース (CSV File でなく、SDF File) で SQL 文の組立方やリレーションの仕組みを学ばれると良いでしょう。

    VB をはじめとするプログラミング言語と SQL 言語 (クエリ) は元々別の世界でしたので、データの扱い方が全く違います。 その辺を意識しておくと良いと思います。

    なお、CSV で保存されたデータを Datatable で扱うことは可能ですが、プログラミング言語と SQL 言語 双方の知識が必要です。

    各々、得手不得手があるので、どのようなデータがあって、それをどう選択、加工したいのかを明示した質問をされるのが良いでしょう。

    2012年6月24日 15:19
  • 説明不足ですみませんでした。

    まず、5フィールド程のCSVデータ(約10万件)を1件ずつ Datatable に読み込み、その Datatable を使用して重複の取得(重複データと件数)やほかの Datatable と連結して不一致データの取得を行いたいです。
    Adapter.Update で DB を更新するとうまくいくのですが、更新に時間がかかりすぎて現実的ではありませんでした。
    LINQ は、プログラムを配布するPCがwin2000環境のため、使用できません。
    何か良い方法があれば、ご教授宜しくお願いします。
    (下記に、コードを抜粋いたしました。)

            Dim Row As DataRow
                Row = Me.DataSet.table1.NewRow
                Row("txtData1") = strData1
                Row("txtData2") = strData2
                Row("txtData3") = strData3
                Row("txtData4") = strData4
                Row("txtData5") = strData5
                Me.DataSet.table1.Rows.Add(Row)

     'Me.TableAdapter.Update(Me.DataSet.table1)

            Me.DataGridView1.DataSource = Me.TableAdapter.GetData()



    • 編集済み kobayashi2012 2012年6月25日 2:22 コード修正
    2012年6月25日 1:42
  • Me.DataGridView1.DataSource = Me.table1.Fill()

    Fillメソッドって何をしてますか?

    まさかとは思いますが、ここでクリアというか初期化されてませんか?

    Me.DataGridView1.DataSource = Me.table1

    にしたらどうなりますか?

    2012年6月25日 1:51
  • 返信ありがとうございます。

    上記は間違えてました、すみません。正しくは TableAdapter.GetData() でした。

    (誤)Me.DataGridView1.DataSource = Me.table1.Fill()

    (正)Me.DataGridView1.DataSource = Me.TableAdapter.GetData()

    ちなみに下記は、読み込んだデータを全件表示できます。

    Me.DataGridView1.DataSource = Me.table1

    GetDataメソッドは変更していないので、全件表示できるはずなのですが空行しか返ってきません。

    これができれば、クエリビルダを使用してGetDataメソッドでいろいろと加工できると思うのですが・・・。

    2012年6月25日 2:22
  • GetDataメソッドはデータベースから値を取得してくるメソッドです。
     'Me.TableAdapter.Update(Me.DataSet.table1)
    と、コメントアウトされていますので、データベースに値は保存されておらず、したがって、GetDataメソッドで何も取得できないのではないかと思います。
    データテーブルとデータベースとの関係を正しく理解されていないように思いますので、もう一度確認された方が良いと思います。

    ところで、このスレッドの本来の質問ですが、以下のようにされてみてはいかがでしょうか?

    .NET DataTableから重複を取り除くには
    http://yan-note.blogspot.jp/2008/12/datatable.html

    不一致の行を抜くには、以下のようにするとできると思います。

    Dim dt1 As New DataTable()
    
    dt1.Columns.Add("Field")
    dt1.PrimaryKey = New DataColumn() {dt1.Columns(0)}
    dt1.Rows.Add("a")
    dt1.Rows.Add("b")
    dt1.Rows.Add("c")
    dt1.Rows.Add("d")
    dt1.Rows.Add("e")
    dt1.AcceptChanges()
    
    Dim dt2 As New DataTable()
    
    dt2.Columns.Add("Field")
    dt2.PrimaryKey = New DataColumn() {dt2.Columns(0)}
    dt2.Rows.Add("0")
    dt2.Rows.Add("b")
    dt2.Rows.Add("c")
    dt2.Rows.Add("f")
    dt2.Rows.Add("z")
    
    'dt1のRowStateは全てUnChangedにし、dt2のRowStateは全てAddedにしておく。
    
    dt1.Merge(dt2)  'MergeはRowStateを変更しない。
    
    'Mergeでdt1に追加されたもの、すなわちdt2にあってdt1にないもの。
    'この追加された行はdt2から来ているので、RowStateがAddedになっているはず。
    Dim d As DataTable = dt1.GetChanges(Data.DataRowState.Added)


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/

    2012年6月25日 6:11
    モデレータ
  • 返信ありがとうございます。

    GetDataメソッドは1度データベースに保存しないとできないのですね。

    Update()をすると遅くて使えなかったので、試しにLINQで実行したら速度も速く簡単にできたので、保存しなくてもできるものと思ってしまいました。(win2000でもLINQが使えるといいのですがね。)

    また、教えていただいた不一致取得を試してみたのですが、下記のところで遅くなってしまいます。

    Dim d As DataTable = dt1.GetChanges(Data.DataRowState.Added)

    もともと、私のコードがよくないのかもしれません・・・。

    重複については、重複している値と件数を取得したいのです。

    リンク先のグループ集計が似ているので応用できるかもしれません。

    2012年6月25日 7:58
  • 連結が、複数のテーブルのデータを組合せて別のテーブルとして取得する(結合) のことなら以下のリンクが参考になるかと。 VB ではなく他のソフトなので、SQL 文の記述方法は違いがあるかも知れませんが、考え方として ... 。

    http://support.microsoft.com/kb/136699/ja
    Microsoft Query での結合の使用について  (MS Excel)

    http://msdn.microsoft.com/ja-jp/library/aa258291(v=sql.80).aspx
    結合の種類 (MS SQL Server)

    http://msdn.microsoft.com/ja-jp/library/aa275841(v=sql.80).aspx
    外部結合を作成する (MS SQL Server)
        注:最後にテーブル内に該当する行がないものの抽出方法例があります。
                LEFT でも RIGHT でも 可能です。

     

    それと、私なら、CSV から DB へ取込む時に重複したデータは除外しますけど。


    • 編集済み ShiroYuki_Mot 2012年6月26日 1:08 行追加と参考対象追加
    2012年6月25日 15:41
  • kobayashi2012 さま その後いかがでしょうか?
    先ず、質問の文面のみ見て投稿していたので、きっとピンボケな投稿になっていたのでしょう。
    単独のご質問でなかったようですね。
    前の流れ(Visual Studio フォーラムのスレッド)も見れば良かったと、後悔しています。
    申し訳ありませんでした。
     
    ただ、ご質問の「 重複の取得や連結して不一致データ 」という語句から、どうも気になりますので投稿しておきます。
     
    今お作りのアプリが一段落したところで、是非、全体の流れを再検討して見ては如何でしょうか?
    何か、力ずくでデータハンドリングなさっているのでは?
     
    元データを一度 RDB に取り込み、 条件付の Fill メソッドでデータを絞りアプリに呼び込み修正したほうが、ご利用しようとしているクラスのメリットを生かせる様に思います。
    その折には、是非、「データベースの正規化」をお調べください。
    ご質問の内容は、まさに正規化におけるポイントのひとつですから。
    今後のアプリの拡張・修正にも、役立つのではではないでしょうか。
     
    データテーブルはデータベースの構造を .NET の言語でハンドリングし易くする為に導入されたとの認識を持っています。
     
    余計なお世話になってしまっているかも知れませんが ... 。
    2012年7月4日 2:47
  • 返信が遅くなりまして申し訳ございません。

    貴重なご意見ありがとうございます。

    おそらく力ずくだと思います。そもそも DB に取り込まずに実行しようとしているのは、取り込むようにすると、取り込むデータが多いので時間がかかってしまうのがネックになってしまいます。メモリ内の Datatabe で実行できないかと質問させていただきました。

    データが多いので時間がかかるのは仕方ないと思いますし、それだけLINQは強力なものなのですね。

    いただいた意見を参考に、再検討してみます。ありごとうございました。

    2012年7月4日 4:36
  • kobayashi2012 さま 参考投票ありがとうございます。

    データの取込に対してですが、 SQL Server の Command Line Utility (CMD.exe 上で実行) に確か ' bcp Utility ' というのがあったと思います。
    通常、CSV File のハンドリングに用いられ、SQL Server への Import/Export をバッチ処理するために使われます。
    運用は、夜間など利用されにくい時間帯に実行される様スケジュールされている場合が多いでしょう。
    かなり強力な Utility で多様な引数を持っています。
    これ以外にも、何種類かの方法があったと記憶していますが、これが基本でしょう。
     
    もし、頻繁に多量のデータを取得しなければいけないアプリなら、上記も検討項目に加えてください。
    その場合、 VB / SQL Server / OS (CMD) とカバーする範囲が広がってしまうのが難点ですが。
     
    このような構成では、データベース設計・運用が中核で、 VB のアプリは ユーザーインターフェースとして機能させることになります。
     
      ... 別スレッドにて、システム設計の観点から質問なさっては?
        どのようなデータがあって、どのぐらいの量で、どんなことをしたいのか、
        それには、どうデータを扱い、どんな手法で処理すればよいか。
        かなり具体的に書かないと明確な指針を得られないと思います。
      ... 私はこのレベルになるともうよく分かりません!
     
    以上ご参考までに。

    2012年7月4日 6:41
  • ShiroYuki_Motさん 返信ありがとうございます。

    いろいろな方法を教えていただきまして、ありがとうございます。

    簡単ですがVBアプリでしたいことは、約20万件のCSVデータを、DataGridView で表示するさいに、全件表示や抽出、不一致などがしたいです。表示だけで編集などはしませんし、CSVデータも使い捨てで DB にも残しません。

    また、取り込みに時間がかかるというのも、5分くらいなので待てばいいのですが、皆さんもデータの取り込みには、これぐらいかかるものなのでしょうか?

    2012年7月4日 8:32
  • 簡単ですがVBアプリでしたいことは、約20万件のCSVデータを、DataGridView で表示するさいに、全件表示や抽出、不一致などがしたいです。表示だけで編集などはしませんし、CSVデータも使い捨てで DB にも残しません。

    CSV データを使い捨てにし、編集もしないのであれば、DataTable を使わなくてもいいと思います。
    Windows2000で操作させるとなると、.NETFramework は2.0一択になると思われますので
    Linq は無理でしょうが、オブジェクトのコレクションなら以下のように DataSource に設定可能です。

    あとは List クラスのメソッドを使って、抽出や一致検索を行えばいいと思います。

    Imports System.Collections.Generic
    
    Public Class Form1
        Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    
            Dim collection As New List(Of Person)
            For i As Int32 = 0 To 100000
                collection.Add(New Person() With {
                        .Id = i,
                        .Name = "山田太郎",
                        .Address = "神奈川県横浜市",
                        .Age = 18})
            Next
            Me.DataGridView1.DataSource = collection
        End Sub
    End Class
    
    Public Class Person
        Private _Id As Int32
        Public Property Id() As Int32
            <DebuggerNonUserCode()>
            Get
                Return _Id
            End Get
            Set(value As Int32)
                _Id = value
            End Set
        End Property
    
        Private _Name As String
        Public Property Name() As String
            <DebuggerNonUserCode()>
            Get
                Return _Name
            End Get
            Set(value As String)
                _Name = value
            End Set
        End Property
    
        Private _Address As String
        Public Property Address() As String
            <DebuggerNonUserCode()>
            Get
                Return _Address
            End Get
            Set(value As String)
                _Address = value
            End Set
        End Property
    
        Private _Age As Int32
        Public Property Age() As Int32
            <DebuggerNonUserCode()>
            Get
                Return _Age
            End Get
            Set(value As Int32)
                _Age = value
            End Set
        End Property
    End Class


    ひらぽん http://d.hatena.ne.jp/hilapon/

    2012年7月4日 9:22
    モデレータ
  • ひらぽん さま 回答ですね!

    kobayashi2012 さま ひらぽん さまのご提示になったクラスを基に拡張していけば良いですね。

    その時に、継承したクラスをご自分で作って見るとか、コレクションやジェネリックを調べて見るとか。

    確かにデータの蓄積・再利用をしないならデータベースは必要ないですね。

    2012年7月4日 10:02
  • 返信ありがとうございます。

    知りませんでした、こういう方法もあるのですね!

    まだ自分でクラスの作成をしたことがないのでわからないことだらけですが、ご提示いただいたコードを参考にいろいろ試してみます。

    返信いただいた皆様、ありがとうございました。

    2012年7月5日 2:08