今日のトリビア PowershellでMicrosoft Access(2019相当以降)にOLEDBで接続するCSVはUTF-8でエンコードしないほうがよい

# 確かにUTF-8が標準となっているが。。。
## 原因はADODBらしい
[アクセス2019で、型が一致しませんとエラー表示され、動かなくなったVBA](https://social.msdn.microsoft.com/Forums/ja-JP/5493320b-8ec2-49d5-aa38-957128068250/1245012463124751247320191239112289224111236419968332681237512414?forum=vbajp)

これも広い意味で似ているが、不安定な再現手順と対処法をみていく。

## 環境
- Powershell 5.1 ISE上
- Windows 10くらい
- Access 2019くらい32Bitくらい、これ以前のバージョンでは起きないらしい。

## 再現手順等
### 目的
 - Powershellを使用する。
 - ディレクトリ名、ファイル名に機種依存文字などANSIでは文字化けする文字を使用されている。そういうサーバーのファイルリストを作り、Accessのデータベースを作成する。
 - 既存のファイルがあれば削除し、accdbファイルを新規作成する。csvファイルも同様。
 - Schema.iniはANSI形式で予め作成する。ただし大量にデータがあるので`MaxScanRows=1`と1行しか読まない。
 - SQLでテーブルを作る.Adodb.Connectionから実行する。
 - このとき、ディレクトリ名、フルネームのデータ型はMEMO WITH COMP ファイル名はChar
### 再現できるかもしれない手順

 1. Get-Childitemでファイルの一覧を作る。ただし、ディレクトリ、フォルダに記号、機種依存文字、空白が含まれる。
 2. これをUTF-8のタブ区切りCSVに出力。
 3. Microsoft Access起動
 4. データベース accdを作成保存
 5. コード`$dao = new-object -comobject DAO.DBEngine.120;$aCN2=New-Object -ComObject Adodb Connection;`
 6. 接続プロバイダーはMicrosoft.Ace.12.0  
 7. 再びAccessでaccdbファイルを開く。
 8. `$aCN.execute($sSQL)`でCreate Table このときディレクトリName、フルパスファイル名をMEMO WiTH COMP、にする。
  `$aCN2=New-Object -ComObject Adodb Connection` 
 9. 接続プロバイダーはMicrosoft.Ace.12.0
 10. csvに接続
 11. 双方のレコードセットを開く
 12. `Do...Loop`で``Try{`フィールドにデータを代入する`}Catch{}`。ここでエラーが起きたら変数を開放してエラーを表示して終了するようにしておく。
 13. 途中で型が一致しませんとエラー表示されて終了することがある。

### 起きている現象
確かこれから入力するレコードセットがadLongVarWCharとなっており、双方のフィールドのデータ型を見るとadLongVarWChar adLongVarCharというように微妙に違っている。


### 原因
 - 強く疑われるもの
    - SQLで　`Create TABLE Tablename(MEMO WITH COMP);'で作るとデータがない状態ではNullで終わっている。
    - Get-ChildItemでCSVを作るとAttribetes=Directoryの場合ディレクトリ名は出力されない。このため空白のフィールドが存在する。
    - さらに、ファイルやフォルダ名に機種依存文字が存在するが、1行しか読み込ませていないため、そこで間違った判断をしている。
    - 空白の方は普通のメモ型だが、With　COMPはNullで終わるようになる。
 - 他にかんがえられるもの
    + もしくはPowershellの内部はUnicodeのため、空白を出力するときUTF-8になっていない。

### 対応方法

 1. 上記の手順の1で作成するCSVファイルをUnicodeにする。
 2. schema.iniのMaxRowsを設定しない。ただし遅くなる。（これは未実験）
 3. これで動かない場合はWITH COMPをやめる。
 4. それでも動かないときはDAOを読み込まない。（未実験だが、紹介したanswers.Microsoftの解決方法に従うとこうなる）
 5. さらに動かないときはAccessを終了させず、OpenRecordSetでCSVを読み込ませる側を開く（たしかこれもエラー）

このうち一番有効なのは1と3を両方やることだった。そこから3をやめて1だけにすると安定した。
ミスになるレコードの該当フィールドのデータ型を調べると201、203のようにデータ型が微妙に違っていた。通常はこれでも入力されると思うが。。。
とにかくAccessに読み込ませるときはUnicode（その正体はUTF-16)にすると安定する。
Powershellを使ったからなのかもしれない。がVBAでも発生している。

### MicrosoftのQAは
MicrosoftのQAの魔界の仮面弁士氏の回答としては

> - Access を開く環境の差異によって、参照設定の不整合が生じている可能性です。このような場合、本来の障害箇所とは別の箇所でコンパイルエラーとなることがあります。参照設定を可能な限り外してからファイルを開きなおし、改めて、参照設定をやりなおしてみると回復するかもしれません。ActiveX コントロールや追加の参照設定が含まれている場合は、32bit/64bit の違いが無いかも確認しておきましょう。
> - それ以外では、Access ファイルの部分的な破損の可能性もありえます。この場合、完全に修復できる保証はありませんが、特定のフォームだけが失敗するのなら、新しい Access ファイルを用意して、問題となっているフォームやモジュールを一つずつ移植してみることで改善されるかもしれません。
> - あるいは可能性は低いですが、ファイル形式変換時の照合順序指定に問題が生じていたという可能性もあるかもしれません。たとえば長音記号「ー」が問題となることもあるため、念のため、SQL 文中のテーブル名やフィールド名を角括弧で囲ってみてください

ここでも変数`BBB`が数値からテキストになっていると思われる。
しかし、回答者はよくわからないまま外してしまったが魔界の仮面弁士氏のいわれるようにここでは参照設定にエラーがあったのかもしれない.もちろんバージョンが上がればよく起きるためである。
しかし、エラーはなかったとすればどうか

#### なかったものと仮定すると
`BBB = rs1.RecordCount`(ここの部分が青く反転）
ということだが
##### A.変数の宣言、キャスト（型変換）
`BBB`に入れるのが数値だけど文字列型にしたいようだ。ならば文字列型を強制する。さらに全角ならStrConvをかけてもよい。
`Dim　BBB As Strig`として
`BBB = CStr(rs1.RecordCount)`

##### B.フィールドの設定
入るフィールドのデータ型にWITH COMP属性をつけているなら外す。

こうやってもエラーなのかが知りたかった。
これでエラーになる場合はフィールドのデータ型が微妙に違っている可能性があるためである。

# まとめ 今日のトリビアはエラーが起きたときだけ
　このような対処方法を行わなくても、ファイル、フォルダ名がANSIで表せるような場合は、UTF-8でも一切問題は起きないかもしれない。当方もテスト段階ではバグが起きなかったためである。日頃からファイル名やディレクトリ名に注意すれば防げる。
　VBAの場合は指摘されている参照設定、accdbファイルの再作成を試してみるべきだろう。
　コードの再検討についても当然である。
　しかし、それでも読み込み時にエラーが起きるときのであればUnicodeに変えるといった試みをすると良いだろう。ただ、これを知らないと相当揉めると思われる。いくらやってもスクリプトは完璧でエラーなどないからだ。 
　なお、この現象は、現在Access2019相当以降で`adodb.connection`のものしかみあたらない。バグの可能性が考えられるものの。再現が難しいことから、当面はバグとは認識、確認、確定はされないだろう。しかし、双方ともを使用している。