C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[11]: VBからAccessへINSERT


(過去ログ 70 を表示中)

[トピック内 13 記事 (1 - 13 表示)]  << 0 >>

■40641 / inTopicNo.1)  VBからAccessへINSERT
  
□投稿者/ もんたな (1回)-(2009/08/31(Mon) 15:41:54)

分類:[VB.NET/VB2005 以降] 

開発言語はVisualBasic.NET 2005です。
VBからAccessのテーブルへデータをインサートする処理で、
インサートするところまでは問題なく出来たのですが、
インサートされたデータをAccessで見たときに、ちょっと問題がありました。

VBからは「01」というデータをインサートしたのですが、
Accessのテーブルでは「1」と表示されてしまっています。
ちなみにAccessのテーブルはテキスト型で、特にそれを変更することなく
表示できるようなのですが…
どうしたらよいのか分かりません。どなたかよろしくお願いします。

説明が下手ですみません…。
引用返信 編集キー/
■40643 / inTopicNo.2)  Re[1]: VBからAccessへINSERT
□投稿者/ もりお (57回)-(2009/08/31(Mon) 16:18:08)
No40641 (もんたな さん) に返信

もしよろしければ、インサート部分のコードを
お教えくださいな。
引用返信 編集キー/
■40644 / inTopicNo.3)  Re[2]: VBからAccessへINSERT
□投稿者/ もんたな (2回)-(2009/08/31(Mon) 16:24:54)
No40643 (もりお さん) に返信
> ■No40641 (もんたな さん) に返信
>
> もしよろしければ、インサート部分のコードを
> お教えくださいな。

これです↓↓

INSERT INTO t_TRN_HDT_DAT(PROC_BAR, DELI_CD, HDT_IN_DT, IMP_DT)
VALUES("01545264439", "01", #2009/08/28 14:29:00#, #" & Now & "#)"

テーブル名がt_TRN_HDT_DATで、数字の部分は、Accessのテーブルのフィールドが
テキスト型なので""で囲んであります。
ちなみにVALUESは、StreamReaderでテキストファイルから読み込んだデータを
インサートするようになっています。

本当に説明へたですみません…。
引用返信 編集キー/
■40651 / inTopicNo.4)  Re[3]: VBからAccessへINSERT
□投稿者/ 魔界の仮面弁士 (1255回)-(2009/08/31(Mon) 17:04:33)
2009/08/31(Mon) 17:14:54 編集(投稿者)
No40644 (もんたな さん) に返信
> INSERT INTO t_TRN_HDT_DAT(PROC_BAR, DELI_CD, HDT_IN_DT, IMP_DT)
> VALUES("01545264439", "01", #2009/08/28 14:29:00#, #" & Now & "#)"

あれ?

日付の指定に若干の問題がある気がしますが(※注釈)、それに目を瞑ったとしても、
  INSERT INTO t_TRN_HDT_DAT(PROC_BAR, DELI_CD, HDT_IN_DT, IMP_DT)
  VALUES("01545264439", "01", #2009/08/28 14:29:00#, #" & Now & "#)"
というのは、構文的におかしいかと思います。

これがたとえば、
  INSERT INTO t_TRN_HDT_DAT(PROC_BAR, DELI_CD, HDT_IN_DT, IMP_DT)
  VALUES("01545264439", "01", #2009/08/28 14:29:00#, #2009/08/28 14:29:00#)
もしくは、
 SQL = "INSERT INTO t_TRN_HDT_DAT(PROC_BAR, DELI_CD, HDT_IN_DT, IMP_DT) " _
     & "VALUES(""01545264439"", ""01"", #2009/08/28 14:29:00#, #" & Now & "#)"
であるならばまだ分かりますが…。

生成された SQL が、本当に、"01" になっているかどうか確認してみてください。
 VALUES ("01545264439", "01", #2009/08/28 14:29:00#, #2009/08/28 14:29:00#)
ではなく、
 VALUES ("01545264439", 01, #2009/08/28 14:29:00#, #2009/08/28 14:29:00#)
になっていたりはしませんか?

また、できれば『Option Strict On』を有効にして、「数値→文字列」や「日付→文字列」といった
暗黙の型変換が起きないようにしておいた方が良いでしょう。


----- 注釈 -----
※ Now を直接文字列化する事は推奨されていません。書式を明示しましょう。また、
※ JET の日付書式は年月日順でも指定できますが、本来は月日年順とされています。
※ http://support.microsoft.com/kb/416056/ja

引用返信 編集キー/
■40652 / inTopicNo.5)  Re[3]: VBからAccessへINSERT
□投稿者/ やじゅ (1292回)-(2009/08/31(Mon) 17:08:10)
やじゅ さんの Web サイト
No40644 (もんたな さん) に返信

ダブルクォーテーションをシングルクォーテーションに変更してみるとか(意味ないかも)

パラメータ変数を使うようにするとか(お勧め)

引用返信 編集キー/
■40656 / inTopicNo.6)  Re[4]: VBからAccessへINSERT
□投稿者/ もんたな (3回)-(2009/08/31(Mon) 17:36:54)
No40651 (魔界の仮面弁士 さん) に返信
> 2009/08/31(Mon) 17:14:54 編集(投稿者)
>
確かにおかしいですよね…。
実は、一応コードにはこう書いてみました…。

mySQL = "INSERT INTO t_TRN_HDT_DAT(PROC_BAR, DELI_CD, HDT_IN_DT, IMP_DT) " _
& "VALUES('" & Bar & "', '" & Code & "', #" & Day & "#, " _
& "#" & Now.ToString("yyyy/mm/dd hh:mm:ss") & "#)"

VALUESの「Bar」は「01545264439」を格納しています。
同様に「Code」は「01」を、「Day」は「2009/08/28 14:29:00」を格納しています。
この構文ではエラーが発生したのですが…。

引用返信 編集キー/
■40660 / inTopicNo.7)  Re[5]: VBからAccessへINSERT
□投稿者/ 魔界の仮面弁士 (1256回)-(2009/08/31(Mon) 18:26:25)
2009/08/31(Mon) 18:34:25 編集(投稿者)
No40656 (もんたな さん) に返信
> mySQL = "INSERT INTO t_TRN_HDT_DAT(PROC_BAR, DELI_CD, HDT_IN_DT, IMP_DT) " _
>       & "VALUES('" & Bar & "', '" & Code & "', #" & Day & "#, " _
>       & "#" & Now.ToString("yyyy/mm/dd hh:mm:ss") & "#)"
それが正しい SQL 文になっているかどうかを確認するために、生成された mySQL を
 TextBox1.Text = mySQL
などとして画面上に展開し、それを Microsoft Access などで実行させてみてください。

Access 上で実行できないような SQL 構文であれば、VB から呼び出す事もできないでしょう。


> VALUESの「Bar」は「01545264439」を格納しています。
> 同様に「Code」は「01」を、「Day」は「2009/08/28 14:29:00」を格納しています。

Day という変数名の利用は推奨されていません。VB 標準の「Day 関数」と競合する名前だからです。
(予約語ではありませんので、使えないというわけでは無いのですが)

で。Bar、Code、Day それぞれのデータ型は何でしょうか?
また、それらの値に「'」などの特殊文字が含まれない事は保証されていますか?

たとえば Code が As Integer として宣言されていたとしたら、
 Code = "01"
と記述してあったとしても、生成された SQL は
  VALUES('01545264439', '01', 2009/08/28 14:29:00#, …
ではなく、
  VALUES('01545264439', '1', 2009/08/28 14:29:00#, …
になってしまうでしょう。この場合、DELI_CD フィールドがテキスト型であったとしても、
登録されるデータは "01" ではなく "1" になってしまう事が予想されます。


> この構文ではエラーが発生したのですが…。

データによってはエラーになってしまうでしょうね。

まず、Now.ToString の指定がいろいろと間違っています。

12時間法が指定されている割に午前/午後の表記が無いので、朝の8時と夜の8時を区別できませんし、
月と分も区別されていません。たとえば今だと、「2009/15/31 06:15:40」といった文字列になってしまいます。

さらにコントロールパネルの地域設定が『和暦』モードになっていれば、「21/17/31 06:17:06」になるでしょう。
また、日付や時刻の区切り記号が変更されていれば、「2009-17-31 06.17.59」といった表記になる可能性もあります。
なによりも、先述したとおり、JET の日付リテラル表記は年月日順ではありません。


こういう場合には、地域設定に依存しない構文を利用してください。
また、書式指定の際にはカルチャ指定(System.Globalization)も併用するべきです。



それはそれとして。
こういう場合には、データを文字列として埋め込むのではなく、パラメータ クエリにした方が無難ですよ。

引用返信 編集キー/
■40679 / inTopicNo.8)  Re[6]: VBからAccessへINSERT
□投稿者/ もんたな (4回)-(2009/09/01(Tue) 09:42:10)
No40660 (魔界の仮面弁士 さん) に返信
> 2009/08/31(Mon) 18:34:25 編集(投稿者)
>
詳しくご解説いただき、ありがとうございます。

> で。Bar、Code、Day それぞれのデータ型は何でしょうか?
> また、それらの値に「'」などの特殊文字が含まれない事は保証されていますか?

「Day」は推奨されていないことは知りませんでしたので、「Taking」に変更しました。
Bar、Code、TakingすべてString型に宣言しています。

ちなみに、

Acmd.CommandText = "INSERT INTO t_TRN_HDT_DAT(PROC_BAR, DELI_CD, HDT_IN_DT, IMP_DT) " _
         & "VALUES('" & Bar & "', '" & Code & "', #" & Taking & "#, " _
         & "#" & Now().ToString("yyyy/MM/dd HH:mm:ss") & "#)"

と書き直したら思ったようにデータがインサートされました。

> それはそれとして。
> こういう場合には、データを文字列として埋め込むのではなく、パラメータ クエリにした方が無難ですよ。

これってどういうことでしょうか?
よかったら教えて下さい。
引用返信 編集キー/
■40683 / inTopicNo.9)  Re[7]: VBからAccessへINSERT
□投稿者/ 魔界の仮面弁士 (1259回)-(2009/09/01(Tue) 11:25:07)
2009/09/01(Tue) 11:46:17 編集(投稿者)
No40679 (もんたな さん) に返信
> Acmd.CommandText = "INSERT INTO t_TRN_HDT_DAT(PROC_BAR, DELI_CD, HDT_IN_DT, IMP_DT) " _
>          & "VALUES('" & Bar & "', '" & Code & "', #" & Taking & "#, " _
>          & "#" & Now().ToString("yyyy/MM/dd HH:mm:ss") & "#)"
> と書き直したら思ったようにデータがインサートされました。

コントロールパネルの地域設定(地域と言語のオプション)で、日付を「和暦」にされていたり、
日付区切りの "/" や 時刻区切りの ":" を別の文字に変更されていると、この SQL は失敗します。

OS 設定に依存したコードにならないよう、Now.ToString の際には、
       Dim s1 As String = Now().ToString("yyyy/MM/dd HH:mm:ss")
ではなく、
       'Imports System.Globalization
       Dim s2 As String = Now().ToString("MM\/dd\/yyyy HH\:mm\:ss", CultureInfo.InvariantCulture)
       Dim s3 As String = Now().ToString("yyyy\/MM\/dd HH\:mm\:ss", CultureInfo.InvariantCulture)
などの構文を使うことをお奨めしておきます。


逆に言うと、開発したアプリにこういった問題が含まれているかどうかを検証するために、
テスト項目に、地域設定を通常以外の設定にした状態でのテストを含めておくことをお奨めします。
(利用者によっては、年を西暦から和暦表示に変更する方もおられますから)


>>こういう場合には、データを文字列として埋め込むのではなく、パラメータ クエリにした方が無難ですよ。
> これってどういうことでしょうか?

パラメータ クエリとは、Access で言うところの PARAMETERS を使った実装です。

ADO.NET においては、OleDbParameter 等を通じて操作します。
たとえば、このような実装になります。


' PARAMETERS 宣言を用いたパラメータ クエリ。
' http://office.microsoft.com/ja-jp/access/HP010322601041.aspx
'sql = "PARAMETERS Bar TEXT(11), Code TEXT(2), Taking DATE;" _
'    & "INSERT INTO t_TRN_HDT_DAT (PROC_BAR, DELI_CD, HDT_IN_DT, IMP_DT)" _
'    & " VALUES (Bar, Code, Taking, Now())"

' パラメータ部に、疑問符 (?) プレースホルダを使用したパラメータ クエリ。
sql = "INSERT INTO t_TRN_HDT_DAT (PROC_BAR, DELI_CD, HDT_IN_DT, IMP_DT)" _
    & " VALUES (?, ?, ?, Now())"

'ここからが本題。
Using insertCommand As OleDbCommand = con.CreateCommand()
    'SQL とパラメータ定義です。Add する順番は、SQL 内のパラメータ順と一致させてください。
    insertCommand.CommandText = sql
    insertCommand.Parameters.Add("Bar", OleDbType.VarWChar, 11)
    insertCommand.Parameters.Add("Code", OleDbType.VarWChar, 2)
    insertCommand.Parameters.Add("Taking", OleDbType.Date)
    insertCommand.Prepare()

    'データを渡した後、ExecuteNonQuery を実行します。
    insertCommand.Parameters("Bar").Value = "01545264439"
    insertCommand.Parameters("Code").Value = "01"
    insertCommand.Parameters("Taking").Value = #8/28/2009 2:29:00 PM#
    insertCommand.ExecuteNonQuery()

    insertCommand.Parameters("Bar").Value = "01234567890"
    insertCommand.Parameters("Code").Value = "99"
    insertCommand.Parameters("Taking").Value = #12/31/2010 12:34:56 AM#
    insertCommand.ExecuteNonQuery()
End Using


上記では 2 件のデータを登録していますが、データが変化しても SQL 文は同じ物が使えます。

この方法は、
 ・複数件のデータを大量に処理する際、SQL の再解釈が発生しない分、処理効率が向上する。
 ・数値や日付などを扱う際に、データを文字列化する必要が無いため、書式等を気にしなくて済む。
 ・「'」や「"」などの特殊文字を含んだデータを取り扱う際にも、エスケープ処理が不要となる。
などの利点があります。

引用返信 編集キー/
■40688 / inTopicNo.10)  Re[8]: VBからAccessへINSERT
□投稿者/ もんたな (5回)-(2009/09/01(Tue) 13:14:33)
2009/09/01(Tue) 13:14:48 編集(投稿者)
No40683 (魔界の仮面弁士 さん) に返信
> 2009/09/01(Tue) 11:46:17 編集(投稿者)
>
な、なるほど…。

知らないことだらけでしたが、これを参考にコードを書いてみます。

本当にありがとうございました。
解決済み
引用返信 編集キー/
■40713 / inTopicNo.11)  Re[9]: VBからAccessへINSERT
□投稿者/ 魔界の仮面弁士 (1261回)-(2009/09/01(Tue) 21:31:24)
No40688 (もんたな さん) に返信
> 知らないことだらけでしたが、これを参考にコードを書いてみます。
> 本当にありがとうございました。

No40679 を見る限り、本題の
>>> VBからは「01」というデータをインサートしたのですが、
>>> Accessのテーブルでは「1」と表示されてしまっています。
は解決したようですが、結局のところ、何が原因で「1」になっていたのでしょう…?

解決済み
引用返信 編集キー/
■40758 / inTopicNo.12)  Re[10]: VBからAccessへINSERT
□投稿者/ もんたな (6回)-(2009/09/02(Wed) 11:04:42)
2009/09/02(Wed) 11:08:51 編集(投稿者)

No40713 (魔界の仮面弁士 さん) に返信

> No40679 を見る限り、本題の
> >>> VBからは「01」というデータをインサートしたのですが、
> >>> Accessのテーブルでは「1」と表示されてしまっています。
> は解決したようですが、結局のところ、何が原因で「1」になっていたのでしょう…?

すみません…そのことすっかり忘れてました;;

SQLでは「数値」として抽出してきたデータを「文字列」にしたため
「01」が「1」になってしまっていたみたいです…。


あの、重ねて質問で申し訳ないのですが、1つ教えて下さい。

Now().ToString("yyyyMMdd")

と指定してこれをアクセスにインサートしたいのですが、
これでも何か問題ってあるでしょうか…
引用返信 編集キー/
■40761 / inTopicNo.13)  Re[11]: VBからAccessへINSERT
□投稿者/ 魔界の仮面弁士 (1262回)-(2009/09/02(Wed) 11:19:46)
No40758 (もんたな さん) に返信
> あの、重ねて質問で申し訳ないのですが、1つ教えて下さい。
> Today().ToString("yyyyMMdd")
> と指定してこれをアクセスにインサートしたいのですが、
> これでも何か問題ってあるでしょうか…

まず、アプリケーションはどこで実行されますか?

DB サーバー上で実行される場合、厳密には、DBサーバー上の時刻と
実行環境の時刻は一致していない可能性があります。もちろん、時刻合わせを
行うとは思いますが、可能であれば、どちらの時刻を使うのかは、
システム上で一貫性を持たせた方が良いかと思います。

で、もしも DB 上の時間で構わないのであれば、Date/Now 関数を SQL 中に埋め込んで、
 INSERT INTO foo (col1, col2) VALUES ( Now(), Year(Now()) * 10000 + Month(Now()) * 100 + Day(Now()) )
のような追加クエリを組み上げるという手があります。



一方、VB.NET 側で組む場合ですが、
 Today().ToString("yyyyMMdd")
という構文は NG です。

これでも一応は "20090902" を返してくれる事が多いのですが、確実であるとは言えません。
先に書いたように "210902" が返される可能性がありえるからです。

OS の設定を和暦表示にしていた場合、
 MsgBox(#12/31/2009#.ToString("yyyyMMdd"))
が「211231」になる事を忘れないようにしてください。

引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -