はじめに
SAPシステムは、社内の基幹業務データを一元管理する強力な仕組みですが、そのデータを外部のシステムやアプリケーションと連携する場面は年々増えています。Webアプリ、モバイルアプリ、BIツール、他社のクラウドサービス――こうした外部システムがSAPのデータを読み書きするための「共通言語」として使われているのが OData(Open Data Protocol) です。
本記事では、ODataを以下の3つの軸で解説します。
- ODataとは何か — REST APIとの関係、OData固有の機能
- エンティティとエンティティセット — ODataのデータモデルの核心と、テーブルとの対応関係
- Navigation Property — エンティティ間のリレーションを表現する仕組み
なぜODataを学ぶ必要があるのか(why so):S/4HANAの標準UIであるFioriアプリはすべてODataを使ってデータを取得・更新しています。カスタムアプリの開発、外部システムとの連携、API管理――いずれの場面でもODataの理解は避けて通れません。「SAPの中のデータをSAPの外に出す」ための標準的な手段がODataであり、これを知らずにS/4HANA時代の開発・連携を語ることはできません。
ODataとは何か
一言で言うと
OData(Open Data Protocol)は、Web上でデータのCRUD操作(作成・読取・更新・削除)を標準化したプロトコルです。Microsoftが開発し、現在はOASIS標準として公開されています。
ポイントは「ただのREST APIではない」ということです。REST APIはアーキテクチャスタイル(設計方針)であり、具体的なURLの構造やクエリの書き方は開発者次第です。一方、ODataはRESTの上にURLの形式・クエリパラメータ・レスポンス形式などの「規約」を追加したものと理解できます。
REST APIとODataの関係
flowchart LR
subgraph "REST API(設計方針)"
A["HTTPメソッドで操作を表現\nGET / POST / PUT / DELETE"]
end
subgraph "OData(規約)"
B["URLの構造が統一\n/sap/opu/odata/..."] --> C["**クエリオプション**が標準化\n$filter / $select / $expand"]
C --> D["**メタデータ**が自己記述的\n**$metadata** で構造を公開"]
end
A --> Bつまり、ODataは「REST APIの一種」ですが、クライアント側が事前にAPI仕様書を読まなくても、URLのルールとクエリオプションを知っていればデータにアクセスできるという標準化がされている点が大きな違いです。
ODataが標準化しているもの
| 領域 | 内容 | 具体例 |
|---|---|---|
| URLの構造 | エンティティセット名でアクセス先を指定、キーでレコードを特定 | /SalesOrderSet('0000001234') |
| HTTPメソッドの対応 | CRUDとHTTPメソッドの対応が固定 | GET=読取、POST=作成、PUT=更新、DELETE=削除 |
| クエリオプション | フィルタ・ソート・ページング・項目選択などを$パラメータで指定 | ?$filter=Status eq 'Open'&$top=10 |
| レスポンス形式 | JSON(またはXML/Atom)で統一されたフォーマット | {"d": {"results": [...]}} |
| メタデータ | サービスのデータ構造を機械可読な形式で公開 | /$metadata でエンティティ定義を取得 |
ODataのバージョン:V2とV4
SAPの世界では、OData V2とOData V4の2つのバージョンが使われています。
| 項目 | OData V2 | OData V4 |
|---|---|---|
| SAP での位置づけ | SAP Gateway(既存の主力) | S/4HANA Cloud の標準 |
| レスポンス形式 | JSON("d" ラッパー付き) | JSON(よりシンプルな構造) |
| アクション/ファンクション | Function Import | Action / Function(より明確に分離) |
| CDS Viewとの連携 | @OData.publish: true で公開可能 | アノテーションベースで公開(RAP:RESTful ABAP Programming Model、S/4HANA時代の標準開発モデル) |
| 現在の推奨 | 既存資産として継続利用 | 新規開発はV4を推奨 |
読者への示唆(so what):現時点ではV2のサービスが圧倒的に多く、既存システムの保守・調査ではV2の知識が必須です。一方、S/4HANA Cloudでの新規開発ではV4が標準になりつつあります。両方の基本を押さえつつ、新規はV4で設計するというスタンスが実務上の最適解です。V4/RAPが推奨される背景にあるClean Coreの考え方についてはSAP Clean Core戦略とは?コアを守りながら拡張する新しい開発思想を参照してください。
ODataの主要なクエリオプション
ODataの最大の強みの一つが、クエリオプションによる柔軟なデータ取得です。クライアント側がURLパラメータを変えるだけで、取得するデータの範囲・並び順・項目を制御できます。
代表的なクエリオプション一覧
| オプション | 役割 | 使用例 |
|---|---|---|
$filter | 条件でデータを絞り込む | $filter=CompanyCode eq '1000' |
$select | 取得する項目を限定する | $select=OrderID,CustomerName,Amount |
$orderby | ソート順を指定する | $orderby=CreatedAt desc |
$top / $skip | ページングを制御する | $top=20&$skip=40(3ページ目の20件) |
$expand | 関連エンティティを一括取得する | $expand=ToItems(後述のNavigation Property) |
$count(V4)/ $inlinecount(V2) | 件数を取得する。V4では$count=true、V2では$inlinecount=allpages | $count=true(V4)/ $inlinecount=allpages(V2) |
なぜクエリオプションが重要か(why so):従来のRFC/BAPIベースの連携では、「どの条件で・どの項目を・何件取得するか」をプログラム側にハードコードする必要がありました。ODataのクエリオプションは、サービスを変更せずにクライアント側で取得条件を柔軟に変更できるという点で、開発・保守の効率を大幅に向上させます。
実際のURLの例
購買発注(Purchase Order)のODataサービスにアクセスする場合を想定します。
全件取得:
GET /sap/opu/odata/sap/API_PURCHASEORDER_PROCESS_SRV/PurchaseOrderSet
特定の発注を取得(キー指定):
GET /sap/opu/odata/sap/API_PURCHASEORDER_PROCESS_SRV/PurchaseOrderSet('4500000001')
条件付き取得(会社コード1000、作成日降順、上位10件):
GET /sap/opu/odata/sap/API_PURCHASEORDER_PROCESS_SRV/PurchaseOrderSet
?$filter=CompanyCode eq '1000'
&$orderby=CreatedAt desc
&$top=10
&$select=PurchaseOrder,CompanyCode,Supplier,CreatedAt
読者への示唆(so what):このURLの構造を見ると、ODataが「URLだけでデータアクセスの意図を完全に表現できる」ことが分かります。これはREST APIの設計原則に沿いつつも、ODataの規約があるからこそ統一的に読み書きできるのです。ABAPのSELECT文を知っている方なら、$filterはWHERE句、$selectはSELECT句、$orderbyはORDER BY句に対応すると考えると理解しやすいでしょう。SAPテーブルのデータ構造を実際に確認する方法についてはSE16 / SE16N / SE16H 完全解説|SAPテーブルブラウザの使い方と実務活用も参考にしてください。
エンティティとエンティティセット
ODataのデータモデルの核心
ODataでは、データの構造を**エンティティ(Entity)とエンティティセット(Entity Set)**という概念で表現します。
| 概念 | 意味 | データベースとの対応 |
|---|---|---|
| エンティティタイプ(Entity Type) | データの「型」定義。どんな項目(プロパティ)を持つかを定義する | テーブル定義(DDLのCREATE TABLE) |
| エンティティ(Entity) | エンティティタイプに基づく1件のデータ | テーブルの1行(レコード) |
| エンティティセット(Entity Set) | エンティティの集合。APIのアクセスポイントになる | テーブルそのもの(SELECT対象) |
| プロパティ(Property) | エンティティが持つ各項目 | テーブルの列(カラム) |
| キー(Key) | エンティティを一意に特定する項目 | テーブルの主キー |
flowchart LR
subgraph "エンティティタイプ(型定義)"
A["PurchaseOrderType\nキー: PurchaseOrder\nプロパティ: CompanyCode,\nSupplier, CreatedAt ..."]
end
subgraph "エンティティセット(集合)"
B["PurchaseOrderSet\n= PurchaseOrderTypeの集合\n= APIのアクセスポイント"]
end
subgraph "エンティティ(1件)"
C["PurchaseOrder = '4500000001'\nCompanyCode = '1000'\nSupplier = 'VENDOR_A'\nCreatedAt = '2026-04-01'"]
end
A -->|"型に基づき\n集合を公開"| B -->|"1件を\nキーで特定"| Cエンティティの設計粒度:テーブルに揃える
ODataのエンティティを設計する際に最も重要な原則の一つが、エンティティの粒度をテーブル(またはCDS View)の粒度に揃えることです。
なぜテーブル粒度に揃えるのか(why so)
SAPのデータベース設計では、業務の意味単位ごとにテーブルが分かれています。たとえば購買発注なら:
| テーブル | 粒度 | 内容 |
|---|---|---|
| EKKO | 発注ヘッダ(1発注=1行) | 発注番号、仕入先、会社コード、通貨など |
| EKPO | 発注明細(1明細=1行) | 品目、数量、単価、プラント、納入日程など |
この「ヘッダと明細が別テーブル」という構造は、SAPの伝票系データに共通するパターンです。MMモジュールの伝票構造についてはSAP MMモジュール 業務フロー完全解説|購買依頼から支払までP2Pサイクルを網羅で解説しています。
ODataのエンティティも、この粒度に合わせて設計します。
✅ 正しい設計:テーブル粒度に合わせる
- **PurchaseOrderHeader**(エンティティ)← EKKO相当
- **PurchaseOrderItem**(エンティティ) ← EKPO相当
❌ 避けるべき設計:ヘッダと明細を1つのエンティティに混ぜる
- PurchaseOrder(ヘッダ項目 + 明細項目を全部フラットに持つ)
混ぜるとなぜ問題か(so what)
ヘッダと明細を1つのエンティティにまとめると、以下の問題が発生します。
- データの重複:1つの発注に10明細あれば、ヘッダ情報(仕入先、通貨など)が10行分重複して返される
- 更新の複雑化:「ヘッダだけ更新したい」「明細だけ追加したい」という操作が、混在したエンティティでは表現しづらい
- パフォーマンスの悪化:不要なJOINが常に発生し、明細が不要な一覧画面でも重いクエリになる
- SAPの標準パターンとの乖離:標準のODataサービスやCDS Viewはテーブル粒度で設計されており、独自設計はメンテナンスコストが高くなる
原則:「1つのエンティティタイプ ≒ 1つのテーブル(またはCDS View)」を基本とし、エンティティ間の関連は次に説明するNavigation Propertyで表現します。エンティティ設計からODataサービスを実際に作成する手順はODataサービスの作り方|SEGW方式とCDS View+RAP方式を比較するで解説しています。
Navigation Property
エンティティ間の「つながり」を表現する
Navigation Propertyとは、あるエンティティから別のエンティティへの関連(リレーション)を定義する仕組みです。データベースでいう外部キー(Foreign Key)による参照関係を、ODataの世界で表現したものと考えてください。
先ほどの購買発注の例で言えば:
- PurchaseOrderHeader → PurchaseOrderItem への Navigation Property(1対多)
- PurchaseOrderItem → PurchaseOrderHeader への Navigation Property(多対1)
flowchart LR
subgraph "PurchaseOrderHeader"
H["発注ヘッダ\nキー: PurchaseOrder\nNav: ToItems →"]
end
subgraph "PurchaseOrderItem"
I["発注明細\nキー: PurchaseOrder +\nPurchaseOrderItem\nNav: ToHeader →"]
end
H -->|"ToItems\n(1対多)"| I
I -.->|"ToHeader\n(多対1)"| HNavigation Propertyの使い方:$expand
Navigation Propertyの最大の活用場面が、クエリオプション $expand です。$expandを使うと、1回のHTTPリクエストで関連エンティティのデータも一括取得できます。
ヘッダのみ取得:
GET /PurchaseOrderHeaderSet('4500000001')
→ ヘッダの情報だけが返る
ヘッダ+明細を一括取得:
GET /PurchaseOrderHeaderSet('4500000001')?$expand=ToItems
→ ヘッダの情報に加えて、明細の配列も含まれて返る
$expandを使わない場合との比較
flowchart LR
subgraph "$expandなし(2回リクエスト)"
R1["① GET /HeaderSet\nヘッダ取得"] --> R2["② GET /ItemSet\n?$filter=PO eq '...'\n明細を別途取得"]
end
subgraph "$expandあり(1回リクエスト)"
R3["① GET /HeaderSet\n?$expand=ToItems\nヘッダ+明細を一括取得"]
endなぜ$expandが重要か(why so):HTTPリクエストは1回ごとにネットワークのラウンドトリップが発生します。「ヘッダを取得→明細を取得→関連マスタを取得…」と何度もリクエストすると、レスポンス時間が積み重なりUX(ユーザー体験)が悪化します。$expandはこの問題を解消し、1回のリクエストで必要なデータを過不足なく取得するための仕組みです。
Navigation Propertyの代表的なパターン
SAPの業務データでは、以下のようなNavigation Propertyのパターンが頻出します。
| パターン | 多重度 | 例 |
|---|---|---|
| ヘッダ → 明細 | 1対多 | 受注ヘッダ → 受注明細、請求書ヘッダ → 請求書明細 |
| 明細 → ヘッダ | 多対1 | 受注明細 → 受注ヘッダ |
| 伝票 → マスタ | 多対1 | 発注明細 → 品目マスタ、受注ヘッダ → 得意先マスタ |
| ヘッダ → ステータス | 1対1 | 受注ヘッダ → 受注ステータス |
SAPの伝票構造は「ヘッダ-明細」パターンが基本であり、この構造がそのままODataのNavigation Propertyに反映されます。販売管理の伝票構造についてはSAP SDモジュール 業務フロー完全解説|受注から入金までO2Cサイクルを網羅で解説しています。
読者への示唆(so what):Navigation Propertyの設計は、テーブル間の外部キー関係をそのまま反映するのが基本です。SAPのテーブル設計を知っていれば、ODataのNavigation Property設計は自然に導き出せます。逆に、テーブル構造を無視した独自のNavigationを設計すると、実装が複雑になり保守性が下がります。
$metadataで構造を確認する
ODataサービスは、自身のデータ構造を$metadataエンドポイントで公開しています。これはODataの「自己記述的」という特徴の核心部分です。
メタデータの取得:
GET /sap/opu/odata/sap/API_PURCHASEORDER_PROCESS_SRV/$metadata
$metadataにアクセスすると、以下のような情報がXMLで返されます。
<EntityType Name="PurchaseOrderHeader">
<Key>
<PropertyRef Name="PurchaseOrder"/>
</Key>
<Property Name="PurchaseOrder" Type="Edm.String"/>
<Property Name="CompanyCode" Type="Edm.String"/>
<Property Name="Supplier" Type="Edm.String"/>
<Property Name="CreatedAt" Type="Edm.DateTime"/>
<NavigationProperty Name="ToItems"
Relationship="PO_Header_To_Items"
FromRole="Header" ToRole="Item"/>
</EntityType>
※ 上記はOData V2の$metadata構文です。V4ではType属性とPartner属性を用いた異なる構文になります。
なぜ$metadataが重要か(why so):通常のREST APIでは、API仕様書(SwaggerやOpenAPIドキュメント)を別途用意しなければ、クライアント開発者はAPIの構造を知ることができません。ODataでは$metadataにアクセスするだけで、エンティティタイプの定義、プロパティの型、Navigation Propertyの関係がすべてプログラムから読み取れるのです。FioriアプリやSAP BTPの開発ツールは、この$metadataを自動的に読み込んでUIの雛形を生成します。$metadataの確認やODataサービスのテスト方法についてはODataサービスのテスト・デバッグ|Gateway Client・エラーログ・よくあるエラーパターンで詳しく解説しています。
OData全体像のまとめ
ここまでの内容を1つの図で整理します。
flowchart LR
subgraph "クライアント(Fiori / 外部アプリ)"
C1["HTTPリクエスト\nGET / POST / PUT / DELETE\n+ クエリオプション"]
end
subgraph "ODataサービス"
S1["$metadata\nエンティティ定義"]
S2["エンティティセットA\nHeaderSet"]
S3["エンティティセットB\nItemSet"]
S2 -->|"Navigation\nProperty"| S3
end
subgraph "SAPデータベース"
D1["テーブルA\nEKKO"]
D2["テーブルB\nEKPO"]
end
C1 -.->|"構造を確認"| S1
C1 -->|"データ操作"| S2
C1 -->|"データ操作"| S3
S2 --> D1
S3 --> D2よくある疑問
Q1. ODataとRFC/BAPIは何が違うのですか?
RFC/BAPIはSAP固有のインターフェースであり、呼び出し側にSAPのコネクタ(SAP JCoなど)が必要です。一方、ODataは標準的なHTTP通信で動作するため、Webブラウザやcurlコマンドからでもアクセスできます。
| 項目 | RFC/BAPI | OData |
|---|---|---|
| プロトコル | SAP独自(RFC) | HTTP/HTTPS |
| クライアント要件 | SAP JCoなどのコネクタが必要 | HTTPクライアントがあれば可 |
| データ形式 | SAP独自のバイナリ | JSON / XML |
| 適用場面 | SAP同士の連携、バッチ処理 | Web/モバイルアプリ、クラウド連携 |
RFC/BAPIが不要になったわけではなく、用途に応じた使い分けが重要です。大量データのバッチ連携にはRFC/BAPIが適し、UIからのリアルタイムアクセスにはODataが適しています。データ移行でのBAPI活用についてはSAPのデータ移行ベストプラクティスで解説しています。
Q2. ODataのエンティティとABAPのデータ型はどう対応しますか?
ODataのプロパティ型(Edm型)は、ABAPのデータ型と以下のように対応します。
| OData型(Edm) | ABAPデータ型 | 例 |
|---|---|---|
| Edm.String | CHAR, NUMC, STRING | 発注番号、会社コード |
| Edm.Decimal | DEC, CURR, QUAN | 金額、数量 |
| Edm.DateTime | DATS, TIMS | 作成日、作成時刻 |
| Edm.Int32 | INT4 | カウンタ、連番 |
| Edm.Boolean | CHAR1(‘X’ / ’ ‘) | フラグ項目 |
Q3. エンティティの粒度はテーブルより細かくしてもいいですか?
基本はテーブル粒度に合わせますが、CDS Viewで特定のユースケース向けにプロジェクション(射影)したものをエンティティにすることはあります。たとえば、品目マスタ(MARA)の全400項目のうち、在庫照会に必要な20項目だけを選んだCDS Viewを作り、それをODataエンティティとして公開するパターンです。重要なのは、1エンティティが複数テーブルを無秩序にJOINして1フラットな構造にしないことです。CDS ViewについてはCDS Viewとは何か?S/4HANAのデータモデリングを支える新しい基盤で詳しく解説しています。
Q4. Navigation Propertyはいくつでも定義できますか?
技術的には制限はありませんが、実務では必要なものだけを定義するのが鉄則です。Navigation Propertyが多すぎると、$expandで大量のデータを取得できてしまい、パフォーマンス問題の原因になります。「このサービスを使うアプリが、実際にどのエンティティ間の遷移を必要とするか」を起点に設計してください。
まとめ
- ODataはREST APIの上に規約を追加したプロトコルであり、URLの構造・クエリオプション・メタデータの形式が標準化されている
- クエリオプション(
$filter、$select、$expandなど)により、サービスを変更せずにクライアント側で取得条件を柔軟に制御できる - エンティティの設計粒度はテーブル(またはCDS View)に揃えるのが基本。ヘッダと明細を混ぜた独自設計はデータの重複・パフォーマンス問題・保守性の低下を招く
- Navigation Propertyでエンティティ間のリレーションを表現し、
$expandで関連データを一括取得する。設計はテーブル間の外部キー関係に合わせる - $metadataでサービスの構造を自己記述的に公開でき、開発ツールやFioriはこれを利用してUIを自動生成する
- SAP の世界ではOData V2とV4が共存しており、既存はV2、新規開発はV4が推奨
次の記事では、SAPにおけるODataの具体的な活用場面を解説しています。SAP GatewayとCDS Viewの役割でODataサービスの基盤を、Fiori×OData|FioriアプリがODataをどう使うかでFioriアプリのアーキテクチャを確認してください。
ODataの仕組みをさらに深く学ぶなら、ABAPからRAP・UI5 Fioriまで一貫して学べる実践講座がおすすめです。全335レクチャーでOData開発の実践力を体系的に身につけられます。