none
レコード存在チェックして更新 RRS feed

  • 質問

  • あるテーブルをWhere条件で取得して、対象が無かったら、AddNewして、
    対象が有ったら、値を変更して、UpDateしたいのですが、

    一回読み込みで行いたいのですが、昔のレコードセットのように、
    rs.EOFだったら、AddNewして、対象があったら、更新してUpdateのような処理をするばあい、

    DataTableを使って行う方法は御座いますでしょうか?

    2010年12月10日 1:20

回答

  • DataTableを使用されるのでしたら、TableAdapterやSqlDataAdapterのUpdateメソッドなどを使えば自動的に行ってくれます。ちなみにSqlDataAdapterはTableAdapterに内包されています。

     


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
    • 回答としてマーク TAKAPI 2010年12月10日 6:53
    2010年12月10日 1:35
    モデレータ
  • 他には、DataTableを使う方法ではないので話が飛びますが、以下のようなものもあります。

    ご参考までに・・・

     

    SqlServerやOracleであれば、Margeがあります。

    ただし、SqlServerは2008以降、Oracleは、たしか9i以降からサポートしています。

    Marge文は、対象のレコードがもしあれば更新、なければ追加といったことが可能な構文を

    一つのクエリで書くことができます。

    つまり、レコード存在チェックのSELECT文、及びその結果に応じたINSERTやUPDATEの

    一連の流れを一度に行うことができるものです。

     

    SQLSERVER

    http://msdn.microsoft.com/ja-jp/library/bb510625(v=SQL.100).aspx

    http://www.atmarkit.co.jp/fdb/rensai/sqlsvr08rev/3/sqlsvr08rev03_02.html

    ORACLE

    http://www.shift-the-oracle.com/sql/merge.html

    http://www.atmarkit.co.jp/fdb/rensai/orasql12/orasql12_1.html

    • 回答としてマーク TAKAPI 2010年12月10日 6:53
    2010年12月10日 1:40
  • SqlDataAdapter ではなくてベタな方法ですが、サンプルを書いてみました。
    SQL Server 2005 以降用です。
    テーブルは table1、主キーは field1、データは field2 を想定しています。

    'Imports System.Data.SqlClient
    Using connection As New SqlConnection()
        '接続情報
        Dim connectionString = New SqlConnectionStringBuilder()
        connectionString.IntegratedSecurity = True
        connectionString.DataSource = ".\SQLExpress"
        connectionString.MultipleActiveResultSets = True
        connection.ConnectionString = connectionString.ToString()

        connection.Open()

        Using transaction = connection.BeginTransaction()
            Using commandR = connection.CreateCommand(), _
                    commandC = connection.CreateCommand(), _
                    commandU = connection.CreateCommand()

                'データ取得用
                commandR.Transaction = transaction
                commandR.CommandText = _
                    "SELECT field2 FROM dbo.table1" & _
                    " WHERE field1 = @field1"
                commandR.Parameters.Add("@field1", SqlDbType.Int)

                'データ追加用
                commandC.Transaction = transaction
                commandC.CommandText = _
                    "INSERT INTO dbo.table1 (field1, field2)" & _
                    " VALUES (@field1, @field2)"
                commandC.Parameters.Add("@field1", SqlDbType.Int)
                commandC.Parameters.Add("@field2", SqlDbType.NVarChar)

                'データ更新用
                commandU.Transaction = transaction
                commandU.CommandText = _
                    "UPDATE dbo.table1" & _
                    " SET field2 = @field2" & _
                    " WHERE field1 = @field1"
                commandU.Parameters.Add("@field1", SqlDbType.Int)
                commandU.Parameters.Add("@field2", SqlDbType.NVarChar)

                '何かの値を元に・・・
                For Each field1 In New Integer() {10, 20, 30}
                    'データを取得してみて、
                    commandR.Parameters("@field1").Value = field1
                    Using reader = commandR.ExecuteReader()
                        If Not reader.Read() Then
                            '取得できなかったら追加
                            commandC.Parameters("@field1").Value = field1
                            commandC.Parameters("@field2").Value = "新しい値"
                            commandC.ExecuteNonQuery()
                        Else
                            '取得できれば更新
                            commandU.Parameters("@field1").Value = field1
                            commandU.Parameters("@field2").Value = _
                                reader("field2").ToString() & "の更新値"
                            If commandU.ExecuteNonQuery() = 0 Then
                                Throw New Exception()
                            End If
                        End If
                    End Using
                Next
            End Using

            'すべて正常に処理できた場合だけコミット
            transaction.Commit()
        End Using
    End Using

    • 回答としてマーク TAKAPI 2010年12月10日 6:53
    2010年12月10日 5:00
  • DataAdapter の例も一応挙げておきます。慣れるとさほど苦にはならないと思うんですが・・・

    ' 型推論を使ってます。レイトバインディングじゃないのであしからず。
    ' あとコードは試してませんので注意。
    Using connection = New SqlConnection("接続文字列")
    	connection.Open()
    	Dim transaction = connection.BeginTransaction()
    	Try
    		' プライマリキーは必ず含める
    		Dim command = New SqlCommand("SELECT id, name, address FROM person WHERE name = @name", connection)
    		' パラメータを指定して検索
    		command.Parameters.AddWithValue("@name", "山本太郎")
    
    		' テーブル取得
    		Dim adapter = New SqlDataAdapter(command)
    		Dim builder = New SqlCommandBuilder(adapter)
    		Dim dt As New DataTable()
    		adapter.Fill(dt)
    
    		' DataTable 側にプライマリキーを指定しておく
    		dt.PrimaryKey = New DataColumn() {dt.Columns("id")}
    
    		' 山本太郎が婿入りして鈴木太郎になった
    		If (dt.Rows.Count = 0) Then
    			' データが存在しないので作成
    			Dim row = dt.NewRow()
    			row("name") = "鈴木太郎"
    			row("address") = "東京都"
    			dt.Rows.Add(row)
    		Else
    			' 存在したので更新
    			dt.Rows(0)("name") = "鈴木太郎"
    		End If
    
    		' テーブル更新
    		adapter.Update(dt)
    
    		transaction.Commit()
    
    	Catch ex As Exception
    		transaction.Rollback()
    	Finally
    		transaction.Dispose()
    	End Try
    
    End Using
    

    あとコーディングが面倒であれば、ADO.NET のラッパーとか作って管理する方法もあります。

    http://d.hatena.ne.jp/hilapon/20100709/1278734481

     


    ひらぽん http://d.hatena.ne.jp/hilapon/
    • 回答としてマーク TAKAPI 2010年12月10日 6:53
    2010年12月10日 6:04
    モデレータ

すべての返信

  • DataTableを使用されるのでしたら、TableAdapterやSqlDataAdapterのUpdateメソッドなどを使えば自動的に行ってくれます。ちなみにSqlDataAdapterはTableAdapterに内包されています。

     


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
    • 回答としてマーク TAKAPI 2010年12月10日 6:53
    2010年12月10日 1:35
    モデレータ
  • 他には、DataTableを使う方法ではないので話が飛びますが、以下のようなものもあります。

    ご参考までに・・・

     

    SqlServerやOracleであれば、Margeがあります。

    ただし、SqlServerは2008以降、Oracleは、たしか9i以降からサポートしています。

    Marge文は、対象のレコードがもしあれば更新、なければ追加といったことが可能な構文を

    一つのクエリで書くことができます。

    つまり、レコード存在チェックのSELECT文、及びその結果に応じたINSERTやUPDATEの

    一連の流れを一度に行うことができるものです。

     

    SQLSERVER

    http://msdn.microsoft.com/ja-jp/library/bb510625(v=SQL.100).aspx

    http://www.atmarkit.co.jp/fdb/rensai/sqlsvr08rev/3/sqlsvr08rev03_02.html

    ORACLE

    http://www.shift-the-oracle.com/sql/merge.html

    http://www.atmarkit.co.jp/fdb/rensai/orasql12/orasql12_1.html

    • 回答としてマーク TAKAPI 2010年12月10日 6:53
    2010年12月10日 1:40
  • > 他には、DataTableを使う方法ではないので話が飛びますが、以下のようなものもあります。

    DataTable 使うなら、trapemiya さん仰るとおり Update が一番ですが、ちょうどいま MySQL 使ってて同じようなことをしてましたので、honefai さんとおなじく参考までに挙げておきます。

    MySQL なら INSERT ... ON DUPLICATE KEY UPDATE 構文というのがあります。

    http://dev.mysql.com/doc/refman/5.1/ja/insert-on-duplicate.html

    こちらは INSERT 文に ON DUPLICATE KEY UPDATE を指定し、UNIQUE インデックスか PRIMARY KEY 内で重複するデータを挿入しようとしたら、UPDATE が実行されます。

    #あくまで参考までに・・・


    ひらぽん http://d.hatena.ne.jp/hilapon/
    2010年12月10日 2:03
    モデレータ
  • select から、 データー有無をチェックして。Updateまでの、サンプルコード等ないでしょうか?

    SqlDataAdapterに不慣れなもので。

     

    2010年12月10日 4:17
  • SqlDataAdapter ではなくてベタな方法ですが、サンプルを書いてみました。
    SQL Server 2005 以降用です。
    テーブルは table1、主キーは field1、データは field2 を想定しています。

    'Imports System.Data.SqlClient
    Using connection As New SqlConnection()
        '接続情報
        Dim connectionString = New SqlConnectionStringBuilder()
        connectionString.IntegratedSecurity = True
        connectionString.DataSource = ".\SQLExpress"
        connectionString.MultipleActiveResultSets = True
        connection.ConnectionString = connectionString.ToString()

        connection.Open()

        Using transaction = connection.BeginTransaction()
            Using commandR = connection.CreateCommand(), _
                    commandC = connection.CreateCommand(), _
                    commandU = connection.CreateCommand()

                'データ取得用
                commandR.Transaction = transaction
                commandR.CommandText = _
                    "SELECT field2 FROM dbo.table1" & _
                    " WHERE field1 = @field1"
                commandR.Parameters.Add("@field1", SqlDbType.Int)

                'データ追加用
                commandC.Transaction = transaction
                commandC.CommandText = _
                    "INSERT INTO dbo.table1 (field1, field2)" & _
                    " VALUES (@field1, @field2)"
                commandC.Parameters.Add("@field1", SqlDbType.Int)
                commandC.Parameters.Add("@field2", SqlDbType.NVarChar)

                'データ更新用
                commandU.Transaction = transaction
                commandU.CommandText = _
                    "UPDATE dbo.table1" & _
                    " SET field2 = @field2" & _
                    " WHERE field1 = @field1"
                commandU.Parameters.Add("@field1", SqlDbType.Int)
                commandU.Parameters.Add("@field2", SqlDbType.NVarChar)

                '何かの値を元に・・・
                For Each field1 In New Integer() {10, 20, 30}
                    'データを取得してみて、
                    commandR.Parameters("@field1").Value = field1
                    Using reader = commandR.ExecuteReader()
                        If Not reader.Read() Then
                            '取得できなかったら追加
                            commandC.Parameters("@field1").Value = field1
                            commandC.Parameters("@field2").Value = "新しい値"
                            commandC.ExecuteNonQuery()
                        Else
                            '取得できれば更新
                            commandU.Parameters("@field1").Value = field1
                            commandU.Parameters("@field2").Value = _
                                reader("field2").ToString() & "の更新値"
                            If commandU.ExecuteNonQuery() = 0 Then
                                Throw New Exception()
                            End If
                        End If
                    End Using
                Next
            End Using

            'すべて正常に処理できた場合だけコミット
            transaction.Commit()
        End Using
    End Using

    • 回答としてマーク TAKAPI 2010年12月10日 6:53
    2010年12月10日 5:00
  • DataAdapter の例も一応挙げておきます。慣れるとさほど苦にはならないと思うんですが・・・

    ' 型推論を使ってます。レイトバインディングじゃないのであしからず。
    ' あとコードは試してませんので注意。
    Using connection = New SqlConnection("接続文字列")
    	connection.Open()
    	Dim transaction = connection.BeginTransaction()
    	Try
    		' プライマリキーは必ず含める
    		Dim command = New SqlCommand("SELECT id, name, address FROM person WHERE name = @name", connection)
    		' パラメータを指定して検索
    		command.Parameters.AddWithValue("@name", "山本太郎")
    
    		' テーブル取得
    		Dim adapter = New SqlDataAdapter(command)
    		Dim builder = New SqlCommandBuilder(adapter)
    		Dim dt As New DataTable()
    		adapter.Fill(dt)
    
    		' DataTable 側にプライマリキーを指定しておく
    		dt.PrimaryKey = New DataColumn() {dt.Columns("id")}
    
    		' 山本太郎が婿入りして鈴木太郎になった
    		If (dt.Rows.Count = 0) Then
    			' データが存在しないので作成
    			Dim row = dt.NewRow()
    			row("name") = "鈴木太郎"
    			row("address") = "東京都"
    			dt.Rows.Add(row)
    		Else
    			' 存在したので更新
    			dt.Rows(0)("name") = "鈴木太郎"
    		End If
    
    		' テーブル更新
    		adapter.Update(dt)
    
    		transaction.Commit()
    
    	Catch ex As Exception
    		transaction.Rollback()
    	Finally
    		transaction.Dispose()
    	End Try
    
    End Using
    

    あとコーディングが面倒であれば、ADO.NET のラッパーとか作って管理する方法もあります。

    http://d.hatena.ne.jp/hilapon/20100709/1278734481

     


    ひらぽん http://d.hatena.ne.jp/hilapon/
    • 回答としてマーク TAKAPI 2010年12月10日 6:53
    2010年12月10日 6:04
    モデレータ
  • ありがとうございました。出来そうです。

     

    2010年12月10日 6:53