Power Appsでデータを保存する方法は1つではありません。フォームを置いて送信ボタンを押すのが基本ですが、それでは足りない場面があります。

そこで使うのがPatch関数です。ただし自由度が高いぶん、気づきにくい落とし穴がいくつもあります。この記事では基本と、公式ドキュメントに書かれた注意点を整理します。

まず使い分け|フォームかPatchか

Microsoftの公式ドキュメントは、こう線を引いています。

状況 使うもの
簡単な変更をユーザーに入力させて保存する 編集フォームコントロール
ユーザー操作を必要としない更新 Patch
複数の画面にまたがるフォーム Patch
その他の複雑な状況 Patch

「入力画面が1つで、項目もそのまま保存するだけ」ならフォームで十分です。わざわざPatchにする必要はありません。

フォームの基本操作はキャンバスアプリの作り方入門で解説しています。

基本の書き方

構文はこうです。

Patch( データソース, 基本レコード, 変更レコード )

既存のレコードを更新する

Patch( IceCream, LookUp( IceCream, Flavor = "Chocolate" ), { Quantity: 400 } )

Chocolateという行を探して、Quantityだけを400に書き換えます。指定したフィールドだけが更新され、他の項目には影響しません。

新しいレコードを作る

Patch( IceCream, Defaults( IceCream ), { Flavor: "Strawberry" } )

更新と作成を分けるのは、2番目の引数だけです。

  • データソースから取得したレコードを渡す → 更新
  • Defaults関数の結果を渡す → 新規作成

このとき、指定しなかった列にはデータソースの既定値が入ります。IDのような自動採番の列も、データソース側で生成されます。

落とし穴①|Defaultsのデータソースを揃える

公式に明記されています。新しいレコードを作成するには、Patchのデータソースと、Defaults関数のデータソースが一致している必要があります。

次のような書き方は動きません。

Patch( 注文テーブル, Defaults( 別のテーブル ), { ... } )

コピー&ペーストで式を流用したときに起きがちです。「作成されない」「エラーになる」ときは、まずここを確認してください。

落とし穴②|戻り値に関連テーブルの値は入らない

Patchは変更または作成したレコードを返します。作成時には、データソースが自動生成したIDなども含まれます。

ただし、関連するテーブルのフィールドの値は返しません。

つまり、保存した直後に戻り値から関連先の情報を取ろうとしても、空のままです。必要なら、あらためてLookUpで取得し直す必要があります。

「保存はできたのに、その後の処理で値が取れない」という症状の原因がこれです。

複数レコードをまとめて処理する

Patchは1回の呼び出しで複数のレコードを作成・変更できます。基本レコードの代わりにテーブルを渡します。

Patch( IceCream, Table( { ID: 1, ... }, { ID: 2, ... } ), Table( { Quantity: 300 }, { Quantity: 400 } ) )

落とし穴③|レコード数が一致しないとエラー

公式の注記です。各変更テーブルのレコード数は、ベーステーブルのレコード数と一致しなければなりません。一致しない場合はエラーになります。

ギャラリーの選択結果などを動的に渡す場合、件数がずれる可能性を考えておく必要があります。

落とし穴④|委任の警告はPatchのせいではない

ここは誤解されやすい点です。

公式によれば、Patch関数自体はデータソースに書き込みを行うため、委任の対象ではありません。

それでもPatchを含む式に委任警告が出ることがあります。原因はレコードを選ぶ部分——FilterやLookUp、ForAllが、データソースの委任制限を超えるクエリを含んでいる場合です。

警告が出たら、それがPatchに対するものか、データを探す側に対するものかを確認してください。直すべきはたいてい後者です。

委任の考え方はデータソース選びと委任で詳しく解説しています。

エラー処理|IfErrorが推奨

データソースへの書き込みは失敗しうる操作です。公式もIfErrorを推奨メカニズムとして挙げています。

IfError( Patch( ... ), Notify( "更新に失敗しました: " & FirstError.Message, NotificationType.Error ) )

典型的な3つのエラー

エラー 原因と対処
ネットワークエラー 接続喪失、データソースの一時的な利用不可、権限不足。IfErrorで意味のあるメッセージを出す
サーバー上の変更と競合 読み書きの間に別のユーザーが同じレコードを変更した。Refresh関数でデータソースを更新してからやり直す
権限エラー ユーザーに作成・変更の権限がない。IfErrorで検出して案内する

「競合」のエラーは、複数人で同時に使うアプリで必ず起きます。Refreshして再試行する導線を最初から作っておくと、問い合わせが減ります。

権限の設計についてはアクセス制限と共有をご覧ください。

落とし穴⑤|ForAllの中で条件が常に真になる

これは気づきにくく、しかも被害が大きい問題です。

ForAllの中で、同じデータソースに対してLookUpやFilterを使うと、フィールド名が衝突します。

公式が挙げる例では、OrderId = A[@OrderId] と書いたつもりが、両辺ともLookUpの範囲内のフィールドとして解釈され、条件が常に真になります。

結果として常に最初の行が返ります。エラーは出ません。間違ったレコードを更新し続けるという、最悪の壊れ方をします。

対処|AsまたはThisRecordを使う

公式の推奨は、As演算子またはThisRecordキーワードで明示することです。

LookUp( '[dbo].[Orders1]' As B, B.OrderId = A[@OrderId] )

または

LookUp( '[dbo].[Orders1]', ThisRecord.OrderId = A[@OrderId] )

入れ子になった式では、省略せずに書く。これが事故を防ぐ唯一の方法です。

関連する関数

  • Update——レコード全体を置き換える。Patchは指定した列だけを変える
  • UpdateIf——条件に合う複数レコードの特定の列を変更する
  • Collect——レコードを作成する

コレクションや変数の扱いは変数とコレクションにまとめています。

よくある質問

Q. フォームとPatchはどちらを使うべきですか?

簡単な変更なら編集フォームコントロールです。ユーザー操作を伴わない更新や、複数画面にまたがるフォームなど複雑な場合にPatchを使います。

Q. 新規作成と更新はどう書き分けますか?

2番目の引数だけが違います。データソースから取得したレコードを渡せば更新、Defaults(データソース)を渡せば新規作成です。

Q. 新しいレコードが作成されません

Patchのデータソースと、Defaultsのデータソースが一致しているか確認してください。公式にも一致が必要と明記されています。

Q. Patchで委任警告が出ます

Patch自体は委任の対象外です。警告の原因は、Filter・LookUp・ForAllなどレコードを選ぶ部分にあります。そちらを見直してください。

Q. 複数人で使うと保存に失敗します

「サーバー上の変更と競合」の可能性があります。読み書きの間に他のユーザーが同じレコードを変更した場合に発生します。Refresh関数でデータソースを更新してからやり直してください。

Q. ForAllの中のLookUpが必ず最初の行を返します

フィールド名が衝突しています。As演算子かThisRecordで、どちらのスコープのフィールドかを明示してください。エラーが出ないため見つけにくい不具合です。

まとめ

  • 簡単な変更はフォーム、複雑な更新はPatchというのが公式の線引き
  • 更新と作成の違いは2番目の引数だけ。Defaults()を渡せば新規作成
  • PatchとDefaultsのデータソースは一致させる必要がある
  • 戻り値に関連テーブルの値は含まれない。必要ならLookUpで取り直す
  • テーブルを渡せば一括処理できるが、レコード数が一致しないとエラー
  • Patch自体は委任の対象外。警告はレコードを選ぶ部分が原因
  • エラー処理はIfErrorが推奨。競合エラーはRefreshして再試行
  • ForAllの入れ子では As/ThisRecord を使う。省略すると条件が常に真になり、エラーなしで誤ったレコードを更新する

参考・出典

  • Patch 関数|Microsoft Learn――構文と引数、Defaults関数による新規作成とデータソース一致の要件、戻り値に関連テーブルのフィールドが含まれないこと、テーブルを使った複数レコードの処理とレコード数一致の制約、Patch自体は委任の対象外でありレコード選択部分が警告の原因になること、IfErrorが推奨メカニズムであること、ネットワークエラー・サーバー上の競合・権限エラーという典型的な失敗と対処、ForAll内でのフィールド名の衝突とAs演算子・ThisRecordによる解消
  • EditForm、NewForm、SubmitForm、ResetForm、ViewForm 関数|Microsoft Learn――SubmitFormが検証に合格した場合に変更を送信し、成功時にOnSuccessが実行されること