SharePoint Lists は簡易的なテーブルとして、様々な情報を格納するのに適しています。
SharePoint サイトでの活用はもちろんのこと、Power Apps や Power Automate などでも活用することができます。
特に、Power Apps が出てきたことで、多くの方が、SharePoint Lists を活用しているように思います。
しかしながら、Power Apps 側から見ると、一般的なデータベースに比べて多くの誓約があるため、以下のように、定期的に SharePoint Lists の中身を消しているという方もいらっしゃるかもしれません。
しかしこの方法だと、レコードの数だけ項目の削除処理が実行されるため、かなり多くのアクションを消費することになります。
これは、Power Platform 要求の制限に引っかかる可能性が出てきます。
制限を超えるとそのユーザーのほかのフローや Power Apps の動作に影響が出る可能性がありますので、できるだけアクション数を減らす必要があります。
SharePoint のバッチ要求
「項目の削除」アクションは、SharePoint API の Delete 処理を使用しています。
この API の仕様に準じており、毎回この処理を実行しています。
つまり、この API をレコードの数だけ実行することになり、それが Power Platform 要求の制限に加算されることになります。
その為、1回の API の中で、できるだけ多くのデータを削除させる方法を模索する必要があります。
それを行うための方法が、Batch 要求です。
Batch 要求は、一つのAPIコールの中で複数の処理を指定できることが特徴です。
なので、この Batch 要求を使用し、リストの削除コマンドを複数入れれば、アクション数をかなり減らすことができそうです。
ただし、一回の Batch 要求で処理可能なコマンドは最大で1000件までですので、それ以上の件数を処理したい場合は、分割して要求する必要がありそうです。
削除要求コマンドの書き方
Batch 要求自体の書き方は、OData version 3.0 の規約に従う必要があります。
この仕様に合わせて Batch 要求を書くと以下のようになります。
--batch_26309428-854f-44b9-b83a-13e99fe26ab0
Content-Type: multipart/mixed; boundary=changeset_c19e0398-48e4-4d1a-95e0-f8209f2127bc
Content-Length: 1073
Content-Transfer-Encoding: binary
--changeset_c19e0398-48e4-4d1a-95e0-f8209f2127bc
Content-Type: application/http
Content-Transfer-Encoding: binary
DELETE https://{tenantName}.sharepoint.com/sites/{SiteName}/_api/web/lists/getByID('{ListID}')/items(5002) HTTP/1.1
Content-Type: application/json;odata=verbose
Accept: application/json;odata=nometa
IF-MATCH: *
--changeset_c19e0398-48e4-4d1a-95e0-f8209f2127bc
Content-Type: application/http
Content-Transfer-Encoding: binary
DELETE https://{tenantName}.sharepoint.com/sites/{SiteName}/_api/web/lists/getByID('{ListID}')/items(5003) HTTP/1.1
Content-Type: application/json;odata=verbose
Accept: application/json;odata=nometa
IF-MATCH: *
--changeset_c19e0398-48e4-4d1a-95e0-f8209f2127bc
Content-Type: application/http
Content-Transfer-Encoding: binary
DELETE https://{tenantName}.sharepoint.com/sites/{SiteName}/_api/web/lists/getByID('{ListID}')/items(5004) HTTP/1.1
Content-Type: application/json;odata=verbose
Accept: application/json;odata=nometa
IF-MATCH: *
--changeset_c19e0398-48e4-4d1a-95e0-f8209f2127bc--
--batch_26309428-854f-44b9-b83a-13e99fe26ab0--
基本的に全体を Batch 文で囲い
各処理を Changeset 文を定義して記述する
というスタンスですね。
何事もそうですが、全体的な処理方法は、最終的な処理手段を実際に確認し、問題ないことを確立した上で組んでいくのが一番早く作れます。
フローの全体像
それでは、フローの全体像です。
このフローは
Paul Murana さんの YouTube チャンネルの
こちらの動画を参考にしました
また、このフローは、サイトやリストを簡単に変更できるように、ソリューションで作成しています。
サイトとリストは、環境変数を使用しています。
リスト残り件数確認用の変数
前述の通り、SharePoint の Batch 要求は、1000件までしかコマンド実行できませんので
1000件ごとにループする必要があります。
その為、残り0件になるまで、リストから1000件取得して Batch 要求 を実行するという
ループを定義する必要があります。
そこで、対象の SharePoint リストの件数をループ内で取得して格納する変数を定義しておきます。
バッチIDと変更セットIDの定義
後続の Batch 要求で使用する、バッチIDと変更セットのIDをここで定義しておきます。
{
"batchGUID": @{guid()},
"changeSet": @{guid()}
}
変更セットテンプレートの定義
Batch 要求の中で、レコードを削除するコマンドのテンプレートをここで定義しておきます。
--changeset_@{outputs('BatchIDs')['changeSet']}
Content-Type: application/http
Content-Transfer-Encoding: binary
DELETE @{parameters('SPS-MultiDelete-Site (rinatamu_SPSMultiDeleteSite)')}/_api/web/lists/getByID('@{parameters('SPS-MultiDelete-List (rinatamu_SPSMultiDeleteList)')}')/items(|ID|) HTTP/1.1
Content-Type: application/json;odata=verbose
Accept: application/json;odata=nometa
IF-MATCH: *
ここでは、環境変数を用いてますので、環境変数以外を指定したい場合は、以下の箇所を適宜書き換えてください
@{parameters('SPS-MultiDelete-Site (rinatamu_SPSMultiDeleteSite)')}
@{parameters('SPS-MultiDelete-List (rinatamu_SPSMultiDeleteList)')}
ループ処理
1000件ごとにループさせることで、膨大なリストの削除を効率的に行うように
ループを定義し、その中にリストの取得と削除を行うようにします。
Do Until アクション
Expression
@equals(variables('itemCount'), 0)
処理件数やタイムアウトは、削除するリストデータの件数などに応じて適宜修正するようにしましょう。
リストデータの取得
1000件ごとにデータを絞って取得する必要があります。
SharePointコネクタの「複数の項目の取得」も使っていいのですが、確実に件数を絞りたいので、API から直接取得したいと思います。
SharePoint に HTTP要求を送信します アクション
(Send an HTTP Request to SharePoint)
URI
/_api/web/lists/getbyid('@{parameters('SPS-MultiDelete-List (rinatamu_SPSMultiDeleteList)')}')/items?$select=Id&$top=1000
上記の URI は、リストの内部IDを指定する URI となっています。
これは、環境変数を使用する場合は、内部IDを渡す仕様なためです。
getbyTitle
を使用することで、リスト名を直接指定することも可能ですが
日本語のリストだとちょっとめんどくさいので、可能であればIDを指定することをお勧めします。
また、件数は、URI の top パラメータで指定しています。
取得データをJSONデータとして定義する
取得した結果のデータはかなり複雑なデータ形式で、Body内のデータはこのようになっています。
{
"d": {
"results": [
{
"__metadata": {
"id": "87a600a6-5fa2-4cb0-877e-ff486238733b",
"uri": "https://{tenantName}.sharepoint.com/sites/{siteName}/_api/Web/Lists(guid'cb13a955-7ca3-47e4-bd44-c0a9d4f8ac34')/Items(5004)",
"etag": "\"1\"",
"type": "SP.Data.List_x0020_5000ListItem"
},
"Id": 5004,
"ID": 5004
}
]
}
後続のデータ渡しに苦慮することから、一旦JSON分析を行います。
JSON解析 アクション(Parse JSON)
コンテンツ
body('Get_Items_From_HTTP')
スキーマ
{
"type": "object",
"properties": {
"d": {
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"__metadata": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"uri": {
"type": "string"
},
"etag": {
"type": "string"
},
"type": {
"type": "string"
}
}
},
"Id": {
"type": "integer"
},
"ID": {
"type": "integer"
}
}
}
}
}
}
}
}
ID値だけに絞った配列に変換する
このデータだとまだ扱いづらいので、ID値のみをピックアップした配列データに変換します。
選択 アクション(Select)
from
body('Parse_JSON')?['d']?['results']
Map
{
"ID": item()?['Id']
}
処理件数のカウント
今回処理対象の件数をここで取得します。
値
length(body('Get_Items'))
テンプレート文に当てはめ
先に作成していたテンプレート文を元に、削除コマンドの内容を作成していきます。
選択 アクション(Select)
from
body('Get_Items')
Map
replace(
outputs('Template'),
'|ID|',
string(
item()['Id']
)
)
ここでは、テンプレート文の |ID|
を各レコードのID値に置き換える処理をしています。
出力結果はこのようになります。
{
"body": [
"--changeset_c19e0398-48e4-4d1a-95e0-f8209f2127bc\nContent-Type: application/http\nContent-Transfer-Encoding: binary\n\nDELETE https://{tenantName}.sharepoint.com/sites/{siteName}/_api/web/lists/getByID('{listID}')/items(5002) HTTP/1.1\nContent-Type: application/json;odata=verbose\nAccept: application/json;odata=nometa\nIF-MATCH: *\n",
"--changeset_c19e0398-48e4-4d1a-95e0-f8209f2127bc\nContent-Type: application/http\nContent-Transfer-Encoding: binary\n\nDELETE https://{tenantName}.sharepoint.com/sites/{siteName}/_api/web/lists/getByID('{listID}')/items(5003) HTTP/1.1\nContent-Type: application/json;odata=verbose\nAccept: application/json;odata=nometa\nIF-MATCH: *\n",
"--changeset_c19e0398-48e4-4d1a-95e0-f8209f2127bc\nContent-Type: application/http\nContent-Transfer-Encoding: binary\n\nDELETE https://{tenantName}.sharepoint.com/sites/{siteName}/_api/web/lists/getByID('{listID}')/items(5004) HTTP/1.1\nContent-Type: application/json;odata=verbose\nAccept: application/json;odata=nometa\nIF-MATCH: *\n"
]
}
変更セット文の作成
配列で作成された各レコードごとの削除コマンドを、変更セット文として一つの文字列として作成します。
Input
join(
body('Select'),
decodeUriComponent('%0A')
)
Batch 要求実行
最終的な Batch 文を定義した上で、Batch 要求を実行します
SharePoint に HTTP要求を送信します アクション
(Send an HTTP Request to SharePoint)
URI
/_api/$batch
Header
{
"X-RequestDigest": "digest",
"Content-Type": "multipart/mixed; boundary=batch_@{outputs('BatchIDs')['batchGUID']}"
}
Body
--batch_@{outputs('BatchIDs')['batchGUID']}
Content-Type: multipart/mixed; boundary=changeset_@{outputs('BatchIDs')['changeSet']}
Content-Length: @{length(outputs('BatchDelete'))}
Content-Transfer-Encoding: binary
@{outputs('BatchDelete')}
--changeset_@{outputs('BatchIDs')['changeSet']}--
--batch_@{outputs('BatchIDs')['batchGUID']}--
まとめ
Power Apps や Power Automate の運用が進むと、データ件数や処理件数の制限といった壁が出てくることがあります。
そういう時に、SharePoint 側の API とかをきちんと把握することで、様々な解決策が出てくると思います。
専門的になって取っつきにくいと思うかもしれませんが、本記事を実際に試してもらい、より幅広く業務改善できるようになってもらえればとてもうれしいです。