DOBON.NETプログラミング道掲示板

■35582 / 親記事)  DataAdapter.Updateで構文エラー
  
□投稿者/ けい 一般人(11回)-(2024/03/11(Mon) 20:33:55)
  • アイコン環境/言語:[Windows10、VB.NET2022 ACCESS(Microsoft365)] 
    分類:[.NET] 

    お世話になります。

    Windows10、VB.NET2022で開発しています。

    ACCESSのテーブルAがあり、
    フィールドは[ID](主キー)と[フィールドP]とします。

    ソフトで、テーブルA全体のデータセットをSELECT文で取得し、
    1行ずつ、[フィールドP]を「値」で更新する処理を書いています。

    まずFor〜Nextループの中で、UPDATE文を使用してみます。

    Try〜Catch〜Finally、トランザクションはここでは省略します。

    System.Data.OleDb Ver8.0.0は、
    NuGetでインストール済です。

    -------------------------------------
    Private Sub TEST()
    Using cn As New OleDb.OleDbConnection(接続文字列)

    Dim da As OleDb.OleDbDataAdapter
    Dim ds As DataSet
    Dim dt As DataTable
    Dim dr() As DataRow
    Dim i As Integer
    Dim cmd As OleDbCommand

    Dim strSelect As String
    Dim strUpdate As String

    strSelect = "select * from [テーブルA]"
    da = New OleDbDataAdapter(strSelect, cn)
    ds = New DataSet
    da.Fill(ds, "テーブルA")
    dt = ds.Tables("テーブルA")
    dr = dt.Select

    For i = 0 To dr.Length - 1
    strUpdate = "update [テーブルA]" &
    " set [フィールドP]=[値] where [ID]=" & dr(i)("ID") & ""
    cmd = New OleDbCommand
    cmd.Connection = cn
    cmd.CommandText = strUpdate
    cmd.ExecuteNonQuery()
    Next

    End Using

    End Sub
    -------------------------------------

    この書き方ですと、更新処理は成功しますが、
    すでにテーブルAの特定行における処理をしている中で、
    UPDATE文のWHEREで再度同じ行を検索するような書き方になっています。

    なので、ここではDataAdapter.Updateの書き方で書きたいです。
    下記のように書いてみました。

    -------------------------------------
    Private Sub TEST()
    Using cn As New OleDb.OleDbConnection(接続文字列)

    Dim da As OleDb.OleDbDataAdapter
    Dim ds As DataSet
    Dim dt As DataTable
    Dim dr() As DataRow
    Dim cb As OleDbCommandBuilder
    Dim i As Integer
    Dim strSelect As String

    strSelect = "select * from [テーブルA]"

    da = New OleDbDataAdapter(strSelect, cn)
    ds = New DataSet
    da.Fill(ds, "テーブルA")
    dt = ds.Tables("テーブルA")
    dr = dt.Select

    cb = New OleDbCommandBuilder(da)

    For i = 0 To dr.Length - 1
    dr(i).BeginEdit()
    dr(i)("フィールドP") = [値]
    dr(i).EndEdit()
    Next

    da.Update(ds, "テーブルA")・・・※

    End Using

    End Sub
    -------------------------------------

    こちらですと、※の部分で構文エラーが出ます。

    ※をFor〜Nextループの中においても、エラーでした。

    BeginEdit、EndEditを外してもエラーです。

    以前VB.NET2012で開発していた際には、
    この書き方で更新処理ができたと思うのですが、
    おかしな部分がございますでしょうか。

    どうぞよろしくお願い申し上げます。

マルチポストを報告
違反を報告
引用返信 削除キー/
■35583 / ResNo.1)  Re[1]: DataAdapter.Updateで構文エラー
□投稿者/ KOZ 一般人(31回)-(2024/03/12(Tue) 09:47:46)
  • アイコンNo35582に返信(けいさんの記事)

    cb = New OleDbCommandBuilder(da)
    cb.QuotePrefix = "["
    cb.QuoteSuffix = "]"

    これでうまくいかないでしょうか?
違反を報告
引用返信 削除キー/
■35584 / ResNo.2)  Re[2]: DataAdapter.Updateで構文エラー
□投稿者/ けい 一般人(12回)-(2024/03/12(Tue) 10:26:48)
  • アイコンNo35583に返信(KOZさんの記事)

    KOZさま、できました!
    ありがとうございます!
    ループ中でこれを行う場合は、
    この文言が必要になりますかね。
    助かりました!

    > ■No35582に返信(けいさんの記事)
    >
    > cb = New OleDbCommandBuilder(da)
    > cb.QuotePrefix = "["
    > cb.QuoteSuffix = "]"
    >
    > これでうまくいかないでしょうか?
解決み!
違反を報告
引用返信 削除キー/
■35585 / ResNo.3)  Re[1]: DataAdapter.Updateで構文エラー
□投稿者/ 魔界の仮面弁士 大御所(1570回)-(2024/03/12(Tue) 10:31:36)
  • アイコンNo35582に返信(けいさんの記事)
    > ソフトで、テーブルA全体のデータセットをSELECT文で取得し、
    > 1行ずつ、[フィールドP]を「値」で更新する処理を書いています。
    それはあまり一般的なやり方では無いですね…。今回やろうとしているように、
    DataAdapter か TableAdapter を使う方が手っ取り早いでしょうね。

    仮にループ内で処理するにしても、今のように毎回 OleDbCommand を発行して、
    ad-hoc な SQL をその都度作るようなコードは避けるべきです。ループ処理なら
    OleDbCommand のインスタンス化はループの外で一度だけ行うようにし、
    ループ内では更新値を OldDbParameter で受け渡して、ExecuteNonQuery を
    呼び出すだけのコードにします。


    > System.Data.OleDb Ver8.0.0は、
    > NuGetでインストール済です。
    ということは .NET Framework のプロジェクトではなく、.NET 8 なのですね?


    > こちらですと、※の部分で構文エラーが出ます。
    それは、Visual Basic の構文エラー(コンパイルエラー)では無く、
    JET Database Engine の構文エラー(実行時エラー)なのですよね?

    であれば、実際のエラー内容を公開すべきだと思いますよ。

    何が原因になっているのかは、実際の構文を見てみないと分かりませんが、
    Access 絡みの更新失敗時に良く事故として、たとえばこういったものがあります。

    ・PRIMARY KEY や UNIQUE KEY が無く、更新行を特定可能な SQL コマンドを生成できなかった
     ⇒ 主キーを追加する

    ・フィールド名に予約語が使われており、構文解析に失敗した … NO, DATE など
    ・半角カナや長音記号「ー」など、実行環境によって正しく処理されないフィールド名がある
     ⇒ QuotePrefix / QuoteSuffix を補完する

    ・時刻を持つ日付型フィールドがあるが、UpdateCommand プロパティの該当 Parameter が、
     DbType.DateTime ではなく DbType.Date にマッピングされていて、更新元行の検出に失敗していた
     ⇒ パラメーターの DbType を修正する


    まずは、自動生成される更新クエリの内容を確認するため、
     cb = New OleDbCommandBuilder(da)
    の実行直後に、このようなコードを書いて、正しい更新コマンドが得られているかを確認しましょう。

     da.UpdateCommand = cb.GetUpdateCommand()
     Debug.Print($"{da.UpdateCommand.CommandText}")
     For Each p As OleDbParameter In da.UpdateCommand.Parameters
       Debug.Print($" [{p.ParameterName}], Col=[{p.SourceColumn}], Ver={p.SourceVersion}, DB型={p.OleDbType}, VB型={p.DbType}, Size={p.Size}")
     Next

    なお、元の SQL に近づけるのであれば、
     cb.ConflictOption = ConflictOption.OverwriteChanges
     cb.QuotePrefix = "["
     cb.QuoteSuffix = "]"
    を加えておいた方が良いかもしれませんが、この辺りは任意で。



    > Dim i As Integer
    ループカウンタのような局所的な変数を、コードの先頭で事前宣言すべきではありません。

    VB.NET 2002 といった古いバージョンならいざ知らず、2003 以降では
     For i As Integer = 0 To dr.Length - 1
    のようにして、For 内でしか使われない、ブロック スコープの変数にすべきです。
    (VB2008 以降では、For i As Integer = 0 ではなく For i = 0 と書くのが一般的)

    https://thom.hateblo.jp/entry/2016/12/12/223708
    https://learn.microsoft.com/ja-jp/dotnet/visual-basic/programming-guide/language-features/declared-elements/scope?WT.mc_id=DT-MVP-8907
違反を報告
引用返信 削除キー/
■35586 / ResNo.4)  Re[2]: DataAdapter.Updateで構文エラー
□投稿者/ けい 一般人(13回)-(2024/03/12(Tue) 11:43:17)
  • アイコンNo35585に返信(魔界の仮面弁士さんの記事)

    魔界の仮面弁士様、
    ありがとうございます!

    上記UPDATE文を使うほうでは、
    cmd = New OleDbCommand
    cmd.Connection = cn
    をForループの前に置きます。

    更新値は、
    実際には、
    このForループの中において、
    また別のテーブルをSELECT検索し取得するため、
    UPDATE文を毎度代入しなおしています。
    この部分をOleDbParameterでできるかどうか、
    考えてみます。

    System.Data.Oledbは、
    VB.NET2012を使用していたころは、
    Importsしていました。
    VB.NET2022では、Importsでは利用できないとわかり、
    方法を調べて、
    NuGetパッケージとしてインストールしました。
    ですので、自分の中では、プロジェクト自体は.NEY Frameworkだと思っております。.NET8はダウンロードしておりません(詳しくありません)。

    ※の部分でのエラーは、
    Syntax error in Update Statement.
    でした。
    テーブルAの主キーの実際の名称に「No」が入っておりました。
    これを変更しました。
    cb.QuotePrefix = "["
    cb.QuoteSuffix = "]"
    を加えなくてもエラーが出なくなりました。


    更新コマンド内容、確認します。

    Dim i As Integerの行を削除しました。
    For i =0
    の記述はありますが、
    これでもうまくいくのですね。

    ご教示をいただき、ありがとうございます!




違反を報告
引用返信 削除キー/
■35587 / ResNo.5)  Re[3]: DataAdapter.Updateで構文エラー
□投稿者/ 魔界の仮面弁士 大御所(1571回)-(2024/03/12(Tue) 14:40:02)
  • アイコンNo35586に返信(けいさんの記事)
    > テーブルAの主キーの実際の名称に「No」が入っておりました。

    Access には、「Yes/No型」という Boolean 相当のデータ型があるため、
    「No」が予約語になっています。できれば、フィールド名に予約語を採用しないことをお奨めします。


    > Dim i As Integerの行を削除しました。
    > For i =0
    > の記述はありますが、
    > これでもうまくいくのですね。

    VB.NET 2002 までは
     Dim i As Integer
     For i = 0 To 10
    のように、事前に変数宣言が必須でした。

    VB.NET 2003 からは
     For i As Integer = 0 To 10
    のように書くことで、For ブロックのみで有効な「局所変数」を作れるように進化しました。
    https://atmarkit.itmedia.co.jp/fdotnet/special/vs2003/vs2003_04.html


    その後、VB2008 の時代になって「型推論」という機構が生まれ、
     Dim i As Integer = 123
     Dim s As String = "abc"
    といった、変数宣言時に初期値を指定しているコードでは、As 句を省略して
     Dim i = 123
     Dim s = "abc"
    と書いても良いことになりました。この機能があるおかげで、今では
     For i = 0 To 10
    と書いただけで、(事前に Dim i As Integer 無しで)局所変数 i として認識されるようになっています。



    > System.Data.Oledbは、
    > VB.NET2012を使用していたころは、
    > Importsしていました。

    勘違いがあるようですが、「Imports」という機能は参照設定 や NuGet とは別物です。

    プロジェクトから使うには、依存アセンブリを参照設定 もしくは NuGet するなどして
    プロジェクト内から参照しておく必要があるのはご存知の通り。

    一方、それらの機能をコードから呼び出すために
     Imports System.Data
     Imports System.Data.OleDb
    などと書いていたかと思いますが、こちらは必須作業ではありません。

    そもそも Imports ステートメントというのは、
     'Dim adp As New Global.System.Data.OleDb.OleDbDataAdapter()
     Dim adp As New System.Data.OleDb.OleDbDataAdapter()
     'Global.System.Windows.Forms.MessageBox.Show("TEST")
     System.Windows.Forms.MessageBox.Show("TEST")
    といった、名前空間付きの長い型指定を簡略化して、
     Dim cn As New OleDbConnection()
     MessageBox.Show("TEST")
    のように、名前空間を省略してクラス名だけで短く表記するための仕組みに過ぎません。参照設定とは別物です。

    なお、この Imports は、.vb のファイルの先頭に個別に書く方法の他、
    プロジェクト全体に対して一括設定することもできます。

    一括設定するには場合は、プロジェクトのプロパティから、
     ★ [参照設定]タブ > [インポートされた名前空間]
     ☆ [参照]>[全般] ノード > [名前空間のインポート]
    のいずれかで指定します(★は .NET Framework の場合、☆ は .NET の場合です)。

    既定では System.Data 名前空間がインポート済みですが、
    System.Data.OleDb 名前空間はインポートされていないので、ここで追加してもOK。


    そして System.Data.OleDb 名前空間のクラスは、
     .NET Framework では、System.Data.dll というアセンブリに
     .NET では System.Data.OleDb.dll というアセンブリ(System.Data.OleDb v8.0.0 パッケージ)
    に含まれています。

    下記ドキュメント冒頭の「アセンブリ」欄をご覧ください。
    前者は "System.Data.dll" で
    後者は "System.Data.OleDb.dll" になっていますよね。
    このように、名前空間とアセンブリ名は一致しないこともあることに注意しましょう。
    https://learn.microsoft.com/ja-jp/dotnet/api/system.data.oledb.oledbconnection?WT.mc_id=DT-MVP-8907&view=netframework-4.8.1
    https://learn.microsoft.com/ja-jp/dotnet/api/system.data.oledb.oledbconnection?WT.mc_id=DT-MVP-8907&view=dotnet-plat-ext-8.0&viewFallbackFrom=net-8.0


    そして System.Data.dll は通常、標準で有効化されておりますので、
    .NET Framework をお使いなのであれば、アセンブリ参照を追加する必要はないはずです。



    > ですので、自分の中では、プロジェクト自体は.NEY Frameworkだと思っております。.NET8はダウンロードしておりません(詳しくありません)。

    NuGet から得られる System.Data.OleDb 8.0 は .NET Standard 2.0 向けですので、
    .NET Framework でも使えますが、その場合、ターゲット フレームワークを 4.6.2 以上にしてください。

    .NET Framework 3.5 や 4.5.2 といった古いバージョンの場合は、NuGet 参照せずに
    .NET Framework に標準で組み込まれている物をそのまま使いましょう。



    なお、WinForms アプリの場合、新規作成する際のプロジェクト テンプレートにおいて
     ★Windows フォーム アプリケーション ⇒ 既定のプロジェクト名:WindowsApp1
     ☆Windows フォーム アプリ      ⇒ 既定のプロジェクト名:WinFormsApp1
    というよく似た名前のプロジェクトがあるので、注意が必要です。
    前者は .NET Framework 専用、後者は .NET のためのプロジェクトです。

    前者の場合、最初の画面で .NET Framework のフレームワーク バージョンを選択する画面が表示されます。
    後者の場合、2 番目の画面で .NET のフレームワーク バージョンを選択する画面が表示されます。
違反を報告
引用返信 削除キー/
■35588 / ResNo.6)  Re[4]: DataAdapter.Updateで構文エラー
□投稿者/ けい 一般人(14回)-(2024/03/13(Wed) 14:00:26)
  • アイコンNo35587に返信(魔界の仮面弁士さんの記事)

    お詳しくご説明いただきまして、
    まことにありがとうございます。

    Windowsフォームアプリケーションで作成するところを、
    Windowsフォームアプリで作成しておりました。

    いただきました内容をもとに、
    精進させていただきます。

    まことにありがとうございました。

解決み!
違反を報告
引用返信 削除キー/



スレッド内ページ移動 / << 0 >>

このスレッドに書きこむ

Mode/  Pass/


- Child Tree -