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

VB2010からのSQLコマンドの結果

環境/言語:[VisualBasic2010Express]
分類:[.NET]

アクセス97の選択クエリ―と同じ結果をvb2010ExpressからのSQLコマンドで
実現できなくて困っています。
なぜできないか?またどうすればできるかご教授ください。
mdbのテーブルはtbl_Aとtbl_Bの2個
tbl_AのフィールドはS_No(数値型、主キー)N_CD(テキスト型)COLOR(テキスト
型)の3個
tbl_BのフィールドはN_CD(テキスト型、主キー) N_Name(テキスト型)の2個と
なっております。
なおN_CDのデータはA1,A2,B1,B2といった形で英数文字となっています。

アクセスでの選択クエリ―の内容は
SELECT tbl_A.S_No, tbl_B.N_Name, tbl_A.COLOR FROM tbl_A INNER JOIN tbl_B
ON tbl_A.N_CD = tbl_B.N_CD;
としていまして実行結果も問題ありません。
これをvb2010の方ではそのまま
SQL.CmdText = "SELECT tbl_A.S_No, tbl_B.N_Name, tbl_A.COLOR FROM tbl_A INNER
JOIN tbl_B ON tbl_A.N_CD = tbl_B.N_CD"
としてみましたがうまくいきません。(選択されません)

ただし、N_CDの値をA1,B2,C3とかの英数文字ではなくA,B,Cのように英文字のみ
にするとうまくいきます。(アクセスでの結果と同じ結果になります。)
なおプロバイダはOLEDB.4.0です。

読みにくい長文で申し訳ありませんが原因、解決策をご存知の方、ご教授頂ける
ようお願い致します。
「ー」が「―」になっている点が気になりますが、それは置いといて…。

■No32240に返信(sironekoさんの記事)
> としてみましたがうまくいきません。(選択されません)

Access と結果が異なるとのことですが、Access 97 が使用する SQL 構文は
ANSI 89 モードの物ですが、Jet OLE DB Provider Version 4.0 のそれは
ANSI 92 モードであるため、微妙に差異を生じているのかも知れません。
(ちなみに、同 3.51 Provider は ANSI 89 モードが採用されています)


3 点確認させてください。

(1) 両方の N_CD のフィールド長は同一でしょうか?
 長さが異なっていたとか、"A1" の後ろに余計な文字(空白、Tab、NULL文字等)が
 含まれていたといった可能性はありませんか?


(2) N_CD のテキスト型は、CHAR タイプ/VARCHAR タイプのいずれでしょうか?
 (注:Access のテーブルデザイナでは両者を識別できません)


(3) 既存の mdb ではなく、新規の mdb でも同じ現象になりますか?


Imports System.Runtime.InteropServices
Imports System.IO
Imports System.Data
Imports System.Data.OleDb

Module Module1

    Sub Main()
        Dim mdb As String = Path.Combine( _
            My.Computer.FileSystem.SpecialDirectories.MyDocuments, _
            "SAMPLE.MDB")
        If File.Exists(mdb) Then
            File.Delete(mdb)
        End If
        CreateDatabase(mdb)

        Dim cb As New OleDbConnectionStringBuilder()
        cb.Provider = "Microsoft.JET.OLEDB.4.0"
        cb.DataSource = mdb
        cb("Locale Identifier") = 1041
        cb("Jet OLEDB:Engine Type") = 4
        cb("Mode") = 1

        Dim sql As String = "SELECT tbl_A.S_No, tbl_B.N_Name, tbl_A.COLOR FROM tbl_A INNER JOIN tbl_B ON tbl_A.N_CD = tbl_B.N_CD;"

        Dim ds As New DataSet()
        Using adp As New OleDbDataAdapter(sql, cb.ConnectionString)
            adp.Fill(ds, "Sample")
        End Using

        MsgBox(ds.Tables("Sample").Rows.Count)   '期待値は 3 行です
    End Sub

    Private Sub CreateDatabase(mdb As String)
        Dim engine As Object = Nothing
        For Each id In New String() {"35", "36", "120"}
            Try
                engine = CreateObject("DAO.DBEngine." & id)
                Exit For
            Catch ex As Exception
            End Try
        Next
        Debug.WriteLine(engine.Version)
        Dim db = engine.CreateDatabase(mdb, ";LANGID=1041;CP=932;COUNTRY=0;", 32I)
        'Dim ver = db.CreateProperty("AccessVersion", 10, "07.53", False)
        'Dim props = db.Properties
        'props.Append(ver)
        'Marshal.ReleaseComObject(ver)
        'Marshal.ReleaseComObject(props)
        db.Execute("CREATE TABLE tbl_A ( S_No INTEGER PRIMARY KEY, N_CD TEXT(2), COLOR TEXT(8));")
        db.Execute("CREATE TABLE tbl_B ( N_CD TEXT(2), N_Name TEXT(10));")

        db.Execute("INSERT INTO tbl_A VALUES ( 100, 'A1', 'RED'  );")
        db.Execute("INSERT INTO tbl_A VALUES ( 200, 'A2', 'PINK' );")
        db.Execute("INSERT INTO tbl_A VALUES ( 300, 'B1', 'BLUE' );")
        db.Execute("INSERT INTO tbl_A VALUES ( 400, 'B1', 'CYAN' );")

        db.Execute("INSERT INTO tbl_B VALUES ( 'A1', 'DAO' );")
        db.Execute("INSERT INTO tbl_B VALUES ( 'A2', 'ADO' );")
        db.Execute("INSERT INTO tbl_B VALUES ( 'B1', 'RDO' );")

        engine.Idle(&H8)

        db.Close()
        Marshal.ReleaseComObject(db)
        Marshal.ReleaseComObject(engine)
    End Sub

End Module



(※) 自分の手元には、Access 97 の環境がありません。
 Access 97 形式の mdb は DAO や ADOX からでは作れないため、
 上記では JET 3.0 形式の mdb で代用しています。
■No32242に返信(魔界の仮面弁士さんの記事)

魔界の仮面弁士様、返信有難うございます。取り急ぎお礼申し上げます。

>Access と結果が異なるとのことですが、Access 97 が使用する SQL 構文は
>ANSI 89 モードの物ですが、Jet OLE DB Provider Version 4.0 のそれは
>ANSI 92 モードであるため、微妙に差異を生じているのかも知れません。
>(ちなみに、同 3.51 Provider は ANSI 89 モードが採用されています)

この回答だけでなんだか胸のつかえが取れたような気がします。

返信にありました確認点ですが

> (1) 両方の N_CD のフィールド長は同一でしょうか?

自分はフィールド長まで気が回っていませんでした。確認しますとおっしゃる通り違っていました。ですがこれが原因ではありませんでした。(同じにしましたが結果は変わらずでした。)
>
> (2) N_CD のテキスト型は、CHAR タイプ/VARCHAR タイプのいずれでしょうか?
>  (注:Access のテーブルデザイナでは両者を識別できません)

これはどちらかわからなかったので少し調べてみたのですが、ネットで「AccessのChar型. Accessのテキスト型は末尾のスペースがカットされるvharchar型だが. 以下のようにCreate文をクエリで実行して作成した場合はchar型の項目を作成することができる。」とありましたのでたぶんVARCHAR タイプと思われます。
>
> (3) 既存の mdb ではなく、新規の mdb でも同じ現象になりますか?
>
これについてはまずアクセス97で新規作成してみました。が、結果は同じでした。
次にわざわざ作っていただいたプログラムでテストしてみました。(本当にありがとうございます。)
ただ、以下の箇所でエラー(3.5
'System.Runtime.InteropServices.COMException' の初回例外が Microsoft.VisualBasic.dll で発生しました。)となったので
> db.Execute("CREATE TABLE tbl_A ( S_No INTEGER PRIMARY KEY, N_CD TEXT(2), COLOR TEXT(8));")
とりあえず以下のように変えて実行させていただきました。(これも自分には原因がわかりませんけど・・・
db.Execute("CREATE TABLE tbl_A ( S_No INTEGER , N_CD TEXT(2), COLOR TEXT(8));")
結果、「期待値は 3 行です」となっていますがメッセージボックスには4と表示されました。

となると3.51 Providerにするしかないのかなと考えております。
それしか選択肢がないのか、他にもあるが最善なのか、といったことも多少、気にはなりますが。
■No32243に返信(sironekoさんの記事)
>>>(ちなみに、同 3.51 Provider は ANSI 89 モードが採用されています)
もっとも、現行 OS での 3.51 の利用は保証されていませんけれどね…。


>>(1) 両方の N_CD のフィールド長は同一でしょうか?
> 自分はフィールド長まで気が回っていませんでした。
> 確認しますとおっしゃる通り違っていました。
> ですがこれが原因ではありませんでした。
> (同じにしましたが結果は変わらずでした。)

フィールド長を「2」に設定したという認識であっていますか?
(データは A1,A2,B1,B2 なので、2 が最小サイズという認識です)


>>(3) 既存の mdb ではなく、新規の mdb でも同じ現象になりますか?
> これについてはまずアクセス97で新規作成してみました。
> が、結果は同じでした。

可能であれば、実験に使った mdb を添付していただけないでしょうか。


> ただ、以下の箇所でエラー(3.5
> 'System.Runtime.InteropServices.COMException' の初回例外が Microsoft.VisualBasic.dll で発生しました。)となったので

DAO 3.6 未満のバージョンの場合は、

CREATE TABLE tbl_A ( S_No INTEGER, N_CD TEXT(2), COLOR TEXT(8), CONSTRAINT idxNO PRIMARY KEY ( S_No ) )

とする必要があるかも知れません。(環境が無いので未確認です)


> 結果、「期待値は 3 行です」となっていますがメッセージボックスには4と表示されました。

う、コピペミス。

400, 'B1', 'CYAN' ではなく、
400, 'B2', 'CYAN' としていたつもりでした。m(_ _)m


何にせよ、「0行」にはならなかったという事であれば、当初の
>> としてみましたがうまくいきません。(選択されません)
とは異なる状況である、ということですよね。

検証した mdb で再現しない以上は、プログラムや環境の問題ではなく
「データ」に何かしらの問題があるようにも思えます。

仮に、元のデータに「異常をもたらすような何か」が含まれていたと
仮定すると、コピー & ペーストやインポートでは、その異常データが
新規 mdb にも伝播してしまう危険性があります。
原因を特定するまでは、改めてデータを手入力するか……あるいは一行ずつ、
検証用データを INSERT 文で挿入してテストデータを用意してみてください。
■No32244に返信(魔界の仮面弁士さんの記事)
> フィールド長を「2」に設定したという認識であっていますか?
> (データは A1,A2,B1,B2 なので、2 が最小サイズという認識です)


すみませんです。サイズがバラバラだったのを 20 に統一しただけでした

> 可能であれば、実験に使った mdb を添付していただけないでしょうか。

ここではサイズ的に無理でした。今度の休みに適当なところを探してアップしてみます。遅くて恐縮ですが、もしその時でもよろしければDLしてください。

> 400, 'B1', 'CYAN' ではなく、
> 400, 'B2', 'CYAN' としていたつもりでした。m(_ _)m

上記の部分を変更しますとダイアログで 3 となりました。

> 検証した mdb で再現しない以上は、プログラムや環境の問題ではなく
> 「データ」に何かしらの問題があるようにも思えます。
>
> 仮に、元のデータに「異常をもたらすような何か」が含まれていたと
> 仮定すると、コピー & ペーストやインポートでは、その異常データが
> 新規 mdb にも伝播してしまう危険性があります。
> 原因を特定するまでは、改めてデータを手入力するか……あるいは一行ずつ、
> 検証用データを INSERT 文で挿入してテストデータを用意してみてください。

はい。了解しました。また、貴重な時間を使って返信いただきありがとうございます。
自分のmdbファイルに問題がある可能性が高いということですので時間を見つけていろいろテストしてみたいと思います。
2014/04/09(Wed) 12:07:39 編集(投稿者)

■No32252に返信(sironekoさんの記事)
>> フィールド長を「2」に設定したという認識であっていますか?
>> (データは A1,A2,B1,B2 なので、2 が最小サイズという認識です)
> すみませんです。サイズがバラバラだったのを 20 に統一しただけでした

元質問は A1,A2,B1,B2 でしたよね?
であれば 20 ではなく、2 まで減らしておいてください。
(3 文字以上のレコードは、検証前に取り除いておいてください)

この作業の目的は、タブなどの見えにくい文字が混入している可能性を排除
するためのものです。そのため、余計な文字が入り込む余地が無くなるまで、
フィールド長を最大限に減らしておいてください。


この検証のためには、現状バラバラになっているテストデータのサイズを
統一したり、検証用データを間引いたりことも必要になってくるはずです。
そうした、検証用データの絞り込みやクレンジング作業の中で、
問題個所のデータやレコードを見つけられることがしばしばあります。


もし、そうしたデータが見つからなくとも、最小限の検証データを
構築する事ができれば、それが、他の環境でも再現性のある現象なのかどうか、
掲示板を見ている第三者にも検証しやすくなりますし。


> ここではサイズ的に無理でした。
OneDrive に置いて公開設定にするとか。


> 今度の休みに適当なところを探してアップしてみます。
了解です。
それまでに問題点が見つからないようであれば、一応こちらも、比較検証用として
Access 97 を入れた Hyper-V を、VB2010 環境とは別に用意しておきますね。
■No32253に返信(魔界の仮面弁士さんの記事)
> 2014/04/09(Wed) 12:07:39 編集(投稿者)
お世話になります。
あれから少し試してみました。
仮面弁士さんのコンソールアプリから出力されたmdbと自分でテストしていたmdbを比較したりデータを変えてみて試した結果、原因がわかりましたので報告いたします。

tbl_AのN_CDですがここのインデックスの設定を「はい(重複あり)」とすると英数文字の場合にはアクセスとVBで選択結果が変わるようです。

>
>>ここではサイズ的に無理でした。
> OneDrive に置いて公開設定にするとか。
>
別の話になりますが、これもおかしな話でデータを削除して5件しかないのですがサイズが2Mもありました。(今のtest.mdbは90kぐらいです)

>
>>今度の休みに適当なところを探してアップしてみます。
> 了解です。

もうすでに需要は無いかもしれませんが一応自分のmdbをアップしておきます。
https://onedrive.live.com/?gologin=1#cid=F25B9C2B97DD5F4E&id=F25B9C2B97DD5F4E%21105
と、この掲示板でも大丈夫そうなので両方に置いておきます。

最後に、原因が分かったので解決済みではあるのですが、インデックスの設定、はい(重複あり)で実現することは不可能でしょうか?
また後日(自分の時間が取れるときにもう少し調べた後)新たな質問として投稿するべきでしょうか?
添付ファイル: test2.zip (8 KB)
2014/04/14(Mon) 12:13:18 編集(投稿者)

■No32256に返信(sironekoさんの記事)
> あれから少し試してみました。

こちらでも確認してみました。結論から言うと、
 ★★
 ★★ この mdb は破損していますね…!
 ★★
という回答になります。


プログラムの問題ではなく、インデックスが破損しているために
JOIN 操作に失敗しているという状況です。

Access 97 にて、test2.mdb の「修復」を行うと、トドメを指す形に
なるらしく、完全に開けなくなりました。


なお、インデックスが使われないような問い合わせ方、たとえば

SELECT tbl_A.S_No, tbl_B.N_Name, tbl_A.COLOR
FROM tbl_A INNER JOIN tbl_B
ON StrConv(tbl_A.N_CD, 0) = StrConv(tbl_B.N_CD, 0)

などとした場合は、期待する結果が得られるようです。


> tbl_AのN_CDですがここのインデックスの設定を「はい(重複あり)」とすると
> 英数文字の場合にはアクセスとVBで選択結果が変わるようです。

mdb の定義を確認してみました。

tbl_A の N_CD は、「重複ありのインデックス」ですが、
tbl_B の N_CD は、「重複なしの固有インデックス」と「主キー」の
両方に割り当てられていますね。

両テーブルのインデックスを破棄して、再度、元のように
付け直した場合には、正常に稼動するようになりました。


> もうすでに需要は無いかもしれませんが一応自分のmdbをアップしておきます。
> https://onedrive.live.com/?gologin=1#cid=F25B9C2B97DD5F4E&id=F25B9C2B97DD5F4E%21105
> と、この掲示板でも大丈夫そうなので両方に置いておきます。

.NET から離れて、Access 97 にて検証してみましたが、やはり
破損しているらしく、この mdb 自体が奇妙な動作をしていました。

検証結果の画像を添付します。

(1) Microsoft.JET.OLEDB.4.0 での動作テスト結果です。.NET を介さずに
 テストしていますが、やはり「C3」「D4」が JOIN されません。

(2) tbl_B.N_CD で、「C3」や「D4」を書き換えようとすると、
 システムエラー -1601 が発生しました。
 mdb が破損している時に発生するエラーコードの一つです。

(3) 「A」や「B」の書き換えには成功します。

(4) tbl_B.N_CD に新規行を追加し、既存行と同じ「A」という値を
 追加すると、当然ながらデータ重複エラーが報告されます。

(5) しかし、「C3」「D4」に関しては重複登録できます。

(6) ちなみに、既存の「C3」と追加した「C3」は完全に同じデータでした。

(7) test6 の状態で再テストすると、追加したレコードは JOIN されますが、
 既存レコードは繋がりません。
添付ファイル: TEST.ZIP (81 KB)
■No32263に返信(魔界の仮面弁士さんの記事)
> 2014/04/14(Mon) 12:13:18 編集(投稿者)

> こちらでも確認してみました。結論から言うと、
>  ★★
>  ★★ この mdb は破損していますね…!
>  ★★
> という回答になります。
>
>
> プログラムの問題ではなく、インデックスが破損しているために
> JOIN 操作に失敗しているという状況です。

検証結果の画像、拝見いたしました。データ、mdbの破損でしたか・・・。お手数かけてしまいました。
当方の環境ではそんなそぶりもなく普通に扱えていたので気が付きませんでした。

以下、全ては試せないのですが試せたものについて結果を報告しておきます。

> (2) tbl_B.N_CD で、「C3」や「D4」を書き換えようとすると、
>  システムエラー -1601 が発生しました。
>  mdb が破損している時に発生するエラーコードの一つです。

エラーなく書き換え可能でした。
>
> (3) 「A」や「B」の書き換えには成功します。

同じく成功します。

> (4) tbl_B.N_CD に新規行を追加し、既存行と同じ「A」という値を
>  追加すると、当然ながらデータ重複エラーが報告されます。

同じくエラーとなります。

> (5) しかし、「C3」「D4」に関しては重複登録できます。

エラーとなり登録できません。

画像添付しております。
https://onedrive.live.com/?gologin=1#cid=F25B9C2B97DD5F4E&id=F25B9C2B97DD5F4E%21105
のmdbtest.zipです。

最後に一つだけお願いですが、コンソールアプリで出力されたSAMPLE.mdb、これのテーブルの構造をアクセスで変えたのですが、これも壊れてしまっているのでしょうか?できれば確認をお願い致します。データが悪いのは当方のアクセスのせいで開くと壊れるのではないのか?という気もします。以前リレーションシップが正常に表示されないといったことがあったのを思い出しました。
遅ればせながら当初の質問である ”アクセス97の選択クエリ―と同じ結果をvb2010ExpressからのSQLコマンドで実現できない”件に関しましてはデータ(MDB)の問題でありプログラム上の問題ではないことがわかりましたのでこの質問については解決済みとさせていただきます。

問題解決にあたり尽力していただいた魔界の仮面弁士さんには、改めてお礼申し上げます。どうも有難うございました。
解決済み!

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