● Excel2010で、開かれていない閉じたままのブック・Excel(xls)データをできるだけ速く読み込む方法(DAOにて)
本記事は「32bitの」Excelだけでのお話です。ご注意ください。
ただ、OSが64bitであっても、インストールされているExcelが32bitなら大丈夫ではないかと思います。
現在のExcelがどのbitかは、「Excel bit 確認」などの語句でWeb検索してみてください。
また、「閉じたままのブックからデータを読み出す方法」は、一般的には以下の5つがあります。(バージョン指定がないものは全バージョンで使えると思ってください。)
(01)「ExecuteExcel4Macro」というワークシート関数(VBAならApplicationオブジェクトのExecuteExcel4Macroメソッドに相当)を使う
(02)「Microsoft Query」というGUIの機能を使う
(03)「Microsoft Query」をVBA化したもの(つまりプログラム)を使う
・すべてのバージョンで「QueryTableオブジェクト+ODBC接続+VBA」が使える
・2007以降なら「QueryTableオブジェクト+ListObjectオブジェクト+ODBC接続+VBA」も使える
(「ListObjectオブジェクト」=「テーブル機能)のこと)
(04)「DAO+OLEDB接続+VBA+CopyFromRecordsetメソッド」を使う。(主にxls向け。xlsxとxlsmはできれば不可。)
(05)「ADO+OLEDB接続+VBA+CopyFromRecordsetメソッド」を使う。(xls、xlsx、xlsm全部行ける)
※参考記事
DAO、ADO、Microsoft Query(ExcelVBAのQueryTableオブジェクト)、では「読み込み」だけでなく、「閉じたまま」の「複雑条件集計」や「書き込み」もできます。
ただし、Excelの場合はなんと「削除」ができないので対応策が必要です。
対応策はこちら。『できないこと』。(←その他の「できないこと」も書いてあります。なお、「あえて削除しない」ことも結構あります。不正対策や整合性維持のためです。基本、削除フラグを立てるだけでも対応できます。もちろん、読み込みだけに使っても構いません。)
Excel2010で、『 開かれていない閉じたままのブック・Excel(xlsやxlsx・xlsm)ファイルのデータ 』を読み込むだけでなく、書き込む方法の一覧表(DAOやADOにて)
『~SQLにて「閉じたExcelファイルを ”閉じたまま” 書き込む方法01」~Microsoft Query(QueryTableオブジェクト)を利用する場合~』
『ADO:Excel2010で、開かれていない閉じたままのブック・Excel(xls)データをできるだけ速く読み込む方法ーその2(ADOにて)』
『Access2000VBA・Excel2000VBA独学~別の閉じたExcelファイルを ”閉じたまま” 読み込みや書き込をする方法~5つ』
目次
★ はじめに
★ テストデータをDAOで読み込んで、VBEditorのイミディエイトウィンドウに一覧表示する
★ テストデータをDAOで読み込んで、別のシートに転記する
★ Excel2010:動的な表ではない、多段的なクロス集計表を読み込めるかどうか?
1シートに複数の表がある場合に読み込めるかどうか?
(00)結論
(01)サンプルファイルのダウンロードとプログラムコード
(02)読み込み時の列名について
(03)読み込み時の行の位置について
(04)読み込み時の列と行についての少しのまとめ
(05)※補足:「F1」「F2」「F3」「F4」・・・という列名は本当は誰が決めるのか?
(06)列と行でクロスする1つのセルの値を読みたいときのおおまかな流れ
(07)列と行がクロスする1つのセルの値の読み込み→「ExecuteExcel4Macro」も視野に。
(08)同じコードで仮想マシンの「Win2000+Excel2000」でも試してみた結果
★ LANでつながったファイルサーバのファイルを読みに行くテスト
★ 「外部テーブルのフォーマットが正しくありません」エラーが出たりした場合の対処
※Shift+TABキー、もしくは、Homeキー、Homeキー+TAB数回、を押すと、目次付近に戻れます。
「32bitの」Excelファイルの値を、そのExcelファイルを開かずに読みに行く方法です。
(「64bitの」ExcelではDAOは使えないかもしれません(未確認です。)その場合はADOでやる方法があります。→こちら、でテストしましたが、これは32bitExcelでやったテストですので、64bitだとまた微妙にちがうかもしれません。ご参考にとどめるだけにしてください。)
閉じたままのExcelファイルのデータを取得するには、「ExecuteExcel4Macro」を使ったり、「Excel.Application」などで読み込む方法があるようなのですが、以下のサイトによりますと、データの量が多いと速度が遅くなる?みたいです。
http://excel-ubara.com/excelvba5/EXCELVBA242.html
というわけで、DAOとかADOなどのミドルウェアを使う方法で試してみました。
ミドルウェアとは、私もよくわかっていないのですみませんが、例えばExcelやAcessなどのアプリケーションソフトと、SQL ServerやJETエンジン、オラクル、MySQLといったデータベースシステムなどとの間に立って、お互いの仕様の違いを「なかったもの」としてデータをやり取りさせてくれるソフトウェアのこと・・・と大雑把には理解しています。
データベースのみならず、中間に位置してなんらかの便宜を取り計らってくれるソフトウェア全般とか・・・。
ODBCもミドルウェアの一種らしいです。
そんな感じです。
以下のサイトが図解でわかりやすかったのでご参考にしてください。
・「間違いだらけのデータアクセス」から脱却するために ミドルウェア
http://www.int21.co.jp/directmw/DirectMw.html" target="_blank
ちなみにですが、DAOやADOを使うと、具体的には、それ経由で Access/SQL Server/オラクル/MySQL、などのデータベースにアクセスし、そのデータをExcelシートに吸い込むことができます。
もちろん、データを読み行く対象は同じくExcelファイルでもOKですし、テキストファイルなどでも大丈夫です。
そういったことができることは以前から知ってはいたのですが、実際に使っていたのはAccess上でばかりで、Excelのデータを読みにいったことが無かったので、今回試してみました。
==========
ちょっとテストしてみたら、ネットワーク越し(UNCパス)のどこか向こう側のExcelファイルの中身を読みに行くにもそこそこ速いみたいなので、ご紹介したいと思います。(速くなかったらごめんなさい)
実際、僕自身の職場でも、ExecuteExcel4Macroでの処理と比べると10倍以上は速くなった気がします。
ネットワーク越しのExcelファイルの100個くらいのセルの値を読み込むのに、体感で0.5~1秒くらいでした。
今はDAOよりもADOを使うほうが普通だと思いますが(なのかな??)、あえて、古いほうのDAOが使えないかテストしてみました。Excel2000、Access2000ともに、どちらでもDAOは使えるのですが、2010などの新しいExcelでも古い技術のDAOが使えるのかが疑問だったので・・・。
あと、僕自身、AccessでもDAOしか使ったことがないという情けない状況だったのでそれもあります。
結果、一応、DAOも動くみたいです。(ただし新しいバージョンのExcelは 32bit版のExcelだけかも??64bitOSでもインストールされたExcelが32bit版ならDAOも動くみたいです。64bit版のオフィスならADOしか動かないかもしれません。ご自身でも調べてみてください。)
というわけで、以降、テスト内容です。
★ テストデータをDAOで読み込んで、VBEditorのイミディエイトウィンドウに一覧表示する
まず、D:\DAOテスト.xls を作成します。
Sheet1のA1からC11に以下のように値を入力します。
「氏名」「年齢」「性別」はA1、B1、C1、に入力します。
それ以降に各値をオートフィルなどで入力します。
1 2 3 4 5 6 7 8 9 10 11 |
氏名 年齢 性別 1 20 男 2 21 男 3 22 男 4 23 男 5 24 男 6 25 男 7 26 女 8 27 女 9 28 女 10 29 女 |
=============
次に、別の xlsファイルを作成してVisual Basic Editor を開きます。
ツール→参照設定、にて、「Microsoft DAO 3.6 Object Library」にチェックを入れてOKします。
標準モジュールを1つ、挿入します。
以下のコードをコピペして、実行します。
※「xlFName = "D:\DAOテスト.xls"」の "D:\DAOテスト.xls" の部分は、UNCパスでもOKです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
Option Explicit Private Sub ReadByDao01() '参照設定でMicrosoft DAO 3.6 Object Library にチェックを入れておく。 'このファイルを「D:\DAOテスト.xls」として配置 Dim DB As DAO.Database Dim RS As DAO.Recordset Dim xlFName As String Dim SheetName As String Dim StrData01 As String 'データベースファイル名定義、システムテーブル定義 xlFName = "D:\DAOテスト.xls" SheetName = "Sheet1$" '「Sheet1」をシステムテーブルとして読み込む。 '「$」を付けるとシステムテーブルになる。 'データベース(xlFName)ファイルを開く(ここではxlsファイル) 'HDR=NO →「IMEX」の値を「=1」にしたときは、1行目を列名として読込む。(どうやら数値もテキストとして読み込まれるようです) 'HDR=YES →「IMEX」の値を「=1」にしたときは、1行目を列名として読込まない。(どうやら数値は数値、文字列は文字列として読み込まれるようです) 'IMEX=1→表示されているセル書式の値でデータを吸い込む。(今回のテストでは完全にはそうはなりませんでした。よくわかりません。) ' ただし、https://support.microsoft.com/ja-jp/help/194124/prb-excel-values-returned-as-null-using-dao-openrecordset ' や http://blog.sorceryforce.net/?p=154 を参照 'Excel 8.0→「VBA Excel バージョン番号」でGoogle検索。 ' 8.0で2000でも2010でも動いた。 ' Set DB = OpenDatabase(xlFName, False, False, "Excel 8.0;HDR=NO;IMEX=1") Set DB = OpenDatabase(xlFName, False, False, "Excel 8.0;HDR=YES;IMEX=1") 'レコードセットを定義 Set RS = DB.OpenRecordset(SheetName) 'レコードセットの最終レコードまでを読込む 'EOFはシートの最後じゃなくて、レコードとして入力された最終行 Do Until RS.EOF With RS 'A列・B列・C列のデータを取得 '「HDR=NO」=1行目を列名として読み込まない場合その1→列名を「Index番号」で表現して変数に吸い込む ' StrData01 = StrData01 & .Fields(0) & vbTab & .Fields(1) & vbTab & .Fields(2) & vbCrLf '「HDR=NO」=1行目を列名として読み込まない場合その2→列名を「システムテーブルとしての列名」で表現して変数に吸い込む ' StrData01 = StrData01 & .Fields("F1") & vbTab & .Fields("F2") & vbTab & .Fields("F3") & vbCrLf '「HDR=YES」=1行目を列名として読み込む場合→列名を1行目の「セルの列名」で表現して変数に吸い込む StrData01 = StrData01 & .Fields("氏名") & vbTab & .Fields("年齢") & vbTab & .Fields("性別") & vbCrLf .MoveNext '次のレコード(行)に移動 End With Loop Debug.Print StrData01 RS.Close DB.Close Set RS = Nothing Set DB = Nothing End Sub |
========================
では次に、"D:\DAOテスト.xls" のSheet2にも何らかのデータを書き、それをまた別のExcelファイルから読みこんで、そのファイルのSheet1に転記するテストもしてみます。
では、まず、"D:\DAOテスト.xls" のSheet2に、以下のようなデータを入力します。
これもオートフィルでテキトーでいいです。
1 2 3 4 5 6 7 8 9 10 11 |
氏名 年齢 性別 100 10 女 101 21 女 102 32 女 103 43 女 104 54 男 105 65 男 106 76 女 107 87 女 108 98 男 109 109 女 |
できたら、そのシートの名前を、「読み込みテスト」という名前に変えます。
それもできたら、まったく別のExcelファイルのVisual Basic Editor を開きます。
ツール→参照設定、にて、「Microsoft DAO 3.6 Object Library」にチェックを入れておきます。
その後、挿入→ひょじゅんモジュールにて、標準モジュールを1個挿入して、そこに以下のプログラムコードをコピペして実行してみます。
※「xlFName = "D:\DAOテスト.xls"」の "D:\DAOテスト.xls" の部分は、UNCパスでもOKです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
Option Explicit Private Sub ReadByDao02() 'VBE画面のツール→参照設定、にて、「Microsoft DAO 3.6 Object Library」にチェックを入れておきます。 '「AS Object(レイトバインディング=実行時バインディング)」を使えば、参照設定しなくても大丈夫な方法も使えます。 'ただ、初心者の方は、インテリセンス機能を使えるように、参照設定にチェックを入れる方法をおすすめします。 Dim DB As DAO.Database '読込対象のXLSファイルのDAOオブジェクト用の変数 Dim RS As DAO.Recordset '読込対象のシートのDAOオブジェクト用の変数 Dim SrcXLFName As String '読込対象のXLSファイルのフルパス格納用の変数 Dim SrcSHTName As String '読込対象のシート名の格納用の変数 Dim WS01 As Worksheet '自ファイルのデータ転記対象のシートを格納(=指定)するためのオブジェクト変数 Dim i As Integer 'そのシートの行数を表すための変数 '読込対象としたいXLSファイルを指定する SrcXLFName = "D:\DAOテスト.xls" '読込対象としたいシートを指定する SrcSHTName = "読み込みテスト$" '「読み込みテスト」シートをシステムテーブルとして読み込む。 '「$」を付けるとシステムテーブルになる。 'データベースファイル(SrcXLFName)を開く。ここではxlsファイル。(=DAOとしてのデータベースを定義づけ) 'HDR=NO →「IMEX」の値を「=1」にしたときは、1行目を列名として読込む。(どうやら数値もテキストとして読み込まれるようです) 'HDR=YES →「IMEX」の値を「=1」にしたときは、1行目を列名として読込まない。(どうやら数値は数値、文字列は文字列として読み込まれるようです)(列名を読みたくない場合など) 'IMEX=1→表示されているセル書式の値でデータを吸い込む。(今回のテストでは完全にはそうはなりませんでした。よくわかりません。) 'Excel 8.0→「VBA Excel バージョン番号」でGoogle検索。「8.0」で2000でも2010でも動いた。 Set DB = OpenDatabase(SrcXLFName, False, False, "Excel 8.0;HDR=YES;IMEX=1") '読込対象のシートを開く。(DAOとしてのレコードセットを定義づけ) Set RS = DB.OpenRecordset(SrcSHTName) Set WS01 = Worksheets("Sheet1") 'データを吸い込むシートの定義(データを転記するシートの定義づけ) i = 2 '列名のことを考えて、 '読み込みデータを 2行目から入力したい場合は 2とする。 '「HDR=NO」にしたとき、列名も読み込んで '1行目から入力したいなら 1とする 'ここでは前者でテストしてます。 WS01.Range("A2:C30").ClearContents '読み込み先のファイルの行数が増減するといけないので、 '一応、転記するシートをきれいに空白しておく。 '前回よりデータ行数が少ないと、前回のデータが残ってしまうので。 '読込対象のレコードセット(シート)の最終レコードまでを読込む 'EOFはシートの最後じゃなくて、レコードとして入力された最終行 Do Until RS.EOF With RS 'Sheet1の A列・B列・C列に読み込んだデータを転記 WS01.Range("A" & i) = .Fields("氏名") 'レコードセット側のカレント行の「氏名」列の値を「Aのi」セルに転記 WS01.Range("B" & i) = .Fields("年齢") 'レコードセット側のカレント行の「年齢」列の値を「Bのi」セルに転記 WS01.Range("C" & i) = .Fields("性別") 'レコードセット側のカレント行の「性別」列の値を「Cのi」セルに転記 .MoveNext 'レコードセット側(読込データ側)を次のレコード(行)に移動 End With i = i + 1 '自ファイル側(Excelファイル側)も次の行に移動 Loop RS.Close DB.Close Set RS = Nothing Set DB = Nothing End Sub |
Sheet1にうまく読み込めたでしょうか?
このコードでは列名は読み込まない設定になっているので、1行目は空白ですが、2行目からちゃんと転記されていれば成功です。
先にも書きましたが、これは読み込み対象のExcelファイルが、ファイルサーバにあったり、別のPCにある場合も、パスをUNCパスを使えば大丈夫です。
もともとDAOやADOを使うと、読み込み先のデータの指定(取得)が直感的でラクなので、また、「ExecuteExcel4Macro」などと比べるとかなり柔軟な処理ができるので、速くて処理もしやすいなら、これからこちらをメインで使うほうがいいかもしれないなあ、と思いました。
僕自身はExcelをほとんど知らないに等しいので、DAOやADOがあってくれてほんとうに助かります。
もちろん、「少ないセルの数・・・2、3個のセルの値を取得するだけ」、といった場合なら、「ExecuteExcel4Macro」のほうが短く書けるし便利なので、適材適所で使い分ければ良いと思います。
なお、レコードセット側のカレント行の列の値をExcelのセルに転記する場合、
WS01.Range("A" & i) = .Fields("氏名")
WS01.Range("B" & i) = .Fields("年齢")
WS01.Range("C" & i) = .Fields("性別")
といった記述方法でも、
WS01.Range("A" & i) = .Fields!氏名
WS01.Range("B" & i) = .Fields!年齢
WS01.Range("C" & i) = .Fields!性別
という記述方法でも、どちらでも大丈夫です。
ただ、前者のほうが、( )の中身を・・・、リテラル値って変ですけど「実際の値というか名前」でも、また逆に、実際の名前じゃなくて「文字列型変数」でも、どちらでも格納(記述)することができるので、便利です。
汎用的なコードを書きやすくなる=共用部品を作りやすくなる=面倒くさいのが減る、ので、こちらの記述方法をおすすめします。
AccessでフォームのMeキーワードを使うときも同じです。
例えばテキストボックスなどを指定するときに、
「Me!氏名」と書くよりも、
「Me("氏名") 」と書くほうが面倒かもしれませんが、のちのち色々と便利です。
見た目的にも「あ、コントロール名だな」とわかったり、サブフォーム内のコントロール指定でもカッコの数で記述ミスが無いかわかったり・・・。( )の中にIndex番号を使うこともあり、「あああ、これは本当に"オブジェクト"なんだな」と改めて「なるほど」と感じたり、「コレクション」の意味が少し直感的にわかりやすくなったり気付きやすくなったり・・・。オブジェクトとコレクションの関係が「!」を使う場合よりも明確に感じられたり・・・。
とにかく、(" ")で書くほうが、ヘルプやWebの解説とあいまって、何かと「気づき」が増えやすいと思うのです。
「!」ばっかりで書いていたときは、そういうことは全く起きませんでした。
「カッコばっかり多くてウザイ。読みにくい。」と感じる方も多いのかもしれませんが・・・ぼく個人としては視覚的にもやりやすいです。
また、VBEditorでは、かなり長い単語でもダブルクリックで一発文字列選択(一発単語選択、一発変数選択、など)ができるケースがほとんどなので、列名に関してはコピペなども使いやすく、("")の中に列名を入力する場合も手間がかからないため、さほど苦になりません。
よくAccessの参考書やWebサイトにて、フォームでもADOでもDAOでも、後者の「!」を使った記述方法がよく説明されるのですが、共用部品が作りにくくなっちゃって、意外と、けっこう面倒くさいです。
「最初から、 .Fields("性別") みたいな、書きかたを教えてくれればよかったのに・・・」、と思うことが多いです。
★ 列名も転記して、データはループではなく「Range.CopyFromRecordset」メソッドで転記する例
以下の例は、「D:\DAOテスト.xls」というExcelファイルの、「Sheet1」の内容を現在アクティブなシートに吸い込みます。
ファイルの置き場所はサーバでもいいので、「D:\DAOテスト.xls」の部分は「\\サーバ名\1\DAOテスト.xls」といったUNCパスでも大丈夫です。
このようなやり方でやれば、後半に書いた「グリーンの三角のエラー」が出ませんので、列名も転記したい場合は、このやり方がひとつの基本、となると思います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
' ' Option Explicit Private Sub ReadByDao03() 'VBEの参照設定でMicrosoft DAO 3.6 Object Library にチェックを入れておく。 '事前に「Sheet1」に何らかの表を持つ「D:\DAOテスト.xls」が配置してある必要があります。 '「AS Object(レイトバインディング=実行時バインディング)」を使えば、参照設定しなくても大丈夫な方法も使えます。 'ただ、初心者の方は、インテリセンス機能を使えるように、参照設定にチェックを入れる方法をおすすめします。 'このようなやり方でやれば、後半に書いた「グリーンの三角のエラー」が出ませんので、 '列名も転記したい場合は、このやり方がひとつの基本、となると思います。 Dim DB As DAO.Database Dim RS As DAO.Recordset Dim xlFName As String Dim SheetName As String Dim StrData01 As String Dim i As Integer xlFName = "D:\DAOテスト.xls" SheetName = "Sheet1$" '「Sheet1」をシステムテーブルとして読み込む。 '「$」を付けるとシステムテーブルになる。 '↑データベースファイル名定義、システムテーブル定義 Set DB = OpenDatabase(xlFName, False, False, "Excel 8.0;HDR=YES;IMEX=1") '↑ 'データベース(xlFName)ファイルを開く(ここではxlsファイル) 'HDR=NO →「IMEX」の値を「=1」にすると、1行目を列名として読込む。(どうやら数値もテキストとして読み込まれるようです) 'HDR=YES →「IMEX」の値を「=1」にすると、1行目を列名として読込まない。(どうやら数値は数値、文字列は文字列として読み込まれるようです) 'IMEX=1→表示されているセル書式の値でデータを吸い込む。(今回のテストでは完全にはそうはなりませんでした。よくわかりません。) ' ただし、https://support.microsoft.com/ja-jp/help/194124/prb-excel-values-returned-as-null-using-dao-openrecordset ' や http://blog.sorceryforce.net/?p=154 を参照 'Excel 8.0→「VBA Excel バージョン番号」でGoogle検索。 ' 8.0で2000でも2010でも動いた。 Set RS = DB.OpenRecordset(SheetName) '↑レコードセットを定義 For i = 0 To (RS.Fields.Count - 1) Debug.Print RS.Fields(i).Name ActiveSheet.Cells(1, i + 1).Value = RS.Fields(i).Name Next i '↑列名だけをアクティブなシートに転記。 ' 列番号は「0(ゼロ)」からスタートなので、 ' 最後の列の列番号は(RS.Fields.Count - 1)で表します。 ActiveSheet.Range("A2").CopyFromRecordset RS '↑レコードセットの実データ内容「のみ」を、つまり、列名以外のすべてのデータをアクティブなシートに転記。 '=============ここから本来は不要================== '以下、レコードセットの最終レコードまでをイミディエイトに表示。 '本来は要らない処理だけど、前項のプログラムとの比較のために一応書きました。 '(EOFはシートの最後じゃなくて、レコードセットとしての最終行。) RS.MoveFirst 'CopyFromRecordsetしたあとは、RS.EOFまで行ってしまうようなので、 'いったん先頭のレコードに戻らないといけない。 'ループをEOFまでやって、そのあとCopyFromRecordsetしたときも同じ。 Do Until RS.EOF With RS 'A列・B列・C列のデータを取得 StrData01 = StrData01 & .Fields("氏名") & vbTab & .Fields("年齢") & vbTab & .Fields("性別") & vbCrLf .MoveNext '次のレコード(行)に移動 End With Loop Debug.Print StrData01 '=============ここまで本来は不要================== RS.Close DB.Close Set RS = Nothing Set DB = Nothing End Sub ' ' |
★ Accessのmdbファイルのテーブル内容を、DAOにて、列名も転記して、データはループではなく「Range.CopyFromRecordset」メソッドで転記する例
以下の例は、「D:\1\tes001.mdb」というmdbファイルの、「T99_社員マスタ」というテーブルのデータをシートに吸い込みます。
ファイルの置き場所はサーバでもいいので、「D:\1\tes001.mdb」の部分は「\\サーバ名\1\tes001.mdb」といったUNCパスでも大丈夫です。
こちらも、このようなやり方でやれば、後半に書いた「グリーンの三角のエラー」が出ませんので、列名も転記したい場合は、このやり方がひとつの基本、となると思います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
' ' Sub ReadByDao04() 'VBEの参照設定でMicrosoft DAO 3.6 Object Library にチェックを入れておきます。 '事前に「D:\1\tes001.mdb」というmdbファイルに、「T99_社員マスタ」というテーブルが作成されている必要があります。 '「AS Object(レイトバインディング=実行時バインディング)」を使えば、参照設定しなくても大丈夫な方法も使えます。 'ただ、初心者の方は、インテリセンス機能を使えるように、参照設定にチェックを入れる方法をおすすめします。 Dim DB As DAO.Database Dim RS As DAO.Recordset Dim i As Integer Set DB = OpenDatabase("D:\1\tes001.mdb") Set RS = DB.OpenRecordset("T99_社員マスタ") For i = 0 To (RS.Fields.Count - 1) ActiveSheet.Cells(1, i + 1).Value = RS.Fields(i).Name Next i '↑列名だけをアクティブなシートに転記。 ' 列番号は「0(ゼロ)」からスタートなので、 ' 最後の列の列番号は(RS.Fields.Count - 1)で表します。 ActiveSheet.Range("A2").CopyFromRecordset RS '↑レコードセットの内容を、列名以外をアクティブなシートに転記。 RS.Close Set RS = Nothing DB.Close Set DB = Nothing End Sub ' ' |
★ Excel2010:動的な表ではない、多段的なクロス集計表を読み込めるかどうか?1シートに複数の表がある場合に読み込めるかどうか?
Excel2010でテストした場合なので、古いバージョンではどうなのかわかりませんが、また、表の形にもよるのかもしれませんが、一応、動的な表ではなく、多段的なクロス集計表(静的な表)であっても読み込めるっぽいです。(僕のほうだけとか2010だけとかだったらすみません!また、ネットワーク越しでUNCパスではまだ試してません!)
そしてこの場合、1シートに複数の表があっても、また、結合されたセルがあっても読み込めるようです。
結合されたセルの値は、「勝手に」「分解されたものとみなされて」読み込まれるようです。
(値自体は・・・、セルが分解されたのちに、そのうちの一番左のセルに値が来るみたいです。)
また、「シート上の」、「何らかの値が入力されたもっとも左側に位置するセル」が、「”このシート=このシステムテーブル”の一番左側の列」と自動認識され、「F1」という列名として認識されるようです。
結合されたセルについては、例えばシステムテーブルのどこかの行の、「F1」、「F2」、「F3」、で結合されていた場合なら、自動的に「F1だけに値が入力された」ものとみなされ、、「F2、F3、は空白」とみなされるようです。
では、列の指定の方法をご説明します。
xlsm拡張子のサンプルファイルのダウンロード
https://euc-access-excel-db.com/00000WPZIP/tes1xlsm.zip
xls拡張子のサンプルファイルのダウンロード
https://euc-access-excel-db.com/00000WPZIP/tes1xls.zip
※ウィルスチェックはESETで行いました。
サンプルファイルの名前は、2010用のものは「tes1.xlsm」で、2003以前用のものは「tes1.xls」です。
解凍してから「Dドライブのルートとデスクトップの2か所」にコピペして、デスクトップ側の「tes1.xlsm」や「tes1.xls」から試してみてください。
VBEを開いて、「Module1」の「ReadByDAO03」を実行します。
このサンプルはファイルの中身のシートで、「静的な表-意外とわかりにくい」というシート(クロス集計表)の内容の一部を、Sheet2に転記するものです。(「静的な表-意外とわかりにくい」というシートはDAOにて「システムテーブル」として扱っています。あと、Sheet2はだいぶ後ろのほうのシートです。)
基本的には、デスクトップの「tes1.xlsm」からも「tes1.xls」からも、Dドライブの「tes1.xls」(xls拡張子のほう)は読めると思いますが、「tes1.xlsm」では「外部テーブルのフォーマットが正しくありません」といった感じのエラーになると思います。これはxlsx拡張子のファイルに作り直しても同じです。
xlsmやxlsxはどうやらDAOでは「閉じた状態」では開けないっぽいです。
その場合は、Dドライブのルートのxlsmを直接開いて、テストしてみることも試してみてください。一応、開いてあればSheet2にデータが吸い込まれると思います。
ただし、その場合は、「閉じたファイルの内容を読みたい」ということはできなきなくなってしまいますが・・・。
どうしても閉じたファイルの内容をDAOかADOで読みたい場合は、xlsmやxlsx拡張子のファイルの場合は、ADOで読みに行くのが良いかもしれません。(一番最後にADOでのテストの記事のURLを載せてあります)
なお、2010でファイルを開いた場合は、xlsmでもxlsでも、「コンテンツの有効化」を押してからお試しください。
VBEの「参照設定」に、「Microsft DAO 3.6 Object Liblary」にチェックが入ってないとエラーになりますのでそこを事前に確認してください。
あと、Sheet2に読み込んだ値が数値のセルだけ下図のようになってしまったら、その下のURLのWeb記事に解決方法が書いてありますのでそれをそのままお試しください。
『文字列扱いになっている数字を数値へ一括で変換する方法』
https://excel-master.net/cells-worksheets-control/text-numerical-conversion/
一応、「ファイル→オプション→数式」、にて、「エラーチェック」のところで「バックグラウンドでエラーチェックを行う」のチェックをはずすと緑のマークやミニメニューは表示されなくなります。
でもちょっと心配なので、上記URLのように「1をかけてしまう」ほうが確実かと思います。
特に数値は、文字列と誤認されてしまうと集計間違いを起こしてしまいますのでそれが起こる可能性があって、しかも、気づきもしない確率が高まる・・・、ということですと問題ですので・・・。一時的にならよいかもしれませんけど・・・。
※2018/07/29追記:VBAにてワンクリックでグリーン三角マークを消して数値化するサンプルプログラムを作りましたのでご覧になってみてください。
『Excel2010:「このセルにある数値が、テキスト形式か、またはアポストロフィで始まっています」というグリーンの隅っこ三角エラーマークを消して、セルの値を明示的に数値化するプログラム』
では、以下、DAOでのサンプルプログラムです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
' ' Option Explicit Private Sub ReadByDao3() 'VBE画面のツール→参照設定、にて、「Microsoft DAO 3.6 Object Library」にチェックを入れておきます。 Dim DB As DAO.Database '読込対象のXLSファイルのDAOオブジェクト用の変数 Dim RS As DAO.Recordset '読込対象のシートのDAOオブジェクト用の変数 Dim SrcXLFName As String '読込対象のXLSファイルのフルパス格納用の変数 Dim SrcSHTName As String '読込対象のシート名の格納用の変数 Dim WS01 As Worksheet '自ファイルのデータ転記対象のシートを格納(=指定)するためのオブジェクト変数 Dim i As Integer 'そのシートの行数を表すための変数 ' ' '読込対象としたいXLSファイルを指定する ' SrcXLFName = "D:\DAOテスト.xls" ' ' '読込対象としたいシートを指定する ' SrcSHTName = "読み込みテスト$" '「読み込みテスト」シートをシステムテーブルとして読み込む。 ' '「$」を付けるとシステムテーブルになる。 SrcXLFName = "d:\tes1.xlsm" SrcSHTName = "静的な表-意外とわかりにくい$" '「Sheet1」をシステムテーブルとして読み込む。 '「$」を付けるとシステムテーブルになる。 'データベースファイル(SrcXLFName)を開く。ここではxlsファイル。(=DAOとしてのデータベースを定義づけ) 'HDR=NO →「IMEX」の値を「=1」にしたときは、1行目を列名として読込む。(どうやら数値もテキストとして読み込まれるようです) 'HDR=YES →「IMEX」の値を「=1」にしたときは、1行目を列名として読込まない。(どうやら数値は数値、文字列は文字列として読み込まれるようです)(列名を読みたくない場合など) 'IMEX=1→表示されているセル書式の値でデータを吸い込む。(今回のテストでは完全にはそうはなりませんでした。よくわかりません。) 'Excel 8.0→「VBA Excel バージョン番号」でGoogle検索。「8.0」で2000でも2010でも動いた。 Set DB = OpenDatabase(SrcXLFName, False, False, "Excel 8.0;HDR=YES;IMEX=1") '読込対象のシートを開く。(DAOとしてのレコードセットを定義づけ) Set RS = DB.OpenRecordset(SrcSHTName) Set WS01 = Worksheets("Sheet2") 'データを吸い込むシートの定義(データを転記するシートの定義づけ) i = 2 '列名のことを考えて、 '読み込みデータを 2行目から入力したい場合は 2とする。 '「HDR=NO」にしたとき、列名も読み込んで '1行目から入力したいなら 1とする 'ここでは前者でテストしてます。 WS01.Range("A2:C30").ClearContents '読み込み先のファイルの行数が増減するといけないので、 '一応、転記するシートをきれいに空白しておく。 '前回よりデータ行数が少ないと、前回のデータが残ってしまうので。 '読込対象のレコードセット(シート)の最終レコードまでを読込む 'EOFはシートの最後じゃなくて、レコードとして入力された最終行 Do Until RS.EOF With RS ' 'Sheet1の A列・B列・C列・・・・に読み込んだデータを転記(列を列名で指定する場合のやり方) ' WS01.Range("A" & i) = .Fields("F1") 'レコードセット側のカレント行の「F1」列の値を「Aのi」セルに転記 ' WS01.Range("B" & i) = .Fields("F2") 'レコードセット側のカレント行の「F2」列の値を「Bのi」セルに転記 ' WS01.Range("C" & i) = .Fields("F3") 'レコードセット側のカレント行の「F3」列の値を「Cのi」セルに転記 ' WS01.Range("D" & i) = .Fields("F4") 'レコードセット側のカレント行の「F4」列の値を「Dのi」セルに転記 ' ' WS01.Range("G" & i) = .Fields("F18") 'レコードセット側のカレント行の「F18」列の値を「Gのi」セルに転記 ' WS01.Range("H" & i) = .Fields("F19") 'レコードセット側のカレント行の「F18」列の値を「Hのi」セルに転記 ' WS01.Range("i" & i) = .Fields("F20") 'レコードセット側のカレント行の「F20」列の値を「iのi」セルに転記 ' ' ' 'Sheet1の A列・B列・C列・・・・に読み込んだデータを転記(列をDAOの列番号で指定する場合のやり方) WS01.Range("A" & i) = .Fields(0) 'レコードセット側のカレント行の「F1」列の値を「Aのi」セルに転記 WS01.Range("B" & i) = .Fields(1) 'レコードセット側のカレント行の「F2」列の値を「Bのi」セルに転記 WS01.Range("C" & i) = .Fields(2) 'レコードセット側のカレント行の「F3」列の値を「Cのi」セルに転記 WS01.Range("D" & i) = .Fields(3) 'レコードセット側のカレント行の「F4」列の値を「Dのi」セルに転記 WS01.Range("G" & i) = .Fields(17) 'レコードセット側のカレント行の「F18」列の値を「Gのi」セルに転記 WS01.Range("H" & i) = .Fields(18) 'レコードセット側のカレント行の「F18」列の値を「Hのi」セルに転記 WS01.Range("i" & i) = .Fields(19) 'レコードセット側のカレント行の「F20」列の値を「iのi」セルに転記 .MoveNext 'レコードセット側(読込データ側)を次のレコード(行)に移動 End With i = i + 1 '自ファイル側(Excelファイル側)も次の行に移動 Loop RS.Close DB.Close Set RS = Nothing Set DB = Nothing End Sub ' ' |
列名は、読み込み元の表(システムテーブル)の何らかの値が入った列(=起点となるセル=列名だけでも可のようです)の「一番左の列から順」に・・・、
「F1」
「F2」
「F3」
「F4」・・・
という名前でDAO側では自動認識されます。
あるいは
「0」
「1」
「2」
「3」・・・
とDAO側から自動認識されます。
なお、この場合は列の直接の名前ではなく、
「左からの列番号が0番の列(つまり1番目の列)」
「左からの列番号が1番の列(つまり2番目の列)」
「左からの列番号が2番の列(つまり3番目の列)」
「左からの列番号が3番の列(つまり4番目の列)」・・・
という意味で扱われます。
なので、読み込みたい列の指定方法としては、前述のプログラムの場合を例にとると、
RS.Fields("F1")
RS.Fields("F2")
RS.Fields("F3")
RS.Fields("F4")・・・
といった感じで指定するか、あるいは、
RS.Fields(0)
RS.Fields(1)
RS.Fields(2)
RS.Fields(3)・・・
と指定します。
(※「With」でまとめてある場合は、「RS」は省略できます。)
ここでの注意点としては、シート=システムテーブル上のA列が「F1」の列にみなされるわけではなく、最初に値の入ったセル(起点のセル)の列が「F1」になります。
(※ ただ、これはExcelのバージョンによって違うかもしれません。でも2010ではこういう動きでした。2000だと「A列=F1=0」になるっぽいです。僕のPCだけかもしれませんがもしそうだったらごめんなさい。後述の(08)に少しこのことについて書きました。)
例えば、A列もB列もC列も全部(縦方向に)空白で列名も無く、D列になって初めて何らかの値が入っていたら、それが列名であっても値であってもD列が「F1」にみなされます。
なお、シート=システムテーブル上に複数の表があって、それぞれ、複数の空白列で区切られている場合は、その区切りの空白列は「F×××」とみなされるようです。
「F×××」という列名で「みなされない」のは、シート=システムテーブル上の一番最初(一番左側&一番上側)に位置する表・あるいはセルの左側に、「もし空白列があった場合は、その列だけ」のようです。
あと、こちらの→「ADO.NETでExcelファイルに接続する」のサイトに、レコードセットのデータ範囲について少し詳しい説明がありました。
ここから少し引用させていただきます。
=========引用ここから========
【レコードセットのデータ範囲について】
•[名前](Excelで定義した領域名)
•[シート名$](データが入力されている左上から右下までの範囲)
•[シート名$名前]
•[シート名$セル範囲]([シート名$] の範囲内のみ)
をテーブル名として、SELECT/INSERT/UPDATEが普通に使えます。ただしDELETEはできない。
=========引用ここまで========
これはDAOではなく、ADOに関するWebページの記述でした。
でも、もしかしたらDAOでも同じように指定できるのかもしれません。
ご自身でもお試しになってみてください。
なお、ADOでは・・・
•[シート名$セル範囲]([シート名$] の範囲内のみ)は
「シート名$A1:G200」のように指定します。
•[シート名$名前]は
名前の定義、の機能で定義した名前を使うのかなあ?
テストしてないので、ちょっとよくわかりません。
一応、こちらもご参考に↓。「Excel 8.0」みたいな「ISAM形式」についても書いてあります。
『ADO:Excel2010で、開かれていない閉じたままのブック・Excel(xls)データをできるだけ速く読み込む方法ーその2(ADOにて)』
また、「IMEX=1」などの「IMEXの値」については、以下のサイトがよくわかりますのでこちらも、是非ご参考にしてみてください。
『DAOでExcelデータを読み込みテキストボックスに表示』
http://hanatyan.sakura.ne.jp/vbhlp/dao_002.htm
ポイントだけ引用させていただきます。
=========引用ここから========
接続文字列のオプション IMEX の意味と結果
0 はエクスポートモードです。
1 はインポートモードです。
2 はリンクモード (完全な更新が可能) です。
省略すれば 2 と同じようです。
HDR=NO 1行目を読み込んで表示する。
HDR=YES 1行目を読み込まない。
(詳しくは下記、※1. を見て下さい。)
組合せによる表示状態(※テキストボックスに表示した場合)
IMEXの値 HDR=NO HDR=YES
省略 1行目を読込まない 1行目を読込む
0 1行目を読込まない 1行目を読込まない
1 1行目を読込まない 1行目を読込む
2 1行目を読込まない 1行目を読込む
=========引用ここまで========
※実際に、上記のIMEXの値を変えて、HDR=NOやHDR=YESや省略を試すと、エラーで読み込めないケースもありました。なのでこの情報をベースに、ご自分でも状況に応じて事前にお試しください。(2018/08/11追記)
また、列に関してはそういう風で、行に関しては起点のセルに関係なく、空白行があっても無くても、それも含めて「××行目」とみなされるようです。
ただ、「(シートとしての)1番最初の行は「列名」のための行」、とみなされます。
ですので、シート=システムテーブル上の・・・
行番号2の行が実データとしての「1行目」
行番号3の行が実データとしての「2行目」
行番号4の行が実データとしての「3行目」
行番号5の行が実データとしての「4行目」・・・
とみなされるようです。
つまり「行番号の値・マイナス・1」が、DAOやADOにおける「データとしての」行の位置となります。
少しまとめますと・・・、次のような感じです。
シート=システムテーブル上の、何らかの値が入力された「最も左・最も上」のセルを起点として、
列に関しては、シートの列番号A、B、C、、、に関わらず、
その起点のセルの列が「F1」列、あるいは「列番号0」となり、
行に関しては、シートの行番号1、2、3、、、から「マイナス1」した数が、
DAOやADOにおける「実データとして」の「1行目」「2行目」・・・、つまり、
行番号2の行が1行目、行番号3の行が2行目となっていき、
もし複数の表があっても、最初のセルが起点となって、そこから数えて、あとは同上。
そして、表と表の区切りに空白列が何列あったとしても、起点セルから数えて「F××」列、あるいは「列番号××」となる。
行に関しては複数表であってもシートの「行番号マイナス1」という形と同じ。
・・・という感じになります。
(05)※補足:「F1」「F2」「F3」「F4」・・・という列名は本当は誰が決めるのか?
これは僕はよくは分かっていないのですみませんが、動きを見てますとなんとなくですけど、多分、Excelが勝手に決めてくれてて、それを、DAOやADO、Microsft Queryやピボットなどが流用している・・・という感じに見えます。
これは「このシートをシステムテーブルとして使う」となった時点で、Excelがそうしてくれているのではないか?と勝手に推測しています。(それか、ODBCドライバがそうしてくれてるのかもしれません。)
例えば、Microsft Query の画面でも、なにかイレギュラーなことがあると、よく列名が「F14」「F15」・・・みたいな感じに勝手に書き換わってる・・・、ということがあります。(同じ列名がダブった時とかだったかな?忘れました。チェックするのが面倒いかったです。すみません。)
そういうところからみると、今回のサンプルファイルも、行番号1に列名が入力されているわけではないので、Excelが勝手に「あれぇ~??、これシステムテーブルのくせに列名が無いなあ。そんじゃー、こっちで勝手に「F1」「F2」「F3」「F4」・・・って付けとくね!」という感じにつけてくれているのだと思います。
あ・・・、でも、そういう意味からすると、もしかしたら、1シートに複数のクロス集計表があって、それをシステムテーブルとしてDAOやADOで読みに行きたいときは、「シートの行番号1のセルたちのところは、横一行・全部、あえて空白にしておいたほうがいい」のかもしれないですね。
そうじゃないかもしれないし、検証してないのでわかりませんが、一度、ご自分でも試してみてください。
いずれにしても、「F1」「F2」「F3」「F4」・・・とか「0」「1」「2」「3」・・・みたいな形で列が指定・操作できるので便利でありがたいことです。
(06)列と行でクロスする1つのセルの値を読みたいときのおおまかな流れ
なお、ある一つのセルを列と行のクロスでピンポイントに読みにいきたい場合は、DAOやADOの場合はちょっと面倒くさいです。
参考図:以下、本文と起点のセルや求めたいセルのアドレスが違いますが、ご参考にしてください。
DAOやADOの場合は、「列」×「行」という指定だけですぐに読みに行けるわけではなくて、いったん、目的の行に移動してから列を指定してクロスしたセルの値を読み込みます。
これは1シート上に複数の表がある場合も同じです。
例えば、「B1」のセルが起点のセルで、すなわち、B列が「F1」になっていた状況を考えてみます。
まず、行番号「1」のすべての横方向のセル達は、全部列名としてとらえられています。
ですので、行番号「2」の行が実データとしての「1行目」となります。
なので、この状況のときにもし「C10」のセルの値を読みに行きたいという場合は、まず「10マイナス2」で、実データとしての「8行分」を移動します。
このとき、なぜ「10マイナス1」ではなくて「10マイナス2」になるのかということが疑問になると思います。
そのことについて(下手くそですみませんが)ご説明しますと、まず、DAOやADOでは、(Accessではなくて)Excelの場合、最初に位置している行の場所はシートとしての行番号「2」の行であり、そしてそこに既に「今居る」ということになっています。(今居る行のことを「カレント行」と呼びます。)
DAOやADOは、「その認識が前提」「きまり・掟」となっている機能なのです。
だから、そこから1行分移動すればB3の行・・・、2行分移動すればB4の行・・・、3行分移動すればB5の行・・・という風になっていきます。
つまり、「行数としてはシートの行番号マイナス1」なんだけれども、目的のセルに移動するための「移動する行の数」としては「行番号マイナス2」ということになってしまい、意味が異なるため、話としてはちょっとややこしくなっています。
ただまあ、そう深く考えずに、
「行数としては、今居る場所としては、シートの行番号マイナス1」なんだけれども
「最初の行から目的のセルに移動する数としてはシートの行番号マイナス2」(目的のセルの行番号―2)
・・・という風に深く考えずに決め打ちして覚えてしまえばよいと思います。
その後、「C列」を読みたいわけなので、「起点のB列=F1=列番号0」ということからすると、1列右になるわけですから、「C列=F2=列番号1」となります。
というけで、「B列が起点の場合」に「C10」のセルをピンポイントで読みに行きたい場合は、まず、「8行分」を移動して、そののち、「RS.Fields("F2")」あるいは「RS.Fields(1)」を読みに行けばいいということになります。
※「With」でまとめてある場合は、「RS」は省略できます。
実際に書くプログラムとしては次のようになります。
RS.Move (8) ’8回(8行分)下に下がって、シートとしての行番号10の行へ移動。
Debug.Print RS.Fields(1) ’Bの列から1つ右の列、つまりC列のその行の値を表示
「Do Until RS.EOF ~ Loop」の行を全部消すかコメントアウトして、この2行に書き換えます。
もちろん「RS.Fields(1)」は「RS.Fields("F2")」と書いてもOKです。
なお、クロスで値を求めたいときは、あらかじめ『シート内の複数の表をすべて含んで考えた通しの行番号(連番)』をふっておけば、SQLでも求められるかもしれません。(エラーでできない可能性もありますが・・・)
ただ、(AccessでのDAO・ADOではなくて)Excelの場合は、上記のようにシート=システムテーブルの列番号(A、B、C、・・・)や行番号(1、2、3、・・・)を参考・指標として使えるので、それを利用するほうが私たち初心者にはわかりやすいとは思います。
ちなみにですが、AccessにおけるDAO・ADOでは、Excelのような「列番号(A、B、C、・・・)や行番号(1、2、3、・・・)」という概念が無いのでどちらの方法もよく使います。
また、逆に、Excelでも複雑な条件でクロスの値を見たい場合は、SQLで求めたほうが便利になるシーンも、もしかしたらあるかもしれません。
エラーでできない可能性もありますが・・・。
まだ試してないので、もしSQLで試したい方は以下の記事をご参考に試してみてください。
『Excel2000にて表と表を紐付けするときに、VLOOKUP関数以外に、「DAO」というミドルウェアを使って、紐付いた値を、1つのセルに表示する方法』
https://euc-access-excel-db.com/tips/ct09_biz-pctec/ct090201_biz_pc_basic/relationship-dao01
(07)列と行がクロスする1つのセルの値の読み込み→「ExecuteExcel4Macro」も視野に。
他の閉じたファイルの、「列と行でクロスするセルの値」を読み込みたいとき、もちろんですが、そのセルの数が少ないなら、「ExecuteExcel4Macro」を使うことも視野に入れてください。
別に、無理にDAOでやる必要もありませんので・・・。
DAOやADOのほうが便利なケースのときだけ、それらを使って、他の閉じたファイルの値の取得をしてみてください。
「ExecuteExcel4Macro」を使うどんなケースがあるかしらん?
例えば、読み込みたいセルが数百個という「表」のような場合や、単一セルをバラバラに100個読み込みたい、というようなケースなら、DAOやADOのほうが便利な気がしますね。
(あくまで、読み込みたい先のファイルは「閉じたまま」の場合です。)
逆に、「単一セルを4、5個(=数個だけ)、読み込めればいい」、みたいな場合はDAOやADOじゃなくて「ExecuteExcel4Macro」のほうがいいかもですね。
また、誰かがすでに開いているExcelファイルを、さらに読みに行く、なんてことが「DAOやADOではできない、ということなら、その場合も「ExecuteExcel4Macro」を使ったほうがいいですよね。
検証してませんのでご自分でもご確認くださいませ。
(08)同じコードで仮想マシンの「Win2000+Excel2000」でも試してみた結果
一応同じプログラムコードにて、Win2000+Excel2000でもクロス集計表をシステムテーブルとして読み込むことができました。
でも、F1の列(番号0の列)が、どうやら「起点のセル」が関係なくて、A列=F1=0番、という感じになって動いてしまいました。
この動きは、「xls拡張子だから」ではないようです。
Win10+Exce2010でこのxlsファイルを動かしたら、xlsmど同様に起点セルからF1列になっていました。
なので、OSか、Excelか、はたまた、ODBCドライバか何かのせいかもしれません。
なぜこうなるのかはわかりませんし、僕のPCだけかもしれません。
2010では、xlsmを読み込んでも、xlsを読み込んでも、起点セルから読み込まれていて、起点セルの列の左側の空白列は全部無視されている感じです・・・
仮想マシン2000の場合はxlsmファイルは読めませんが、xlsを読み行くと、この項のテストのような動きになってしまいました(あくまで僕のPCは、ですが・・・)
いずれにしても、「バージョンごと・OSごと・環境ごとに 動きが違う」かもしれないので、事前に、異なる動きにならないかを確かめておいたほうがよさそうです。
※こちらもまだUNCパスでのテスト=ネットワーク越しのファイルのクロス集計表を読みに行くテストはできていません。すみません。
★ LANでつながったファイルサーバのExcelファイルをDAOで読みに行くテスト
同じプログラムコードでExcel2013 にて、xls拡張子のファイルをDAOにて読みにいったら、エラー無く読めました。
xlsmの場合は、DAOでは読めませんでした。開いてあれば読めるのですが、閉じたまま読みたかったのでダメでした。
ただ、プログラムコードをDAOではなく、ADOに書き換えて読みに行ったら大丈夫でした。
また、xlsについてはADOでも読めました。
たた、ケースによっては、DAOのほうが最初のアクセスだけは速いような気がしました。
ADOは一番最初のアクセス時に数秒の時間がかかることが多かったです。
ただ、それが気にならなないシーンなら、ADOのほうが良いのかもしれません。
ADOなら64bitのExcelでも(もちろんOSが64bitでも)コードを書き換えずに使えるみたいですので。
★ 「外部テーブルのフォーマットが正しくありません」エラーが出たりした場合の対処
まず、ここで書いていることは32ビット版のExcelについての話です。(バージョンは2010)
(64bit版のExcelはDAOが使えなかった気がするので、「Excel.Application」を使う方法か、ADOを使う方法になると思います。)
また、読み込みたい先のファイルが、今の自パソコン内にあっても、ファイルサーバにあっても、どちらの場合もです。(「ファイルサーバにある」ということは、「¥¥サーバ名¥共有フォルダ名」、といったUNCパスを使う場合のことを指します。DAOでは、xls拡張子だけになってしまい、xlsm拡張子はダメですが、UNCパスのファイルも読みに行けます。)
DAOやADOで「閉じたExcelファイル」の中身を読みに行くと、「外部テーブルのフォーマットが正しくありません」というエラーに出くわすことがあります。
これは特に、「DAOにて」、xlsm拡張子のファイルを読み行くときに多いみたいです。
僕の環境では、読み込み先のファイルがどこにあろうが、ローカルだろうがサーバ上だろうが、その拡張子がxlsxやxlsmだと、DAOで読みに行くと必ずこのエラーになりました。
「SrcXLFName = "d:\tes1.xls" 」の内容を、今開いている自ファイルのフルパスに書き換えて、自ファイルを読みにきたときだけはもちろん読めるのですが・・・。
自ファイルじゃなくて、別ファイルで・かつ・閉じているものを読みに行くと、xlsxやxlsmの拡張子だと必ずこのエラーになりました。
そんなとき、このエラーを出させない方法は以下の通りです。
(01)読みに行くファイルをxls拡張子に作り替える
もし読みに行くファイルを「xls拡張子に作り替えても問題が無い」なら、xls拡張子のファイルに作り替えて読みに行きます。相手がxls拡張子なら、xlsmファイルなどから読みにってもエラーになりませんので。
基本的にプログラムコードは(読みに行くファイルの名前(拡張子部分のみ)をxlsmからxlsに変える以外は、他は一切変える必要はありません。エラーは出なくなります。
例えば事情があって、バージョン2003の版などとプログラムコードを一緒にしておきたい場合などはそうします。
あと、後述(03)のADOで速度が出ないとき、もし、この方法でもよければ、この方法でやります。
DAOのほうが少しだけ速いっぽい感じがするので・・・。
拡張子をどうしもてxlsmやxlsxなどにしたいなら、(02)、(03)を検討します。
(02)読みに行くファイルを開いておく
もし読みに行くファイルを、先に開いていてもよければ、読み取る先のファイルを「非表示モード」などであらかじめ開いておきます。(サーバ上に在るファイルなら、サーバ側で開きっぱなしにしてあればOKです。その場合は、その目的のファイルを開くためのプログラムを追記する必要はありません。クライアント側からそのファイルを開きたい場合は開くプログラムを追加します。
ただまあ、それだと「Excel.Application」で処理するのと変わらない感じになる場合もあると思いますので、sの場合は、「Excel.Application」を使ってもよいと思います。
SQLをどうしても使いたいときなどに限っては、DAOにて、この方法でやるとよいかもしれません。
なお、「中身を読みに行きたいファイルはどうしても閉じておかないとまずい」ということなら、(01)か(03)のやり方になるのではないかと思います。(他の良い方法があればそちらで。)
(03)DAOをやめてADOでやる
DAOでやることに固執しなくてもいいなら、ADOでのプログラムコードに書き換えます。VBE画面での参照設定も「ADO 2.8」などの設定を追加します。
ADOの動作速度で問題ないなら、こちらを使うほうがすっきりする場合が多いです。
DAOの時にもしSQL文を使っていたなら、それもまんまで移植というか、流用できたかと思います。(間違ってたらすみません!!!)
詳しくは別の記事を書きますね。
※書きました!
↓こちらです。
『ADO:Excel2010で、開かれていない閉じたままのブック・Excel(xls)データをできるだけ速く読み込む方法ーその2(ADOにて)』
https://euc-access-excel-db.com/tips/ct07_se/ct075010_ac2ktips/readby-ado-01
※その他参考記事
DAO、ADO、Microsoft Query、では「読み込み」だけでなく、「閉じたまま」の「複雑条件集計」や「書き込み」もできます。
ただし、Excelの場合はなんと「削除」ができないので対応策が必要です。
対応策はこちら。『できないこと』。(←その他の「できないこと」も書いてあります。なお、「あえて削除しない」ことも結構あります。不正対策や整合性維持のためです。基本、削除フラグを立てるだけでも対応できます。もちろん、読み込みだけに使っても構いません。)
Excel2010で、『 開かれていない閉じたままのブック・Excel(xlsやxlsx・xlsm)ファイルのデータ 』を読み込むだけでなく、書き込む方法の一覧表(DAOやADOにて)