Power AutomateでExcel最終行(最新行)を取得してメールを1通だけ送る方法|Apply to each回避の決定版

Power AutomateでExcelテーブルの「最終行(最新行)」だけをメール本文に入れて1通送るつもりが、いつの間にかApply to eachが付いて行数分のメールが飛ぶ――この“あるある”を、原因から対策まで具体例つきで整理します。

目次

よくある症状:Excelの最新行だけを使いたいのに、メールが複数通送信される

次のようなフローを作ったときに、意図しない挙動に悩むケースが多いです。

  • Excel(テーブル)から「最後に追加・更新された行」だけを取り出す
  • その行の値(担当者、案件名、日時、メモなど)をメール本文に差し込む
  • メールは1通だけ送る

しかし実際には、Excelの「表に存在する行を一覧表示(List rows present in a table)」が返すデータが配列(複数行)であるため、メール本文で列を参照した瞬間にPower Automateが「これは複数件だね」と判断し、自動でApply to each(各行に適用)を付けます。

その結果、例えば7行取得できてしまうと、7通メールが送られる……という状態になります。

原因:Excelの「一覧表示」は“配列(value)”を返す仕様

Power AutomateでExcelテーブルを読むときの要点はここです。

アクション主な戻り値Power Automate側の扱い起きやすい問題
表に存在する行を一覧表示(List rows present in a table)value(行の配列)複数件(配列)列参照するとApply to eachが自動付与
行を取得(Get a row)単一行(オブジェクト)単一件キー列が必要(最新行の特定には工夫が要る)

「一覧表示」の返り値イメージは、ざっくりこうです(実際はもっと情報が付くこともあります)。

{
  "value": [
    { "ID": 101, "Title": "A案件", "UpdatedAt": "2026-01-04T09:10:00Z" },
    { "ID": 102, "Title": "B案件", "UpdatedAt": "2026-01-04T09:30:00Z" }
  ]
}

メール本文で「Title」などを動的なコンテンツから選ぶと、Power Automateは「value配列の各要素に対してTitleを取る」と解釈し、ループが付与されます。

解決の基本方針:メール作成前に“配列→1行(オブジェクト)”に確定する

Apply to eachを回避するコツは単純で、メール作成の前に次のどちらかを満たせばOKです。

  • そもそも取得結果を1件にしてしまう(推奨)
  • 取得結果(配列)から1行だけ取り出して変数やComposeに保持する

一度「単一行(オブジェクト)」として確定できれば、以降はそのオブジェクトのプロパティ参照になるので、メールで列を差し込んでもループが発生しません。

方法A(おすすめ):Top Count=1 + Order Byで“取得時点で1行だけ”に絞る

最短で安定しやすいのがこの方法です。「最新行」を並び順で先頭に持ってきて、先頭1件だけ取る、という考え方です。

事前準備:Excel側に“並び替え可能な列”を用意する

「最新」を判定する軸がないと、Power Automate側で“最終行”を正しく決められません。Excel側に次のどちらかを用意するのが現実的です。

判定列用途例ポイント
連番(ID / No)追加順を確実に判定1,2,3…重複しないように運用(手入力でもOK)
更新日時(UpdatedAt)更新がある運用でも最新を判定2026-01-04 18:30日時としてソートできる形式で持つ(文字列より日時型推奨)

手入力で更新日時がブレるのが不安なら、Power Automateで行を追加・更新するタイミングでutcNow()を入れる運用にすると安定します。

Power Automate設定:詳細オプションで絞り込む

「表に存在する行を一覧表示」を配置したら、詳細オプション(高度なオプション)を開いて、次を設定します。

  • Order By:並び替え列を降順(desc)
  • Top Count:1

例:

  • Order By = UpdatedAt desc
  • Top Count = 1

IDで判定するなら:

  • Order By = ID desc
  • Top Count = 1

これで「一覧表示」自体の結果は配列のままですが、中身は1件だけになります。次に、その1件を「単一行」として確定させます。

Composeで“単一行”に確定させる(ここがApply to each回避の肝)

次に「作成(Compose)」アクションを追加し、式に以下を入れます。

first(body('表に存在する行を一覧表示')?['value'])

Order Byをdescにしているので、先頭(first)が「最新行」です。
このComposeに「最新行」や「lastRow」など分かりやすい名前を付けておくと、後が楽です。

メール本文はComposeの出力を参照する

メール送信(Outlookの「メールの送信(V2)」など)では、Excelの動的コンテンツ(一覧表示の列)を直接選ばず、Composeの出力を式で参照します。

outputs('最新行')?['Title']
outputs('最新行')?['UpdatedAt']
outputs('最新行')?['担当者']

列名にスペースや記号がある場合でも、['列名']形式なら参照できます。
この参照に統一すると、メール側にApply to eachが付く確率を大きく下げられます。

実装イメージ(フローの並び)

順番アクション役割
1(任意)トリガー:手動/スケジュール/フォーム送信などフロー開始
2表に存在する行を一覧表示(Order By + Top Count)最新候補1件だけ取得
3作成(Compose):first(value)配列→単一行(オブジェクト)へ確定
4メール送信Composeを参照して1通だけ送る

方法B:last() / first()を使って“配列の最後(または最初)”を取り出す

質問でよく挙がるのが、次のような式です。

last(body('表に存在する行を一覧表示')?['value'])

これは配列の最後の要素を取り出す式としては正しいです。ただし、実務で落とし穴になりがちなのが「その配列の順序が本当に“最新順”なのか」です。

last()が向いているケース

  • 「一覧表示」の結果が、運用上の“最終行”と同じ順序で返ってくることが確実
  • または、Order Byで順序を明示できていて、昇順(asc)で取得している(→最後が最新)

たとえば、Order ByをID ascにしているなら、最後が最大IDなので最新になります。この場合は、last()の考え方が合います。

安全にする:空配列対策を入れる

テーブルが空のときにfirst()やlast()を実行するとエラーになりやすいので、最低限次のようなガードを入れるのがおすすめです。

if(
  empty(body('表に存在する行を一覧表示')?['value']),
  null,
  last(body('表に存在する行を一覧表示')?['value'])
)

この式をComposeに入れ、後続のメール送信の前に「nullだったら送らない」条件分岐を入れると、運用で止まりにくくなります。

順序が保証されないときの注意点

Excelコネクタの「一覧表示」は、ファイル状況・コネクタの解釈・テーブルの状態により、必ずしも「目に見える行順=返ってくる順」と一致しない場合があります。
このため、Order Byで並びを確定できないのにlast()に賭けるのは、長期運用ほど事故りやすいです。

結論として、方法Bを採用するならOrder Byで順序を確定するか、次の方法Cのように比較ロジックで最新を決めるのが堅いです。

方法C:Apply to eachは“使うが”、メール送信はループの外で1回だけ行う

「どうしてもOrder Byが効かない」「最新判定が少し複雑(更新日時が空の行がある等)」という場合は、Apply to eachそのものをゼロにするより、メール送信をループの外に置く方が安定します。

ポイントは、ループ内では最新行を変数に上書きしていくだけにし、最後にその変数を使ってメールを1通送ることです。

パターン1:ループの最後の行を変数に保持(順序が信頼できる場合)

  1. 変数を初期化(種類:オブジェクト)
    例:varLatestRow を {} で初期化
  2. 表に存在する行を一覧表示
  3. Apply to each(value)内で「変数の設定」
    値:item()
  4. Apply to eachの外でメール送信(本文でvariables('varLatestRow')を参照)

このやり方は単純ですが、前提として「Apply to eachで回る順序=欲しい順序」である必要があります。順序が怪しいなら、次の比較パターンが安全です。

パターン2:IDや更新日時を比較して“最大の行”を採用(順序に依存しない)

順序に依存しない「最新判定」をやるなら、変数を2つ使うと分かりやすいです。

  • varMaxId(整数):現時点の最大ID
  • varLatestRow(オブジェクト):最大IDの行データ

流れは次の通りです。

  1. 変数を初期化
    varMaxId:0
    varLatestRow:{}
  2. 表に存在する行を一覧表示(必要ならフィルターも)
  3. Apply to each(value)の中で条件分岐
    条件例:int(item()?['ID']) > variables('varMaxId')
  4. 条件が真なら、varMaxIdを更新し、varLatestRowにitem()を格納
  5. ループの外で、varLatestRowを使ってメール送信(1通)

更新日時で判定する場合は、ISO 8601形式(例:2026-01-04T09:30:00Z)で揃えると比較しやすくなります。Excelの日時型のまま扱う場合は、文字列比較で事故らないよう、Power Automate側で変換する運用も検討してください。

メール本文での参照例(変数)

variables('varLatestRow')?['Title']
variables('varLatestRow')?['担当者']
variables('varLatestRow')?['UpdatedAt']

これなら、Apply to eachが存在してもメール送信アクションはループの外に1回だけ置けるため、複数通送信の事故を止められます。

方法D:Office Scriptsで“最終行だけ”を返す(配列を扱わないのでループが消える)

少し発想を変えて、Excel側で「最終行のオブジェクト」を作って返してしまう方法です。Power Automate側では「スクリプトの戻り値=単一オブジェクト」になるため、Apply to eachを避けやすくなります。

Excel Online(Business)で使える「スクリプトの実行(Run script)」とOffice Scriptsを使う構成です(組織の設定によっては使えない場合があります)。

Office Scripts例:テーブルの最終行をオブジェクト化して返す

function main(workbook: ExcelScript.Workbook) {
  const table = workbook.getTable("Table1");
  const headers = table.getHeaderRowRange().getValues()[0] as string[];

const bodyRange = table.getRangeBetweenHeaderAndTotal();
const values = bodyRange.getValues();

if (values.length === 0) {
return { hasRow: false };
}

const last = values[values.length - 1];
const row: any = {};
headers.forEach((h, i) => (row[h] = last[i]));

return { hasRow: true, row };
}

Power Automate側では、戻り値のrow配下を参照してメール本文に差し込む形になります。
「Excelコネクタの一覧表示の順序が信用できない」「最新行の定義がExcel側の見た目の最終行で良い」といったケースでは、実装がスッキリします。

最新行を取り違えないための“Excel設計”のコツ

Power Automateの式や設定よりも、実はExcel側の設計で事故率が大きく変わります。運用で揉めやすいポイントを先に潰しておくのが、結局いちばん安上がりです。

おすすめのテーブル列構成

列名例型のイメージ役割運用の注意
ID数値最新判定の主キー重複させない(可能なら自動採番)
UpdatedAt日時更新の新しさ判定手入力よりフローでutcNow()を入れると安定
Status文字列送信対象の絞り込み例:Completedのみ送る、などFilter Queryに活用
MailSentYes/No二重送信防止送信後にTRUEへ更新すると安全(運用次第)

特に「二重送信防止」のMailSent列は、フローのトリガーが定期実行だったり、再試行が走ったりする運用で効いてきます。
“最新行を送る”だけでも、運用上は「前回送った行をもう一度送らない」仕組みが求められることが多いです。

よくある落とし穴:Apply to eachが消えない/また増える

方法AやBを試しても、なぜかメールのところにループが付いてしまうときは、原因がほぼ決まっています。

落とし穴:メール本文で“一覧表示の列”を直接選んでいる

一度でもメール本文に、Excelの「一覧表示」アクション由来の列(動的コンテンツ)を直接差し込むと、Power Automateは「複数行に対して差し込む」扱いにし、ループを生成します。

対策はシンプルです。

  • メール本文はCompose(または変数)だけを参照する
  • 列の差し込みは、動的コンテンツではなく式(outputs() / variables())で統一する

落とし穴:列名にスペースがあり、参照が崩れている

列名が「担当 者」などスペースを含むときは、次のように角括弧で指定します。

outputs('最新行')?['担当 者']

落とし穴:テーブルが空でfirst/lastが落ちる

実運用で「月初は空」「入力が遅れて空」などは普通に起こります。空配列チェックを入れて、空なら送らない(または“データなし”通知を送る)条件分岐にすると止まりにくいです。

if(
  empty(body('表に存在する行を一覧表示')?['value']),
  null,
  first(body('表に存在する行を一覧表示')?['value'])
)

落とし穴:更新日時が文字列で、ソート結果が期待と違う

例えば「2026/1/4 9:5」のような形式だと、文字列としての並び替えになり、descでも期待通りにならないことがあります。
更新日時は可能ならExcelの日時型にし、フローで入れるならISO 8601(例:2026-01-04T09:05:00Z)で揃えると事故が減ります。

落とし穴:件数が多いのに“最新判定”で全件を見ていない

方法Cのように全件を走査して最新を決める場合、一覧表示アクションの既定の取得件数(ページング設定)により、途中までしか取得できていないと誤判定になります。
この場合は、アクションの設定でページングを有効にし、必要件数を取得できるようにしてください。逆に方法A(Top Count=1)なら、全件取得をしない分だけ負荷も小さく、安定しやすいです。

メール本文を“見やすく”するテンプレート例(最新行を表で差し込む)

最後に、最新行の値をメールに入れるときのテンプレート例です。Outlookの「メールの送信(V2)」はHTML本文を扱えるので、表にすると読み手が楽になります。

件名例:

【最新更新】@{outputs('最新行')?['ID']} - @{outputs('最新行')?['Title']}

本文例(HTML):

<p>最新行(最新更新)の内容です。</p>
<table border="1" cellpadding="6" style="border-collapse:collapse;">
  <tr>
    <th>ID</th>
    <td>@{outputs('最新行')?['ID']}</td>
  </tr>
  <tr>
    <th>タイトル</th>
    <td>@{outputs('最新行')?['Title']}</td>
  </tr>
  <tr>
    <th>更新日時</th>
    <td>@{outputs('最新行')?['UpdatedAt']}</td>
  </tr>
  <tr>
    <th>担当者</th>
    <td>@{outputs('最新行')?['担当者']}</td>
  </tr>
</table>

この形にしておくと、差し込み箇所がすべてCompose由来になり、Apply to eachが勝手に付いて“複数通送信”になる事故を避けやすくなります。

元のスレッドでの結論と、現場での現実解

同様の質問はコミュニティでも頻出で、元のスレッドでは採用回答が「このフォーラムの管轄ではないのでPower Automate Communityへ」という案内で終わっているケースもあります。
ただし実務上は、ここまで紹介したように「配列のままメールに渡さない」を徹底するだけで、多くのケースが解決します。

迷ったら次の順で選ぶのが無難です。

  • まず方法A:Order By + Top Count=1(最短・読みやすい・負荷も軽い)
  • 順序や判定が怪しければ方法C(比較して最大を採用)
  • Excel側で解決したい・返す形を固定したいなら方法D(Office Scripts)

「最終行=見た目の一番下」なのか、「最終更新=UpdatedAtが最大」なのかで正解が変わるので、Excel側に判定軸(ID/UpdatedAt)を用意し、フロー側は“1行のオブジェクトに確定してからメールへ渡す”構造にすると、長期運用で壊れにくいフローになります。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次