2つのテーブルを縦に結合する時は、列の名称とデータ型が揃っている必要がありますよね。データ型については、結合した時に揃っていない列が any型になるだけですみますが、列名が異なると別々の列として結合されてしまいます。
「テーブル1」・「テーブル2」の2つのクエリがあるとして、「テーブル2」に「テーブル1」の列名とデータ型の両方を設定して揃えるにはどうすればいいでしょうか。
まず最初に考えたのは、Table.ToColumns・Table.FromColumns関数を使う方法です。「テーブル1」のテーブルのタイプは、Value.Type関数を使えば抜き出せます。
列名と型の変更(微妙に失敗の例です)= Table.FromColumns(Table.ToColumns(ソース), Value.Type(テーブル1))
「なんだ簡単簡単♪」と思ったのですが、そんなに甘くはありませんでした。よく見ると「日付」列には date型を設定したにも関わらず、値は datetime型のままです。
そうです。このやり方だと、列のデータ型は変更できますが、値そのものの型は変換してくれないのです(このやり方自体は他でも使えるので有用ですが)。
メニューの「データ型」や「データ型の検出」ボタンを使った時は、Table.TransformColumnTypes関数が入るのですが、この関数は列のデータ型を指定するだけではなく、その後に値の型の変換も一緒にやってくれているのです。
なんとかならないかと色々試したのですが、どうやら Table.FromColumns関数や #tableを使う方法では、値の型の変換(.From)はやってくれないようです。面倒ですが「列名の変更」と「型の変更」は分けるべきだという結論に達しました。
列名の変更についてはなんてことありません。さっき書いた「列名と型の変更」を実行してもいいですし、Table.ColumnNames関数を使ってもいいです。
列名の変更= Table.RenameColumns( テーブル2, List.Zip({Table.ColumnNames(テーブル2), Table.ColumnNames(テーブル1)}) )
これでおしまい。しかし型の変更については一筋縄ではいきません。Table.TransformColumnTypes関数の第二引数には「型のリスト(タイプリスト/type list)」を入れる必要があります。これをテーブルから作らなければいけないのです。
因みに型のリストというのはこういうやつのことです。
型リスト(タイプリスト){ {"日付", type date}, {"氏名", type text}, {"数値", Int64.Type} }
これをどうやってテーブルから抜き出すか、色々やってみました。
Value.Type・Type.TableRow・Type.RecordFields・Record.FieldNames・Record.FieldValuesを使って、そこから「Name」と「Type」を抜き出して……いや、色々使えばもちろん抜き出せるんですが、とにかく面倒です。もっと簡単に抜き出せないもんでしょうか。
そこで別の方向から考えてみることにしました。Value.Typeは使わずに、直接テーブル情報から typeを作れないか考えることにしました。
Table.Schema関数の「Kind」列にはデータ型の文字列が表示されますから、これを type型に変換できればいいはずです。
そこでようやく、Expression.Evaluate関数を使うことに思い至りました(長かった……)。
つまりこうです。テーブル2は「列名の変更」ステップで、列名をテーブル1と同じにしているものとします。
型の変更(+値の型変換)= Table.TransformColumnTypes( 列名の変更, Table.AddColumn( Table.Schema(テーブル1), "x", each {[Name], Expression.Evaluate("type "& [Kind])} )[x] )
若干面倒ですが、こうすればテーブル1から列のデータ型を抜き出してテーブル2に適用しつつ、値の変換も一緒に実行できます。