Power Automate の SQL Server コネクタ「Get rows (V2)」に OData 形式の日時フィルターを指定するとエラーになる——この現象はクラウド(Azure SQL)/オンプレミス(ゲートウェイ)双方で起こり得ます。本記事では原因を分解し、ストアドプロシージャを新設せずに実装できる現実的な代替策と、長期運用を見据えた設計・チューニングのベストプラクティスまでを丁寧に解説します。
問題の概要(再現と現象)
Power Automate の SQL Server コネクタ → Get rows (V2) アクションで、Filter Query に OData 形式の日時条件を設定すると、次のような入力で失敗することがあります。
column1 gt 2025-09-05T00:00:00Z and column2 lt 2025-09-06T00:00:00Z
よく見られる症状の例:
- 「型 ‘Edm.DateTimeOffset’ と ‘Edm.String’ の比較演算子が定義されていない」旨のエラー
- 「日付の解析に失敗」や「演算子が無効」などの OData 由来のメッセージ
- オンプレミス データ ゲートウェイ経由だと
Z(Zulu/UTC)指定や小数秒の扱いで失敗しやすい
同じ条件でも、Azure SQL 直結とオンプレミス(ゲートウェイ経由)で挙動が異なることがあり、環境差がトラブルの温床になりがちです。
なぜ日時フィルターが通らないのか(内部のギャップ)
Get rows (V2) の Filter Query は OData の構文に「近い」ものの、実装は各コネクタ/ドライバで部分的に異なります。特に以下のギャップが原因になりやすいポイントです。
- 書式の解釈差:
YYYY-MM-DDThh:mm:ssZ(UTC)や+09:00のオフセット付き、ミリ秒/小数秒の有無などで解釈が分かれる。 - 型マッピングの非対称:SQL の
datetime/datetime2/datetimeoffsetと、OData のEdm.DateTimeOffset/Edm.Dateの相互変換でズレが生じる。 - ゲートウェイ経由の変換:オンプレミスではドライバ層の日時解析・ロケール設定の影響を受けやすい。
- 最適化の制約:OData 側で関数(
year()等)を使うと SQL に非 SARGable(インデックス非活用)な式が投下され、性能悪化やタイムアウトを招く。
列型 × リテラル × 結果の傾向(簡易マトリクス)
| SQL列型 | ODataリテラル例 | 典型的な結果 | リスク/備考 |
|---|---|---|---|
datetime | 2025-09-05T00:00:00Z | 失敗しがち | 古い型でタイムゾーン情報なし。Z 指定で不一致や桁あふれが起きることがある。 |
datetime2 | 2025-09-05T00:00:00 | 環境により可/不可 | 小数秒桁の差異・オフセット有無でばらつき。クォート有無の差で挙動が変わる報告あり。 |
datetimeoffset | 2025-09-05T00:00:00+09:00 | 比較的安定 | OData と親和性が高いが、ゲートウェイ側の解析実装に依存する場合がある。 |
date | '2025-09-05' または 2025-09-05 | 安定しやすい | 「日付だけ」の比較は OData 的にも扱いやすく、障害回避として有効。 |
解決策の比較(早見表)
| 解決策 | 内容 | メリット | デメリット/注意点 | 推奨度 |
|---|---|---|---|---|
| ① Execute a SQL query (V2) | パラメータ化クエリで直接 T-SQL を実行。SELECT * FROM dbo.MyTable WHERE column1 >= @startDate AND column1 < @endDate | UTC/オフセットを正確に扱える。 インデックス活用(SARGable)。 SQLインジェクション耐性。 | オンプレミスはゲートウェイ設定が必要。 ガバナンスにより生SQL禁止の場合は不可。 | 高(第一候補) |
| ② 計算列(永続化)+インデックス | ALTER TABLE ... ADD DateOnly AS CONVERT(date, column1) PERSISTED; | OData の日付比較で安定。 既存インデックスと両立。 | スキーマ変更が必要。 列追加の審査・リリース手続きが発生。 | 高(長期運用向け) |
| ③ ビューで日付専用列を公開 | CREATE VIEW ... AS SELECT CONVERT(date, column1) AS DateOnly ... | 本番テーブルを直接変更しない。 | 非 SARGable になる可能性。 維持管理コスト。 | 中(制約下の代替) |
| ④ 取得後に Flow 側で Filter array | まず全件(もしくは Top)取得後にフローでフィルタリング。 | 実装最短。コネクタ差異の影響を受けにくい。 | 件数が多いと遅い・コスト増。 スロットリングのリスク。 | 低〜中(小規模のみ) |
| ⑤ year()/month()/day() を使う暫定策 | year(column1) eq 2025 and month(column1) eq 9 ... | OData だけで完結。 | 非 SARGable で遅い。 境界条件の漏れやすさ。 | 低(緊急回避のみ) |
解決策①:Execute a SQL query (V2) でパラメータ化する
基本のパターン(UTC で半開区間)
-- 期間開始(含む)〜期間終了(含まない)の半開区間が原則
SELECT *
FROM dbo.MyTable
WHERE column1 >= @startDate -- 例: 2025-09-05T00:00:00Z
AND column1 < @endDate; -- 例: 2025-09-06T00:00:00Z
半開区間(終了を「含まない」)にすることで、ミリ秒・小数秒の丸め誤差や「23:59:59.999 の取りこぼし」を確実に避けられます。Power Automate 側では次のような式でパラメータ値を作れます。
@{formatDateTime(utcNow(),'yyyy-MM-ddT00:00:00Z')}@{formatDateTime(addDays(utcNow(),1),'yyyy-MM-ddT00:00:00Z')}
列型が datetime2 の場合
@startDate/@endDate を datetime2 として解釈させるため、ISO 8601 形式(Z 付きでも可)を渡します。必要に応じて SQL 側で明示キャストして安全側に倒します。
SELECT *
FROM dbo.MyTable
WHERE column1 >= CAST(@startDate AS datetime2(7))
AND column1 < CAST(@endDate AS datetime2(7));
列型が datetimeoffset の場合(タイムゾーンを使う)
SELECT *
FROM dbo.MyTable
WHERE column1 >= CAST(@startDate AS datetimeoffset)
AND column1 < CAST(@endDate AS datetimeoffset);
ローカル時刻で日付指定したい場合は、アプリ側(Flow)で convertTimeZone() を用いて UTC に変換してから渡すと一貫性が保てます。
ベストプラクティス(SQL クエリ)
- 必ずインデックス列に対し「列 >= パラメータ AND 列 < パラメータ」の形にする(左右を関数でラップしない)。
- 必要に応じて
ORDER BY column1を付与し、ページング利用時の順序不安定を防ぐ。 - 大量データではキーセットページング:
WHERE (column1 >= @startDate AND column1 < @endDate) AND id > @lastId ORDER BY id - 権限は
SELECT最小限にし、書込権限を与えない。
Power Automate の実装手順(要点)
- 「SQL Server」コネクタの「Execute a SQL query (V2)」を追加。
- クエリに
@startDate・@endDateを記述。 - 「パラメータ」に動的コンテンツ(上記の
formatDateTime())を割当。 - オンプレミスの場合、ゲートウェイを最新に更新し、接続文字列の暗号化やタイムゾーン設定を確認。
解決策②:計算列(永続化)+インデックスで OData を「日付だけ」にする
Get rows (V2) の日時比較が不安定でも、「日付のみ」の比較は安定しやすいです。そこで、日付を永続化した計算列を作り、インデックスを張って OData 側で日付範囲を指定します。
ALTER TABLE dbo.MyTable
ADD DateOnly AS CONVERT(date, column1) PERSISTED;
CREATE INDEX IX_MyTable_DateOnly ON dbo.MyTable(DateOnly);
以降、Filter Query の例:
DateOnly ge 2025-09-05 and DateOnly lt 2025-09-06
環境によってはクォートの有無('2025-09-05' vs 2025-09-05)で挙動が変わることがあります。安定しない場合は双方を試し、統一ルールをチーム内で明文化するとよいでしょう。
UTC とローカル時刻の整合
保存値が UTC の datetime2 で、ローカル日付で検索したい場合は、計算列でタイムゾーン変換を固定してから date 化することもできます。
-- 例:JST 基準の DateOnlyJst を永続化
ALTER TABLE dbo.MyTable
ADD DateOnlyJst AS CONVERT(date, (column1 AT TIME ZONE 'UTC') AT TIME ZONE 'Tokyo Standard Time') PERSISTED;
CREATE INDEX IX_MyTable_DateOnlyJst ON dbo.MyTable(DateOnlyJst);
ローカル基準日付で OData フィルターを安定させたい要件に有効です。
解決策③:ビューで日付専用列を公開(本体を触れない場合)
本番テーブルに手を入れたくない場合は、ビューで date 列を公開して回避できます。
CREATE VIEW dbo.v_MyTable
AS
SELECT
key,
column1,
CONVERT(date, column1) AS DateOnly
FROM dbo.MyTable;
ただしビューの DateOnly は永続化されないため、検索式は非 SARGable になりがちです。性能要求が厳しい場合は解決策②(永続化)や①(パラメータ化)を選ぶのが安全です。
解決策④:いったん取得して Flow 側で絞る(小規模・短期向け)
件数が僅少なら、まず Get rows (V2) で取得し、Filter array アクションで日時条件を適用する方法があります。
@and(
greaterOrEquals(item()?['column1'],'2025-09-05T00:00:00Z'),
less(item()?['column1'],'2025-09-06T00:00:00Z')
)
ただし大量データでは遅延・スロットリング・ランコスト増のリスクが高く、恒常運用には不向きです。暫定策として割り切り、早期に①か②へ移行しましょう。
解決策⑤:OData の year()/month()/day() を使う暫定策
Filter Query に次のような式を使えば、OData 側だけで日付を構成できます。
year(column1) eq 2025 and month(column1) eq 9 and day(column1) eq 5
ただしこの方法は SQL に「CONVERT/DATEPART 相当の関数を列側に適用」する形で送られ、非 SARGable になりやすく、インデックスが効かずに遅くなります。緊急回避以外では推奨しません。
よくある落とし穴と対処
- 境界条件の取りこぼし:
終了時刻を「含む」にすると 23:59:59.997〜.999 の微小差で漏れがち。半開区間(<)に統一する。 - Z(UTC)とオフセット:
ゲートウェイ経由ではZの解釈で失敗するケースがある。+00:00を明示するか、Flow 側で UTC 化して素のyyyy-MM-ddTHH:mm:ssを渡す。 - 小数秒桁数:
datetime2(7)列に.0しか渡さない/逆に.1234567を渡すなど、桁差で比較が不整合になることがある。必要なら SQL 側でCAST(... AS datetime2(3))に丸める。 - 型の不一致:
列がdatetimeなのにdatetimeoffsetリテラルを渡す等で失敗。列の基準(UTC かローカルか)をチームで固定する。 - クォートの有無:
OData の実装差で、'2025-09-05'と2025-09-05のどちらかしか通らない環境がある。どちらが正と決め、テンプレ化する。
設計のベストプラクティス(長期運用・性能)
- 保存は UTC、表示でローカル化:
保存はdatetime2(推奨)またはdatetimeoffsetで UTC を原則に。表示やレポートでAT TIME ZONEを用いてローカル化。 - インデックス整備:
フィルターに使う列に単独(または先頭)キーのインデックスを作成。計算列方式なら計算列自体にインデックス。 - ページング戦略:
大量データはキーセットページング(WHERE id > @lastId)で再実行耐性と性能を確保。 - 監査・追跡:
フローから「実行 ID」等をパラメータで渡し、テーブルに書き出しておくと後追い調査が容易。
ケース別の実装レシピ
クラウド(Azure SQL)で UTC の 1 日分を取得
-- 2025-09-05 の 1 日分(UTC)
SELECT *
FROM dbo.MyTable
WHERE column1 >= @d0 -- 2025-09-05T00:00:00Z
AND column1 < @d1; -- 2025-09-06T00:00:00Z
オンプレミス(ゲートウェイ)で JST 基準の 1 日分を取得(列は UTC)
-- 列: datetime2(3) UTC 保存
-- パラメータ: JST の 0:00 と翌日 0:00 を Flow で作り、SQL で UTC に変換
SELECT *
FROM dbo.MyTable
WHERE column1 >= (CAST(@jstStart AS datetimeoffset) AT TIME ZONE 'UTC')
AND column1 < (CAST(@jstEnd AS datetimeoffset) AT TIME ZONE 'UTC');
OData で日付だけフィルターする(計算列利用)
DateOnly ge 2025-09-05 and DateOnly lt 2025-09-06
Power Automate:式サンプル集(コピペ可)
| 用途 | 式 | メモ |
|---|---|---|
| 今日 0:00 UTC | @{formatDateTime(utcNow(),'yyyy-MM-ddT00:00:00Z')} | 開始境界に。 |
| 明日 0:00 UTC | @{formatDateTime(addDays(utcNow(),1),'yyyy-MM-ddT00:00:00Z')} | 終了境界に(半開区間)。 |
| 任意タイムゾーン開始(JST 例) | @{formatDateTime(convertTimeZone(utcNow(),'UTC','Tokyo Standard Time'),'yyyy-MM-ddT00:00:00')} | 必要なら Z 付与か SQL で UTC 化。 |
| Filter array(ISO 8601 比較) | @and(greaterOrEquals(item()?['column1'],variables('startUtc')),less(item()?['column1'],variables('endUtc'))) | 小数秒を含む場合はフォーマット統一。 |
トラブルシューティング・チェックリスト
- 列のデータ型は?
datetimeならdatetime2への移行を検討。 - 保存の基準は UTC ? ローカル? チーム内ルールが明文化されているか。
- OData のリテラルはクォート要/不要? プロジェクト内で統一されているか。
- 小数秒の桁合わせはできているか(
datetime2(3)で固定するなど)。 - インデックスは有効に使われているか(実行計画でスキャンになっていないか)。
- オンプレはゲートウェイのバージョン・接続方式(暗号化/経路)を最新・適正に保てているか。
FAQ
Q. Get rows (V2) の日時フィルターが通らないのは仕様ですか?
A. 仕様というより、OData 側の書式と SQL 側の型・変換の「ギャップ」で失敗しやすい箇所がある、というのが実態です。安定を重視するなら①(パラメータ化)か②(計算列)を選びましょう。
Q. Z(UTC)を付けると失敗します。
A. 環境により Z の解析で失敗することがあります。+00:00 を使う/Z を外す/Flow 側で UTC 文字列を作り SQL で型化する、などで回避できます。
Q. 文字列のまま比較しても良いですか?
A. 文字列比較は桁/書式の揺れで誤判定の元です。SQL 側で datetime2/datetimeoffset にキャストし、列側に関数をかけない条件式にしましょう。
Q. year()/month()/day() で十分では?
A. 簡便ですが非 SARGable で遅く、規模が大きくなると破綻します。緊急時以外は避けてください。
Q. ストアドプロシージャは本当に不要?
A. 多くのケースで不要です。①のパラメータ化クエリで十分実現できます。組織ポリシーで生 SQL 禁止なら、ビューや SP の採用を検討してください。
Q. OData の日付はシングルクォートが必要?
A. 実装差があります。クォート有/無のどちらが通るかを確認し、プロジェクト標準を固定してください。
まとめ:最短で成果を出す選び方
| 前提/制約 | 推奨アプローチ | 理由 |
|---|---|---|
| クラウド接続可/生 SQL 可 | ① パラメータ化クエリ | 最小変更・高性能・高安定。UTC/オフセットの整合を確保しやすい。 |
| DB スキーマ変更可/性能重視 | ② 計算列(永続化)+インデックス | OData の日付比較で安定。Get rows (V2) を継続利用可能。 |
| 本体を触れない/運用許容 | ③ ビューで日付公開 | 暫定回避。性能要件次第で②へ移行。 |
| 小規模・短期間 | ④ 取得後に Filter array | 実装最短。件数増に弱い点だけ注意。 |
| 緊急のつなぎ | ⑤ year()/month()/day() | 一時対応のみ。長期運用は非推奨。 |
サンプル一式(コピペして試せる)
テーブルとデータ
CREATE TABLE dbo.MyTable
(
id bigint IDENTITY(1,1) PRIMARY KEY,
column1 datetime2(3) NOT NULL, -- 例:イベント開始UTC
column2 datetime2(3) NULL, -- 例:イベント終了UTC
payload nvarchar(200) NULL
);
INSERT INTO dbo.MyTable(column1, column2, payload) VALUES
('2025-09-04T23:59:59.900', '2025-09-05T01:00:00.000', N'pre'),
('2025-09-05T00:00:00.000', '2025-09-05T02:00:00.000', N'start'),
('2025-09-05T23:59:59.999', '2025-09-06T00:30:00.000', N'end'),
('2025-09-06T00:00:00.000', NULL, N'next');
① パラメータ化クエリ例(半開区間)
SELECT id, column1, column2, payload
FROM dbo.MyTable
WHERE column1 >= @startDate -- 2025-09-05T00:00:00Z
AND column1 < @endDate; -- 2025-09-06T00:00:00Z
② 計算列+インデックス
ALTER TABLE dbo.MyTable
ADD DateOnly AS CONVERT(date, column1) PERSISTED;
CREATE INDEX IX_MyTable_DateOnly ON dbo.MyTable(DateOnly);
Filter Query:
DateOnly ge 2025-09-05 and DateOnly lt 2025-09-06
③ ビューで日付公開
CREATE VIEW dbo.v_MyTable
AS
SELECT id, column1, column2, payload, CONVERT(date, column1) AS DateOnly
FROM dbo.MyTable;
運用ノート(品質と保守性)
- テストデータで境界検証:
00:00:00.000と23:59:59.999、小数秒あり/なしのパターンを最低限カバー。 - ロケール・タイムゾーンの固定:Power Automate の環境設定/式で明示的に UTC を扱い、SQL 側で必要に応じてローカル化。
- コードの再利用:クエリ断片・式をソリューション内のテンプレートとして共有し、二重管理を避ける。
- 監視:実行時間・取得件数・再試行回数をカスタム ログに集計し、しきい値超過で通知。
結論
「Get rows (V2) に OData の日時条件が通らない」問題は、OData と SQL の型・書式のすれ違いが主因です。最優先は① Execute a SQL query (V2) によるパラメータ化。これが組織制約で難しい場合は、② 計算列(永続化)+インデックスで OData を「日付だけ」に寄せて安定化するのが現実解です。短期の暫定や小規模には④、緊急回避には⑤を用いつつ、将来的には①または②に一本化する方針が、品質・性能・保守性の観点で最も費用対効果に優れます。

コメント