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

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

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

Re[5]: AccessVBAで主キーのないテーブルをインポート


(過去ログ 173 を表示中)

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

■99633 / inTopicNo.1)  AccessVBAで主キーのないテーブルをインポート
  
□投稿者/ たかし (1回)-(2022/05/07(Sat) 20:17:55)

分類:[Windows 全般] 

AccessVBAで主キーのないテーブルをインポート

いつもお世話になっています。たかしと申します。
クライアントから、Accessで帳票のみを作るという仕事依頼を受けました。元データはExcel形式でエクスポートされたもので、それをAccessにインポートして帳票作成する仕様です。何でも某事務機製造会社の販売管理ソフトを使っているとのことで、エクスポートデータをExcel形式で送ってきたのですが、なんとそのテーブルには主キーがないのです。そんな馬鹿な、と思ってよくよく見てみると、そのデータには [処理連番] [行] [明細区分]という3つのフィールドがあり、その3つを繋げると一意のコードが作れることが分かりました。

式1: [処理連番] & [行] & [明細区分]

そこで伺いたいのですが、インポートの際にVBAで主キーを設定するということができるものでしょうか?インポートウィザードを使えば自動的に主キーが作成されるのは分かっているのですが、それをVBAで実装することはできるのでしょうか?どなたか教えてください。たかし

DoCmd.TransferSpreadsheet acImport, 10, "Sheet1", パス & "\" & "1_sampledata" & ".xlsx", True, ""

引用返信 編集キー/
■99635 / inTopicNo.2)  Re[1]: AccessVBAで主キーのないテーブルをインポート
□投稿者/ 魔界の仮面弁士 (3344回)-(2022/05/07(Sat) 22:31:38)
No99633 (たかし さん) に返信
> そこで伺いたいのですが、インポートの際にVBAで主キーを設定するということができるものでしょうか?

主キー無しでインポートすることはできていますか?
それができるなら、インポート後に複合主キーを割り当てればよいと思います。

CurrentDb.Execute "CREATE UNIQUE INDEX [PrimaryKey] ON [Sheet1] ([処理連番] ASC, [行] ASC, [明細区分] ASC) WITH PRIMARY"

https://docs.microsoft.com/ja-jp/office/client-developer/access/desktop-database-reference/create-index-statement-microsoft-access-sql


> DoCmd.TransferSpreadsheet acImport, 10, "Sheet1", パス & "\" & "1_sampledata" & ".xlsx", True, ""
10 というのは、acSpreadsheetTypeExcel12Xml のことでしょうか。

第一引数は定数(列挙型)を指定しているのに、
第二引数には何故マジックナンバーを指定しているのでしょうか。

また、パス名の指定で & 演算子が多用されている点も気になりました。
「パス & "\" & "1_sampledata" & ".xlsx"」ではなく
「パス & "\1_sampledata.xlsx"」で良いのでは…?



引用返信 編集キー/
■99637 / inTopicNo.3)  Re[2]: AccessVBAで主キーのないテーブルをインポート
□投稿者/ たかし (2回)-(2022/05/08(Sun) 09:32:48)

> 主キー無しでインポートすることはできていますか?

当然できません。メッセージが出ます。

>>DoCmd.TransferSpreadsheet acImport, 10, "Sheet1", パス & "\" & "1_sampledata" & ".xlsx", True, ""
> 10 というのは、acSpreadsheetTypeExcel12Xml のことでしょうか。

そうです。

> 第一引数は定数(列挙型)を指定しているのに、
> 第二引数には何故マジックナンバーを指定しているのでしょうか。

すみません、どの箇所のことを仰っているのでしょうか?つまり
DoCmd.TransferSpreadsheet acImport, acSpreadsheetTypeExcel12Xml, "Sheet1", パス & "\" & "1_sampledata" & ".xlsx", True, ""で良いですか?

> また、パス名の指定で & 演算子が多用されている点も気になりました。
> 「パス & "\" & "1_sampledata" & ".xlsx"」ではなく
> 「パス & "\1_sampledata.xlsx"」で良いのでは…?

なるほどそうですね。
本題に戻ってインポートの際に自動で採番することは不可能でしょうか?
よろしくお願いします。



引用返信 編集キー/
■99640 / inTopicNo.4)  Re[3]: AccessVBAで主キーのないテーブルをインポート
□投稿者/ 魔界の仮面弁士 (3347回)-(2022/05/08(Sun) 12:18:16)
No99637 (たかし さん) に返信
>>主キー無しでインポートすることはできていますか?
> 当然できません。メッセージが出ます。
いやそもそもどんなメッセージが表示されているのかも
こちらとしてはわかりませんし、そこで「当然」と言われましても…。

手動でインポートを行った場合や、DoCmd.RunCommand acCmdImportAttachExcel した場合、
主キーの指定を 自動設定 / 単一フィールド指定 / 主キー無し を選べますよね?

なので、主キー無しでインポートすることはできるものだと思っているのですが、
たかしさんがお使いの Access ではそうではないのでしょうか。

実際自分は、主キーが無くても DoCmd.TransferSpreadsheet acImport,…… で
新規テーブルに取り込むことができています。

あるいはすでに主キーもしくは UNIQUE INDEX があるテーブルに対して
データを追加する場合であったとしても、やはり acImport は行えるはずです。

もしも既存テーブルへのインポートの場合、キー違反や桁数超過などで取り込めないデータが
含まれていた場合には、競合するデータをスキップして、それ以外のデータのインポートを継続するか、
それともインポートそのものを取りやめるかという確認メッセージが現れます。
メッセージボックスで継続続行を指示すれば競合していないデータのみを取り込めますし、
DoCmd.SetWarnings にて、警告画面の On/Off も行えます。

あるいは acImport を行う代わりに、異種 DB 間クエリを用いて
INSERT INTO あるいは SELECT INTO するという選択肢もあるでしょう。
この異種 DB 間クエリは、エクスポート目的や複数の Access ファイルのマージにも使える手法です。

CurrentDb.Execute "INSERT INTO [Sheet1] SELECT * FROM [Excel 12.0 XML;HDR=YES;IMEX=1;Database=C:\test\Book1.xlsx].[Sheet1$]"

CurrentDb.Execute "SELECT * INTO [Sheet2] FROM [Excel 12.0 XML;HDR=YES;IMEX=1;Database=C:\test\Book1.xlsx].[Sheet1$]"

https://docs.microsoft.com/ja-jp/office/client-developer/access/desktop-database-reference/select-into-statement-microsoft-access-sql

あるいは 「IN 句」でも同様のことができます。(IN 演算子とは異なります)
https://docs.microsoft.com/ja-jp/office/vba/access/concepts/miscellaneous/in-clause-microsoft-access-sql


この他、手動インポートで、保存済みインポート/エクスポートが行われたことがあり、
それを再実行したいという状況であれば、 RunSavedImportExport メソッドを使うこともできます。
今回は関係ないでしょうけれども。


>> 第一引数は定数(列挙型)を指定しているのに、
>> 第二引数には何故マジックナンバーを指定しているのでしょうか。
> すみません、どの箇所のことを仰っているのでしょうか?つまり
> DoCmd.TransferSpreadsheet acImport, acSpreadsheetTypeExcel12Xml, "Sheet1", パス & "\" & "1_sampledata" & ".xlsx", True, ""
> で良いですか?
これがたとえば、参照設定できない環境(VBScript など)あるいは参照設定していない環境だと、
「acImport, acSpreadsheetTypeExcel12Xml」の代わりに「0, 10」と書くケースもあるでしょう。

しかし今回は Access VBA のようですし、しかも「acImport, 10」という混在した不自然な指定方法のため、
何故、一部だけをあえてマジックナンバー表記にしたのだろう…と疑問に思い、理由をお尋ねした次第です。


> 本題に戻ってインポートの際に自動で採番することは不可能でしょうか?
これは、自動採番される主キー列を作りたいということでしょうか?

それとも、Excel 側に存在していなかったフィールドを
>> 式1: [処理連番] & [行] & [明細区分]
のような式で追加したいということですか?

後者の「Excel 側に無いフィールドを式列として追加したい」という話であれば、
今回述べた異種DB間クエリの手法を使えるでしょう。

前者の自動採番列を求める場合は、CREATE TABLE ステートメントでオートナンバー列を
あらかじめ用意しておき、そこに向かって acImport してやればよいでしょう。

CurrentDb.Execute "CREATE TABLE [Table1] ([ID] AUTOINCREMENT PRIMARY KEY, [処理連番] INTEGER NOT NULL, [行] INTEGER NOT NULL, [明細区分] TEXT(10) NOT NULL )"

あるいはオートナンバー列が無い状態で取り込んでから、あとから ALTER TABLE で列を追加することもできますよね。
引用返信 編集キー/
■99641 / inTopicNo.5)  Re[4]: AccessVBAで主キーのないテーブルをインポート
□投稿者/ たかし (3回)-(2022/05/08(Sun) 13:34:01)

> 実際自分は、主キーが無くても DoCmd.TransferSpreadsheet acImport,…… で
> 新規テーブルに取り込むことができています。
>
> あるいはすでに主キーもしくは UNIQUE INDEX があるテーブルに対して
> データを追加する場合であったとしても、やはり acImport は行えるはずです。
>
> もしも既存テーブルへのインポートの場合、キー違反や桁数超過などで取り込めないデータが
> 含まれていた場合には、競合するデータをスキップして、それ以外のデータのインポートを継続するか、
> それともインポートそのものを取りやめるかという確認メッセージが現れます。
> メッセージボックスで継続続行を指示すれば競合していないデータのみを取り込めますし、
> DoCmd.SetWarnings にて、警告画面の On/Off も行えます。

少し説明が不足でした。やってみたことは次のとおりです。
AccessテーブルにIDフィールドを作り、ExcelファイルにもIDフィールドを作りました。
そのIDフィールドを空欄にしてVBAでインポートを行うと、キー違反のメッセージが出てインポートされませんでした。
これは当然ですよね?

そこでおっしゃるようにどちらのファイルからもIDフィールドそのものを削除して見たらインポートできました。
「主キーがないとインポートできない」は、私の判断ミスでした、すいません。


> あるいは acImport を行う代わりに、異種 DB 間クエリを用いて
> INSERT INTO あるいは SELECT INTO するという選択肢もあるでしょう。
> この異種 DB 間クエリは、エクスポート目的や複数の Access ファイルのマージにも使える手法です。
>
> CurrentDb.Execute "INSERT INTO [Sheet1] SELECT * FROM [Excel 12.0 XML;HDR=YES;IMEX=1;Database=C:\test\Book1.xlsx].[Sheet1$]"
>
> CurrentDb.Execute "SELECT * INTO [Sheet2] FROM [Excel 12.0 XML;HDR=YES;IMEX=1;Database=C:\test\Book1.xlsx].[Sheet1$]"
>
> https://docs.microsoft.com/ja-jp/office/client-developer/access/desktop-database-reference/select-into-statement-microsoft-access-sql
>
> あるいは 「IN 句」でも同様のことができます。(IN 演算子とは異なります)
> https://docs.microsoft.com/ja-jp/office/vba/access/concepts/miscellaneous/in-clause-microsoft-access-sql

これについては少し勉強してみたいと思います。


> この他、手動インポートで、保存済みインポート/エクスポートが行われたことがあり、
> それを再実行したいという状況であれば、 RunSavedImportExport メソッドを使うこともできます。
> 今回は関係ないでしょうけれども。

手動ではなくVBAを考えています。


> >> 第一引数は定数(列挙型)を指定しているのに、
> >> 第二引数には何故マジックナンバーを指定しているのでしょうか。
>>すみません、どの箇所のことを仰っているのでしょうか?つまり
>>DoCmd.TransferSpreadsheet acImport, acSpreadsheetTypeExcel12Xml, "Sheet1", パス & "\" & "1_sampledata" & ".xlsx", True, ""
>>で良いですか?
> これがたとえば、参照設定できない環境(VBScript など)あるいは参照設定していない環境だと、
> 「acImport, acSpreadsheetTypeExcel12Xml」の代わりに「0, 10」と書くケースもあるでしょう。
>
> しかし今回は Access VBA のようですし、しかも「acImport, 10」という混在した不自然な指定方法のため、
> 何故、一部だけをあえてマジックナンバー表記にしたのだろう…と疑問に思い、理由をお尋ねした次第です。

分かりました。

>>本題に戻ってインポートの際に自動で採番することは不可能でしょうか?
> これは、自動採番される主キー列を作りたいということでしょうか?

そうです。それが目的です。

> それとも、Excel 側に存在していなかったフィールドを
> >> 式1: [処理連番] & [行] & [明細区分]
> のような式で追加したいということですか?

それでも良いのです。

> 後者の「Excel 側に無いフィールドを式列として追加したい」という話であれば、
> 今回述べた異種DB間クエリの手法を使えるでしょう。
>
> 前者の自動採番列を求める場合は、CREATE TABLE ステートメントでオートナンバー列を
> あらかじめ用意しておき、そこに向かって acImport してやればよいでしょう。
>
> CurrentDb.Execute "CREATE TABLE [Table1] ([ID] AUTOINCREMENT PRIMARY KEY, [処理連番] INTEGER NOT NULL, [行] INTEGER NOT NULL, [明細区分] TEXT(10) NOT NULL )"
>
> あるいはオートナンバー列が無い状態で取り込んでから、あとから ALTER TABLE で列を追加することもできますよね。

分かりました、検討してみます。また分からないことが出てきたらお願いしたいと思います。

引用返信 編集キー/
■99643 / inTopicNo.6)  Re[5]: AccessVBAで主キーのないテーブルをインポート
□投稿者/ 魔界の仮面弁士 (3348回)-(2022/05/08(Sun) 15:35:07)
No99641 (たかし さん) に返信
> AccessテーブルにIDフィールドを作り、ExcelファイルにもIDフィールドを作りました。
> そのIDフィールドを空欄にしてVBAでインポートを行うと、キー違反のメッセージが出てインポートされませんでした。
キー違反とは、値の競合の意味でしょうか。それとも null 挿入のエラーでしょうか。


> これは当然ですよね?
自動採番ではない列を単一主キーに設定し、かつ、インポート時にその値を指定しないというのは、
一般的な行為ではないと思いますよ…。


> そこでおっしゃるようにどちらのファイルからもIDフィールドそのものを削除して見たらインポートできました。
> 「主キーがないとインポートできない」は、私の判断ミスでした、すいません。
今回の場合は、あらかじめ Access 側にテーブルを用意しておき、
[処理連番], [行], [明細区分] の 3 列を「複合主キー」に設定しておくのが良いでしょう。

もしも、「単一主キー」な ID フィールドを自動採番として用意したいのであれば、
通常は Access 側でテーブルを作成し、その列をオートナンバー型にしておきます。
この設定はデザイン時に行えますので、VBA 側ではインポート処理のみを記述すれば十分です。
オートナンバー列であれば、そのフィールド値を指定せずにインポートしても、
「自動採番」された番号が既定値なり、そのまま取り込むことができます。

主キーを自動採番に設定せず、かつその列を指定せずに取り込もうとした場合には、
その列の「既定値」が挿入されます。既定値も設定していなければ null になってしまうわけですね。

オートナンバー列においては、「長整数型」と「レプリケーション ID型」の 2 種類があります。
特に理由が無ければ長整数型を選択しておけば良いでしょう。

オートナンバー型にすれば、キーの重複を気にする必要がなくなります。
(オートナンバーで重複を起こさせることも不可能ではないですが…)

ちなみに長整数型は、最大値を超えると次は -2147483648 になります。
http://www.ruriplus.com/msaccess/exp/exp0901.htm


あるいはデザイン時に作っておくのではなく、プログラムからオートナンバー列を作ることもできます。
今回は不要だと思いますが、一応プログラムから作る方法についても記しておきます。


まず【レプリケーション ID】型のオートナンバーでは、
CurrentProject.Connection.Execute "CREATE TABLE [Table2] ([ID] GUID PRIMARY KEY DEFAULT GenGUID(),[処理連番] INTEGER NOT NULL)
のように、データ型として「GUID」を指定します。

既定値のところに指定した「GenGUID()」はビルトイン関数です。これは上記のように ADODB から呼び出す必要があります。
DAO からの呼び出し(CodeDb.Execute メソッドや、DoCmd.RunSQL メソッド)では GenGUID() を利用できません。

ただし DefaultValue ダイナミックプロパティに指定する方法をとれば、DAO からでも指定できます。
http://accessblog.net/2005/08/how-to-create-guid-autonumber-field.html


一方、【長整数型】のオートナンバーの場合には、前回指定した AUTOINREMENT や COUNTER 、もしくは IDENTITY を指定します。
AUTOINREMENT や COUNTER は、DAO からでも ADODB からでも呼び出せます。

DoCmd.RunSQL "CREATE TABLE [Table4] ([ID] COUNTER PRIMARY KEY, [処理連番] INTEGER NOT NULL)"
CodeDb.Execute "CREATE TABLE [Table5] ([ID] COUNTER PRIMARY KEY, [処理連番] INTEGER NOT NULL)"

IDENTITY を指定する場合は、DAO ではなく ADODB から指定します。
CurrentProject.Connection.Execute "CREATE TABLE [Table6] ([ID] IDENTITY PRIMARY KEY, [処理連番] INT NOT NULL)"

さらに「IDENTITY(開始値, 増分値)」構文を使えば、自動採番の開始値と増分値を設定することもできます。
引数指定が無い場合は、共に 1 が設定されたものとして扱われます。

※100000 から 10 ずつ増加する番号
CurrentProject.Connection.Execute "CREATE TABLE [Table6] ([ID] IDENTITY(100000, 10) PRIMARY KEY, [処理連番] INT NOT NULL)"

※99999 から初めて 1 ずつ減少する番号
CurrentProject.Connection.Execute "CREATE TABLE [Table7] ([ID] IDENTITY(99999, -1) PRIMARY KEY, [処理連番] INT NOT NULL)"


>>…と疑問に思い、理由をお尋ねした次第です。
> 分かりました。
こちらとしては、意図や理由を尋ねていたわけで、
何か会話が微妙すれ違っている気もしますが、どうやら
混在させたことに、特に理由は無かったということですかね。

であればいずれかに統一しておいた方が良いでしょう。
マジックナンバーだと後から見たときに意味が読み取りにくいので、定数を使うことを推奨しておきます。


>>それとも、Excel 側に存在していなかったフィールドを
>>>> 式1: [処理連番] & [行] & [明細区分]
>>のような式で追加したいということですか?
> それでも良いのです。

……本当に?

元の Excel 側のデータが分からないの判断しにくいところですが、
単純連結の場合、たとえば
処理連番 = 12、行 = 1 の場合と
処理連番 = 1、行 = 21 の場合を
区別できなくなりませんか?


処理連番が 1 ではなく "001" などの固定長形式であれば & 連結もアリですが、
その場合、インポート時にテキスト列が数値列に誤読されないような対応も必要になります。

数値列なら、「処理連番 * 10000 + 行」などの形で連結する手法もありますが、
これだと桁数の問題も生じます。

主キー列をテキスト型にしておいて、
 [処理連番] & "@" & [行] & "@" & [明細区分]
のように、それらの列値に含まれないであろう文字を区切りにして連結するのであれば
 処理連番 = 12、行 = 1
 処理連番 = 1、行 = 21
の取り違いは無くなりますね。


ただこの場合は、先に回答した通り「複合主キー」案を推しておきます。
(後から追加する場合は No99635 、先に用意しておくなら No99640 など)
引用返信 編集キー/


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

このトピックに書きこむ

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

管理者用

- Child Tree -