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

DatasetからRecordsetへの変換

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

VB2008にて開発しています。

Dataset(System.Data.Dataset)に入っているデータを
ADODB.Recordsetに移すことは可能でしょうか?
逆のRecordsetの内容をDatasetに入れる方法なら見つかるのですが
その反対が見つかりません。

目的としては、DBからDatasetへデータを取得するvb.netのDLLがあり
それを根幹の部分は変えずにExcelVBAで使えるようにしたいのです。
Excel側ではRecordsetのように1行ずつ読み込んで処理したり
フィールド名を指定して値を読み込んだりといった
ことがしたいです。

ですので、Recordsetに変換できるのなら一番ありがたいですが、
無理であれば他のExcelVBAで扱えるデータ形式に変換するのでもかまいません。

ちなみに、今まで試した方法としては、
dataset.writexmlでXMLの文字列にしてExcelに渡し、
それをExcel側でMSXML2.DOMDocumentオブジェクトに入れるところまでは
成功したのですが、どうも使い勝手が悪いようで、他の方法を探しています。

アドバイスいただけますと助かります。
■No32198に返信(さくらさんの記事)
> Dataset(System.Data.Dataset)に入っているデータを
> ADODB.Recordsetに移すことは可能でしょうか?

DataTable → Recordset ではなく、
DataSet   → Recordset なのですか?


複数のテーブルのリレーション構造を、Recordset で表現するのは難しいです。
ある程度までは、MSDataShape プロバイダーで表現できますが…。

とりあえず下記では、DataTable に対して ToRecordset メソッドを追加してみました。

Module Module1

#Region "DataTable から Recordset への変換"
    <System.Runtime.CompilerServices.Extension()> _
    Public Function ToRecordset(tbl As DataTable) As ADODB.Recordset
        Dim rs As New ADODB.Recordset
        Dim fields As New ArrayList()
        For Each col As DataColumn In tbl.Columns
            Dim n = col.ColumnName
            fields.Add(n)
            Dim t = Convert(col.DataType)
            Dim a = ADODB.FieldAttributeEnum.adFldUpdatable
            If col.ReadOnly Then a = a And Not ADODB.FieldAttributeEnum.adFldUpdatable
            If col.AllowDBNull Then a = a Or ADODB.FieldAttributeEnum.adFldIsNullable
            If a = 0 Then a = ADODB.FieldAttributeEnum.adFldUnspecified
            rs.Fields.Append(n, t, 0, a)
        Next
        Dim cols = fields.ToArray()
        rs.Open()
        For Each row As DataRow In tbl.Rows
            If row.RowState = DataRowState.Deleted Then Continue For
            rs.AddNew(cols, row.ItemArray)
        Next
        If Not rs.EOF Then rs.MoveFirst()
        Return rs
    End Function

    Private Function Convert(t As Type) As ADODB.DataTypeEnum
        Select Case t
            Case GetType(DateTime) : Return ADODB.DataTypeEnum.adDBTime
            Case GetType(Decimal) : Return ADODB.DataTypeEnum.adDecimal
            Case GetType(Boolean) : Return ADODB.DataTypeEnum.adBoolean
            Case GetType(Integer) : Return ADODB.DataTypeEnum.adInteger
            Case GetType(Single) : Return ADODB.DataTypeEnum.adSingle
            Case GetType(Double) : Return ADODB.DataTypeEnum.adDouble
            Case GetType(Byte()) : Return ADODB.DataTypeEnum.adBinary
            Case GetType(String) : Return ADODB.DataTypeEnum.adBSTR
            Case GetType(Short) : Return ADODB.DataTypeEnum.adSmallInt
            Case GetType(Byte) : Return ADODB.DataTypeEnum.adTinyInt
            Case GetType(Long) : Return ADODB.DataTypeEnum.adBigInt
            Case Else : Return ADODB.DataTypeEnum.adVarWChar
        End Select
    End Function
#End Region

End Module
2014/03/19(Wed) 18:58:33 編集(投稿者)

■No32198に返信(さくらさんの記事)
> dataset.writexmlでXMLの文字列にしてExcelに渡し、
> それをExcel側でMSXML2.DOMDocumentオブジェクトに入れるところまでは

DataSet.WriteXml ではなく、
DataTable.WriteXml で保存して、それを
 Workbooks.OpenXML "C:\TEMP\TEST.XML" , , xlXmlLoadImportToList
で読み込ませてみるとか。
魔界の仮面弁士様

返信ありがとうございます!

> DataTable → Recordset ではなく、
> DataSet → Recordset なのですか?
>
> 複数のテーブルのリレーション構造を、Recordset で表現するのは難しいです。

そうですね、今回の場合は単一のテーブルで十分です。
今は自宅で開発環境がないので、ぜひ明日職場で実装してみます^^

XMLから力技でrecordsetに入れようとしたりもしてみて
その時はデータ型が反映できずに挫折したのですが、
そのあたりも丁寧にコーディングされていて
なるほどと勉強になります。


> DataTable.WriteXml で保存して、それを
>  Workbooks.OpenXML "C:\TEMP\TEST.XML" , , xlXmlLoadImportToList
> で読み込ませてみるとか。

先の方法で解決しそうに思いますが、こちらも試してみます。
DatatableのみをXMLにして読み込むのはやってみてたんですが
xlXmlLoadImportToListはやってないので、
これで読み込んだものが扱いやすいようであれば
こちらも検討してみます。


試すのが楽しみです。
進んで出勤したい気分なんて久々です(笑)

また改めて報告させていただきます。
■No32201に追記(魔界の仮面弁士の記事)
> DataTable.WriteXml で保存して、それを
>  Workbooks.OpenXML "C:\TEMP\TEST.XML" , , xlXmlLoadImportToList
> で読み込ませてみるとか。

さらに別案。VBA 側で
 Set rs = CreateObject("ADODB.Recordset")
 rs.Open "C:\TEMP\SAMPLE.XML"
で読み取れるタイプの XML ファイルを .NET 側で生成してみました。


これは、
 「rs.Save "C:\TEMP\SAMPLE.XML", adPersistXML」
で出力されるタイプのファイル形式です。

(本来は編集状態なども管理できる形式ですが、ここでは細かい情報を端折っています)


参考資料:
  http://support.microsoft.com/KB/316337

'--------------------------------------------------
Imports <xmlns:rs='urn:schemas-microsoft-com:rowset'>
Imports <xmlns:z='#RowsetSchema'>
Imports <xmlns:s='uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882'>
Imports <xmlns:dt='uuid:C2F41010-65B3-11d1-A29F-00AA00C14882'>

Module Module1
    Public Sub PersistXML(ByVal tbl As DataTable, filePath As String)
        Dim doc = 
          <xml
              xmlns:s='uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882'
              xmlns:dt='uuid:C2F41010-65B3-11d1-A29F-00AA00C14882'
              xmlns:rs='urn:schemas-microsoft-com:rowset'
              xmlns:z='#RowsetSchema'>
              <s:Schema id='RowsetSchema'>
                  <s:ElementType name="row" content="eltOnly" rs:updatable="true">
                      <%= tbl.Columns.OfType(Of DataColumn)().
                          Select(Function(c, i) GetSchema(c, i + 1)) %>
                  </s:ElementType>
              </s:Schema>
              <rs:data>
                  <rs:insert>
                      <%= From row In tbl.AsEnumerable()
                          Where (row.RowState <> DataRowState.Deleted)
                          Select <z:row
                                     <%=
                                         From col As DataColumn In tbl.Columns
                                         Select New XAttribute(col.ColumnName, row(col))
                                     %>/>
                      %>
                  </rs:insert>
              </rs:data>
          </xml>

        doc.Save(filePath)
    End Sub

    Private Function GetSchema(col As DataColumn, idx As Integer) As XElement
        Dim tname As String
        ' dt:type 属性値を決定しているところ。面倒ならすべて string でも可。
        Select Case col.DataType
            Case GetType(Date) : tname = "dateTime"
            Case GetType(String) : tname = "string"
            Case GetType(Integer) : tname = "int"
            'Case GetType(Decimal) : tname = "number"  '※
            Case Else : tname = "float"
        End Select
        '※ dt:type="number" 型も使えるが、その場合は rs:dbtype 属性が必要

        Dim elm = <s:AttributeType
                      name=<%= col.ColumnName %>
                      rs:number=<%= idx %>
                      rs:write="true">
                      <s:datatype
                          dt:type=<%= tname %>
                          dt:maxLength=<%= col.MaxLength %>
                          rs:maybenull=<%= col.AllowDBNull %>
                      />
                  </s:AttributeType>
        Return elm
    End Function
End Module
■No32202に返信(さくらさんの記事)
> 試すのが楽しみです。
> 進んで出勤したい気分なんて久々です(笑)

失礼しました。

うっかり、VB2008 という箇所を読み落としていました。
そのままだとコンパイルエラーとなりますので、適宜修正をお願いします。


No32200 & No32204 
>  Case GetType(DateTime)
.NET 4 の System.Type には 比較演算子が実装されていますが、
.NET 3.5 以下の System.Type には、比較演算子が実装されていません。

そのため VB2008 では、 先述の「Select Case Type値」構文は
コンパイルエラーとなってしまいます。

型の .FullName による比較で代用するか、もしくは
If 文を用いた表現などに置き換えてみてください。



No32204 
>  Dim doc = 
>          <xml

この部分では、行継続文字(行末の「空白+アンダーバー」)を省略しています。

VB2008 の場合は、上記箇所でエラーとなるため、
 Dim doc = _
          <xml
のように記述するか、もしくは
 Dim doc = <xml
のように、改行せずに記述するようにしてください。


また、<s:ElementType> 直下に置いた
>   <%= tbl.Columns.OfType(Of DataColumn)().
>       Select(Function(c, i) GetSchema(c, i + 1)) %>
と書かれている Linq 式の記述に対しても、
    <%= tbl.Columns.OfType(Of DataColumn)(). _
        Select(Function(c, i) GetSchema(c, i + 1)) %>
のように、行末に「 _」の行継続文字を補完する必要があります。
<rs:insert> 直下の Linq 構文も同様です。
重ねてありがとうございます!


> うっかり、VB2008 という箇所を読み落としていました。
> そのままだとコンパイルエラーとなりますので、適宜修正をお願いします。

了解しました。
丁寧にありがとうございますm(_ _)m


> No32200 & No32204
>> Case GetType(DateTime)
> .NET 4 の System.Type には 比較演算子が実装されていますが、
> .NET 3.5 以下の System.Type には、比較演算子が実装されていません。
>
> そのため VB2008 では、 先述の「Select Case Type値」構文は
> コンパイルエラーとなってしまいます。
>
> 型の .FullName による比較で代用するか、もしくは
> If 文を用いた表現などに置き換えてみてください。

そうなんですか、.NET 4 では少し便利になっているんですね〜
If文よりSelect文が好きなので
Select Case True
Case col.DataType Is GetType(Date)
Case col.DataType Is GetType(String)
...
End Select
にしてみます。


改行の方も了解です。


これからいろいろやってみて、また後ほど結果報告させていただきます。
(ちょっと時間かかるかもですが・・・)
別件対応などが入ってすっかり遅くなりましたが
ご紹介いただいた方法全て試してみましたので結果報告させていただきます。

(1) DataTableをRecordsetに1行ずつ変換して入れる
(2) DtをXMLに変換保存したものを
  Workbooks.OpenXML "C:\TEMP\TEST.XML" , , xlXmlLoadImportToList
(3) DtをrecordsetでOpenできる形式のXMLにする方法

どの方法も成功しまして、最終的に(1)の方法を採用しました。


まず試したのは(2)の方法で、
これはこれで使いドコロがありそうですが、
なるべく外部にファイル保存するのは避けたいということもあり
今回は見送りました。

次に(1)を試して、思い通りの動きで感動!
完璧です。
※ちなみに、クラスの拡張について恥ずかしながら始めて知りまして、
便利なことができるものだと大変勉強になりました。

(3)はちょっとだけ仕様を変えて、
(2)で書いたように外部にファイル保存したくなかったので
XMLにしたものを文字列のままExcel側に渡して、
それをMSXML2.DOMDocumentでLoadしたものを
RecordsetでOpenするようにしました。
これも成功し、少しだけ迷いましたが
Excel側での処理が(1)の方がシンプルなのでそちらに決定!
※参考URLのページはそのものずばりのタイトルだったのに
探し損ねてました・・・。
でも相談前に見つけてたとしても、そのままでは使えなかったような気がします。
使いやすい形にしてご紹介くださってありがとうございました。


というわけで、この件は無事解決とさせていただきます^^

そのまま使えるコードを書いて下さったり
いくつも選択肢を提示して下さって
本当にありがというございました。

また何かご相談させていただくこともあるかもしれませんが
その時はよろしくお願い致します。
解決済み!
後に参考にされる方のために少しだけ補足しておきます。

日付型の変換に関してです。

(1)
> Case GetType(DateTime) : Return ADODB.DataTypeEnum.adDBTime

adDBTime は時刻部分しか格納しないようなので
adDBTimeStamp の方が良さそうです。


(2)
今の日付型(System.Datetime)は0001/01/01からですが、
ADODBの日付型は 0100/01/01 からしか入れられません。
なので、西暦100年より前の日付データなんかがあると
Recordsetへの変換時に「有効なOleAut日付ではありません。」
という例外が出るので、その可能性がある場合は対応が必要です。

まぁ、普通は入り口でブロックしてこんな日付は入らないように
すべきなので問題にならないと思いますが
うちは残念なことに入ってしまっていましたので・・・(^^;


補足は以上です。



魔界の仮面弁士さんへ
現在このモジュールを組み込んだDLLを使って、
社内で使用する予定のExcelアドインを作成中です。
本当にありがとうございましたm(_ _)m
解決済み!
■No32236に返信(さくらさんの記事)
> adDBTime は時刻部分しか格納しないようなので

ありゃ、間違えていますね、失礼しました。


> adDBTimeStamp の方が良さそうです。

VBA の Date 型(OLE の日付型)に相当するのは、本来は adDate のはずです。
これは、1899/12/30 を 0.0 として起算する日付型であり、精度は 1 秒です。

VB.NET で、System.DateTime の FromOADate / ToOADate メソッドを通じて、
変換されるのも、このタイプの日付型ですね。


一方の adDBTimeStamp は、いわゆるタイムスタンプ型と呼ばれる
日付型であり、10億分の 1 までの桁を保持するものです。

ちなみに、SQL Server にも「timestamp 型」もありますが、
こちらは日付ではなく 8 バイトバイナリとして扱われますので、
OLE DB の世界では adBinary として処理されます。
(ただし、パラメータとして使う場合は adVarBinary です)

adDBTimeStamp 型に相当する SQL Server の型は
 datetime 型 … 秒未満は 3桁まで、.000、.003、.007 秒単位の精度
 datetime2型 … 病未満は 7桁まで、100ナノ秒精度
ですね。


> まぁ、普通は入り口でブロックしてこんな日付は入らないように
> すべきなので問題にならないと思いますが
> うちは残念なことに入ってしまっていましたので・・・(^^;

ちなみに Excel ワークシートの日付型だと、
1900年2月29日が扱えてしまいますが、本来は存在しない日付なので
System.DateTime ではエラーになりますね。
http://www.relief.jp/itnote/archives/001333.php




なお VBA には、「Calendar = vbCalGreg」モードとは別に、
我々が使うことは無さそうな「Calendar = vbCalHijri」という
モードがありますが、この変換を .NET 側で請け負う場合は、
HijriCalendar クラスを使うことになります。
■No32239に返信(魔界の仮面弁士さんの記事)
私のいい加減な補足にフォローありがとうございますm(_ _)m


>>adDBTimeStamp の方が良さそうです。
>
> VBA の Date 型(OLE の日付型)に相当するのは、本来は adDate のはずです。
> これは、1899/12/30 を 0.0 として起算する日付型であり、精度は 1 秒です。
>
> VB.NET で、System.DateTime の FromOADate / ToOADate メソッドを通じて、
> 変換されるのも、このタイプの日付型ですね。

なるほど、精度が1秒ということはadDateで時刻まで持てるんですね?
データ型の一覧を見たら日付と時刻が両方記載されていたのが
adTimestampだけだったので、単純にこっちなのかと思ってしまいました(^^;
扱うデータは日付までしか持ってないのですぐはできませんが
(今回扱ってるDBは開発環境もないので・・・)
そのうち日付と時刻両方入ったデータでやってみたいと思います。


> ちなみに Excel ワークシートの日付型だと、
> 1900年2月29日が扱えてしまいますが、本来は存在しない日付なので
> System.DateTime ではエラーになりますね。
> http://www.relief.jp/itnote/archives/001333.php

これは全然思いつきもしませんでした。
上のエラーの方は、OverflowExceptionが発生するのでそれをキャッチして
西暦99年までの日付は強引に西暦100年に変更してしまうようにしたんですが
(配布時に説明をつける予定で)
こちらも対象の例外をキャッチして対応するようにしたいと思います。
※超レアケースぽいので、時間があればになるかもですが・・。


> なお VBA には、「Calendar = vbCalGreg」モードとは別に、
> 我々が使うことは無さそうな「Calendar = vbCalHijri」という
> モードがありますが、この変換を .NET 側で請け負う場合は、
> HijriCalendar クラスを使うことになります。
この辺りは今回は使わなくても良さそう(使いたくない)ですが、
チェックしておきたいと思います。


考えが及ばない所をいろいろ教えていただき勉強になります^^


すみません、面倒見ついでにもう少し教えていただけませんでしょうか。

DBに小数で入っている値が変換後に整数になってしまう現象が起こりました。
取得元DBはOracle9のNumber型で
データ型判定のSelect文ではDecimalと判定されています。

追ってみると
rs.AddNew(cols, row.ItemArray)
の部分で変わってしまい、
row.ItemArrayまでは小数を保持していますが
この直後のrsの中身が整数になっています。
(丸めではなく、小数点以下が切り落とされた数値になります。)

とりあえず
Case GetType(Decimal) : Return ADODB.DataTypeEnum.adDecimal
になっているのをDoubleに
Case t Is GetType(Decimal) : Return ADODB.DataTypeEnum.adDouble
とすると小数まで取得できるようになったのですが、
原因がわからずもやもやしています。

頑張って調べているのですが
なぜDecimalのままではうまくいかないのか、
本来どうするべきなのか、
がわからないでいます。


頼ってばかりですみませんが
ご教示いただけると助かります。
他に足りない情報があれば出しますので
よろしくお願い致します。
2014/04/08(Tue) 19:14:12 編集(投稿者)

DBTYPE_UI1 が DBTYPE_I1 と書かれていた点を修正。
(このあたりの情報は、公式の MDAC SDK でさえ微妙に間違っているという罠)

■No32245に返信(さくらさんの記事)
> なるほど、精度が1秒ということはadDateで時刻まで持てるんですね?

時刻部を含む日付を扱う場合、相手が Jet の場合は adDate、
相手が ODBC なら adDBTimestamp が主に利用されます。
(adDBTimestamp の精度は、SQL Server の日付型よりも細かいです)


.NET System.DateTime 型:0001/01/01 00:00:00.0000000 〜 9999/12/31 23:59:59.9999999
VBA Date 型/Jet DATE 型:0100/01/01 00:00:00 〜 9999/12/31 23:59:59
SQL Server date 型:0001/01/01 〜 9999/12/31
SQL Server time 型:00:00:00.0000000 〜 23:59:59.9999999
SQL Server datetime 型:1753/01/01 00:00:00 〜 9999/12/31 23:59:59.997
SQL Server datetime2 型:0001/01/01 00:00:00.0000000 〜 9999/12/31 23:59:59.9999999
Oracle DATE 型: -4712/01/01 00:00:00 〜 9999/12/31 23:59:59
Oracle TIMESTAMP 型: -4712/01/01 00:00:00.000000000 〜 9999/12/31 23:59:59.999999999

精度から言えば、adDBTimestamp が適切なのですが、そもそも VBA 側では
adDate 相当しか扱えないので、今回は adDate で十分でしょう。



一応、それぞれの日付型の違いを改めて記載しておきます。

----------------------------------------------------------------------
adDate (DBTYPE_DATE)
 秒精度の日付と時刻を管理。VBA や Jet の日付型に相当します。

adDBDate (DBTYPE_DBDATE)
 西暦ベースの年月日を日付部のみで管理。時刻は持ちません。
 SQL Server の DATE 型に相当します。
 year 部は「0〜9999」の範囲です(1 からでは無い)。
 month 部は「1〜12」、day 部は「1〜月の日数」です。

adDBTime (DBTYPE_DBTIME)
 秒精度で時刻部のみを管理。SQL Server の time 型に相当します。
 SQL92 仕様にあわせるため、second 部は「0〜61」の範囲まで対応します。
 hour 部は「0〜23」、minute 部は「0〜59」です。

adDBTimestamp (DBTYPE_DBTIMESTAMP)
 ODBC の日付型:精度はナノ秒。SQL Server では datetime2 型に近いです。
 年月日の部分は DBTYPE_DATE 相当、時分秒の部分は DBTYPE_DBTIME 相当で、
 さらに、秒未満の時刻を fraction 部として「0〜999,999,999」の範囲で持ちます。

adFileTime (DBTYPE_FILETIME)
 Windows API でいうところの FILETIME 構造体相当の日付型です。精度は 100 ナノ秒です。
 System.DateTime 型は、FromFileTime / ToFileTime のメソッドにてこの形式との
 相互変換が可能ですが、このメソッドは何故か Int64 型を経由する仕様となっており、
 System.Runtime.InteropServices.ComTypes.FILETIME への直接変換はできません。
----------------------------------------------------------------------



> 取得元DBはOracle9のNumber型で
> データ型判定のSelect文ではDecimalと判定されています。

Oracle 9i の NUMBER 型は、精度1〜38桁、位取り-84〜127桁の精度の固定小数点数ですね。
その場合は、adNumeric を使うのが良いでしょう。

ただしこの場合、.Fields.Append する際に有効桁数の指定も必要となります。
たとえば、NUMBER(18, 6) あるいは NUMERIC(18, 6) な型を再現する場合は、
 rs.Fields.Append(columnName, adNumeric)
 field = rs.Fields(columnName)
 field.Precision = 18 '列の最大精度
 field.NumericScale = 6 '小数点の右側の桁数(DECIMAL / NUMERIC のみ)
という感じになるはずです(未検証)。


> この直後のrsの中身が整数になっています。
小数桁が指定されていなかったためでは無いでしょうか。

日付型だけではなく、数値型も多数用意されています。
参考資料として掲載しますので、参考にしてみてください。

「※」で書いた部分は、それぞれを adNumeric にて管理しようとした場合に、
Precision プロパティに指定することが推奨される最大値です。

----------------------------------------------------------------------
adNumeric (DBTYPE_NUMERIC)
 固定小数点数。19 バイトで保持されます。基本的な考え方は adDecimal とほぼ同じですが、
 小数桁は 0桁〜38桁 の範囲、数値部は 128bitの整数型として管理されます。

adVarNumeric (DBTYPE_VARNUMERIC)
 小数桁の精度が確定しない NUMERIC 型。Parameter オブジェクトでのみ利用される型です。
 ※内部的には、precision が 255 な adNumeric に相当します。

adDecimal (DBTYPE_DECIMAL)
 固定小数点数。内部処理形式 Decimal の Variant 型です。VBA や Jet の 10 進型と同じものであり、
 「符号」と「96bitの整数型」とで管理される、±79,228,162,514,264,337,593,543,950,335 の
 範囲の数値のうち、「右から0〜28桁を小数部と看做す」という形式で管理されます。
 ※この値を adNumeric にて格納する場合は、precision には 29 を指定してください。

adCurrency (DBTYPE_CY)
 固定小数点数。内部処理形式 Currency の Variant 型です。VBA や Jet の 通貨型に相当。
 小数部の桁数は 4 桁固定、数値部は 64bit の整数型として管理されます。
 ※この値を adNumeric にて格納する場合は、precision には 19 を指定してください。

adSingle (DBTYPE_R4)
 短精度浮動小数点数。4 バイトで保持されます。
 VBA や JET のそれと同じものです。
 ※この値を adNumeric にて格納する場合は、precision には 7 を指定してください。

adDouble (DBTYPE_R8)
 倍精度浮動小数点数。8 バイトで保持されます。
 VBA や JET のそれと同じものです。
 ※この値を adNumeric にて格納する場合は、precision には 15 を指定してください。

adBigInt (DBTYPE_I8)
 符号付き 8 バイトの整数型。System.Int64 型に相当します。
 VBA でいうところの LongLong 型と同じものです。
 ※この値を adNumeric にて格納する場合は、precision には 19 を指定してください。

adUnsignedBigInt (DBTYPE_UI8)
 符号付き 8 バイトの整数型。System.Int64 型に相当します。
 MDAC SDK では、誤って DBTYPE_I8 と解説されているのでご注意を。
 ※この値を adNumeric にて格納する場合は、precision には 20 を指定してください。

adInteger (DBTYPE_I4)
 符号付き 4 バイトの整数型。System.Int32 型に相当します。
 VBA でいうところの Long 型と同じものです。
 ※この値を adNumeric にて格納する場合は、precision には 10 を指定してください。

adUnsignedInt (DBTYPE_UI4)
 符号無し 4 バイトの整数型。System.UInt32 型に相当します。
 ※この値を adNumeric にて格納する場合は、precision には 10 を指定してください。

adSmallInt (DBTYPE_I2)
 符号付き 2 バイトの整数型。System.Short 型に相当します。
 VBA でいうところの Integer 型と同じものです。
 ※この値を adNumeric にて格納する場合は、precision には 5 を指定してください。

adUnsignedSmallInt (DBTYPE_UI2)
 符号無し 2 バイトの整数型。System.UShort 型に相当します。
 ※この値を adNumeric にて格納する場合は、precision には 5 を指定してください。

adTinyInt (DBTYPE_I1)
 符号付き 1 バイトの整数型。System.SByte 型に相当します。
 ※この値を adNumeric にて格納する場合は、precision には 3 を指定してください。

adUnsignedTinyInt (DBTYPE_UI1)
 符号無し 1 バイトの整数型。System.Byte 型に相当します。
 VBA でいうところの Byte 型と同じものです。
 ※この値を adNumeric にて格納する場合は、precision には 3 を指定してください。
----------------------------------------------------------------------
■No32247に返信(魔界の仮面弁士さんの記事)

詳しい説明をありがとうございます!

実は別件対応が入ってしまってこの件はお預け状態でして、
内容はまだざっと見ただけなんですが、疑問が少しスッキリしました。

後日じっくり読ませていただいてから
改めて返信させていただきますm(_ _)m
2014/04/17(Thu) 14:55:03 編集(投稿者)

すっかり報告が遅くなりましてすみません。

数値型の方はadNumericで
PrecisionとNumericScaleを追加設定するようにしまして、
小数まで取得できるようになりました。


■No32247に返信(魔界の仮面弁士さんの記事)
> 精度から言えば、adDBTimestamp が適切なのですが、そもそも VBA 側では
> adDate 相当しか扱えないので、今回は adDate で十分でしょう。
なるほど、納得です。
※元々時刻情報まで持っている場合は少なくて、
本件も含めて日付だけあればいいことがほとんどなので
adDBDateにしてしまおうかとも思ったんですが
(そうすれば西暦0年から入れれるようですし)
汎用性も考慮してそれはやめました。


> 一応、それぞれの日付型の違いを改めて記載しておきます。
ありがとうございます。
一通り読ませていただいて、
またVBAを使う際には参考にさせていただきます。


>>取得元DBはOracle9のNumber型で
>>データ型判定のSelect文ではDecimalと判定されています。
>
> Oracle 9i の NUMBER 型は、精度1〜38桁、位取り-84〜127桁の精度の固定小数点数ですね。
> その場合は、adNumeric を使うのが良いでしょう。
>
> ただしこの場合、.Fields.Append する際に有効桁数の指定も必要となります。

最初に書いたように、adNumericで桁数を指定するようにしました。
コードを追加した場所としては、
レコードセットに列を追加した直後に
数値型であればPrecision とNumericScale を指定する形です。
rs.Fields.Append(n, t, 0, a)
If t = ADODB.DataTypeEnum.adNumeric Then
  rs.Fields(n).Precision = 29
  rs.Fields(n).NumericScale = 5
End If
※本来でしたら元々の精度をとってくるべきかもしれないですが
今回は一律に設定しました。

テストした限りではうまくいっていますが、
この方法で問題ありそうな場合はご指摘いただけたら助かります。



> 日付型だけではなく、数値型も多数用意されています。
> 参考資料として掲載しますので、参考にしてみてください。
ありがとうございます、一通り目を通しまして、
今後の参考にさせていただきます。


異なる体系間でデータ変換する必要が今まではほとんどなくて
最初のDatatableからRecordsetの変換も
無理なんじゃないかと思ったりしていたんですが
いろいろ教えていただいたおかげで対応でき、勉強にもなりました。

本当にありがとうございましたm(_ _)m
解決済み!

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