スキーマの処理
データ ソースによっては、データ型と列名に関する情報が明示的に指定される場合と指定されない場合があります。 OData REST API は通常、$metadata 定義 を使用してこれを処理します。Power Query メソッドOData.Feedは、この情報の解析と、OData ソース から返されたデータへのその適用を自動的に処理します。
多くの REST API には、プログラムによってスキーマを特定する方法がありません。 このような場合は、コネクタ内にスキーマ定義を含める必要があります。
単純なハードコーディング アプローチ
最も簡単な方法は、コネクタにスキーマ定義をハードコーディングする方法です。 ほとんどのユース ケースでは、これで十分です。
全体として、コネクタによって返されるデータにスキーマを適用すると、次のような複数の利点があります。
- 正しいデータ型を設定する。
- エンド ユーザーに表示する必要のない列 (内部 ID や状態情報など) を削除する。
- 応答に欠落している可能性がある列を追加して、データの各ページを確実に同じ形にする (REST API は、一般に、フィールドを完全に省略して null に設定する必要があることを示します)。
Table.Schema を使用して既存のスキーマを表示する
TripPin OData サンプル サービスから単純なテーブルを返す次のコードについて考えてみましょう。
let
url = "https://services.odata.org/TripPinWebApiService/Airlines",
source = Json.Document(Web.Contents(url))[value],
asTable = Table.FromRecords(source)
in
asTable
注意
TripPin は OData ソースであるため、実際には、OData.Feed 関数の自動スキーマ処理を使用する方が理にかなっています。 この例では、ソースを一般的な REST API として扱い、Web.Contents を使用してスキーマを手動でハードコーディングする手法を示します。
次のテーブルは結果です。

便利な Table.Schema 関数を使用して、列のデータ型を確認できます。
let
url = "https://services.odata.org/TripPinWebApiService/Airlines",
source = Json.Document(Web.Contents(url))[value],
asTable = Table.FromRecords(source)
in
Table.Schema(asTable)

AirlineCode と Name は共に any 型です。 Table.Schema により、テーブル内の列に関する多くのメタデータが返されます。たとえば、名前、役職、型情報、多くの詳細プロパティ (Precision、Scale、MaxLength など) などがあります。 この時点では、指定の型 (TypeName)、プリミティブ型 (Kind)、列の値が null (IsNullable) であるかどうかのみを考慮する必要があります。
簡単なスキーマ テーブルの定義
このスキーマ テーブルは 2 つの列で構成されます。
| 列 | 詳細 |
|---|---|
| 名前 | 列の名前。 これは、サービスによって返される結果の名前と一致する必要があります。 |
| 型 | 設定する M データ型。 これは、プリミティブ型 (text、number、datetime など)、または指定の型 (Int64.Type、urrency.Type など) のいずれかを指定できます。 |
Airlines テーブルのハードコーディングされたスキーマ テーブルでは、その AirlineCode および Name 列を text に設定し、次のようになります。
Airlines = #table({"Name", "Type"}, {
{"AirlineCode", type text},
{"Name", type text}
})
他のエンドポイントのいくつかを検討するときは、次のスキーマ テーブルを検討してください。
Airports テーブルには、保持する 4 つのフィールド (record 型のものを含む) があります。
Airports = #table({"Name", "Type"}, {
{"IcaoCode", type text},
{"Name", type text},
{"IataCode", type text},
{"Location", type record}
})
People テーブルには 7 つのフィールドがあり、これには list(Emails)、AddressInfo、null を許容する 列 (Gender)、指定の型 を持つ列 (Concurrency) が含まれます。
People = #table({"Name", "Type"}, {
{"UserName", type text},
{"FirstName", type text},
{"LastName", type text},
{"Emails", type list},
{"AddressInfo", type list},
{"Gender", type nullable text},
{"Concurrency", Int64.Type}
})
これらのテーブルはすべて、1 つのマスター スキーマ テーブル SchemaTable に含めることができます。
SchemaTable = #table({"Entity", "SchemaTable"}, {
{"Airlines", Airlines},
{"Airports", Airports},
{"People", People}
})

SchemaTransformTable ヘルパー関数
次に示す SchemaTransformTable ヘルパー関数は、データにスキーマを適用するために使用されます。 使用できるパラメーターは次のとおりです。
| パラメーター | Type | 説明 |
|---|---|---|
| 席 | 席 | スキーマの適用対象であるデータのテーブル。 |
| schema | 席 | 型 type table [Name = text, Type = type] を持つ列の情報の読み取り元スキーマ テーブル。 |
| enforceSchema | number | (省略可能) 関数の動作を制御する列挙型。 既定値 ( EnforceSchema.Strict = 1) を使用すると、欠落している列を追加し、余分な列を削除することによって、出力テーブルと提供されたスキーマ テーブルとの一致が保証されます。 EnforceSchema.IgnoreExtraColumns = 2 オプションを使用すると、結果に含まれる余分な列を保持できます。 EnforceSchema.IgnoreMissingColumns = 3 を使用すると、欠落している列と余分な列の両方が無視されます。 |
この関数のロジックは次のようになります。
- ソース テーブルに欠落している列があるかどうかを確認します。
- 余分な列があるかどうかを確認します。
- 構造化列 (
list、record、table型のもの) と、型anyに設定されている列を無視します。 Table.TransformColumnTypesを使用して、各列の型を設定します。- スキーマ テーブルに表示される順序に基づいて列の順序を変更します。
Value.ReplaceTypeを使用してテーブル自体に型を設定します。
注意
テーブルの型を設定する最後の手順によって、クエリ エディターで結果を表示するときに、Power Query UI で型情報を推論する必要がなくなります。この場合、API が二重に呼び出される可能性があります。
まとめ
完全な拡張機能のより大きなコンテキストでは、API からテーブルが返された場合にスキーマ処理が実行されます。 通常、この機能はページング関数の最下位レベル (存在する場合) で実行されます。エンティティ情報はナビゲーション テーブルから渡されます。
ページング テーブルとナビゲーション テーブルの実装の多くがコンテキスト固有であるため、ハードコーディングされたスキーマ処理メカニズムを実装する完全な例はここでは示されません。 この TripPin 例は、エンド ツー エンド ソリューションがどのようになるかを示しています。
高度なアプローチ
上記で説明したハードコーディングされた実装は、単純な JSON の応答に対してスキーマの一貫性を確保する優れたジョブですが、応答の最初のレベルの解析に限定されます。 深く入れ子になったデータ セットには、M 型を利用する次のアプローチが有効です。
ここで、言語仕様の M 言語の型について簡単に思い出してみましょう。
"型値" はその他の値を "分類する" 値です。 型で分類される値は、その型に "準拠する" とされます。 M 型システムは、次の種類の型で構成されています。
- プリミティブ型。これはプリミティブ値 (
binary、date、datetime、datetimezone、duration、list、logical、null、number、record、text、time、type) を分類します。これにはさまざまな抽象型 (function、table、any、none) も含まれます。- レコード型。これはフィールドの名前や値の型に基づいてレコード値を分類します。
- リスト型。これは単一項目の基本型を利用して一覧を分類します。
- 関数型。これはそのパラメーターの型に基づいて関数値を分類し、値を返します。
- テーブル型。これは列の名前、列の型、キーに基づいてテーブル値を分類します。
- null 許容型。これは、基本型によって分類されるすべての値に加え、値 null を分類します。
- タイプ型。これは型である値を分類します。
取得した未加工の JSON 出力を使用して (および/またはサービスの $metadata 内の定義を参照して)、OData 複合型を表す次のレコード型を定義できます。
LocationType = type [
Address = text,
City = CityType,
Loc = LocType
];
CityType = type [
CountryRegion = text,
Name = text,
Region = text
];
LocType = type [
#"type" = text,
coordinates = {number},
crs = CrsType
];
CrsType = type [
#"type" = text,
properties = record
];
その構造化された列を表現するのに LocationType によって CityType と LocType が参照されていることに注意してください。
テーブルとして表す上位レベルのエンティティの場合は、テーブル型 を定義します。
AirlinesType = type table [
AirlineCode = text,
Name = text
];
AirportsType = type table [
Name = text,
IataCode = text,
Location = LocationType
];
PeopleType = type table [
UserName = text,
FirstName = text,
LastName = text,
Emails = {text},
AddressInfo = {nullable LocationType},
Gender = nullable text,
Concurrency Int64.Type
];
その後、SchemaTable 変数 (エンティティから型へのマッピングの検索テーブルとして使用できます) を更新して、次の新しい型定義を使用できます。
SchemaTable = #table({"Entity", "Type"}, {
{"Airlines", AirlinesType},
{"Airports", AirportsType},
{"People", PeopleType}
});
前の演習で SchemaTransformTable を使用したのと同様に、共通の関数 (Table.ChangeType) を使用して、データにスキーマを適用できます。 SchemaTransformTable とは異なり、Table.ChangeType は、実際の M テーブル型を引数として受け取り、入れ子になったすべての型にスキーマを 再帰的 に適用します。 そのシグネチャ:
Table.ChangeType = (table, tableType as type) as nullable table => ...
注意
柔軟性を高めるために、この関数は、レコードのリスト (JSON ドキュメントでテーブルを表す方法) だけでなく、テーブルにも使用できます。
次に、コネクタ コードを更新して、schema パラメーターを table から type に更新し、Table.ChangeType の呼び出しを追加する必要があります。 繰り返しになりますが、そうするための詳細は実装固有であるため、ここでは詳細に説明しません。 この拡張された TripPin コネクタの例は、スキーマを処理するためのこのより高度なアプローチを実装するエンド ツー エンド ソリューションを示しています。