Adobe Campaignクエリー道場(4)〜HAVING句でブラボー！

[前回](https://qiita.com/JJSlider/items/547d5348b647506ff846 "Adobe Campaignクエリー道場(3)〜最新日付でビバノンノン")の最後に、以下のような宿題をお出ししました:

> 初めてCampaignからのメールを受け取ったのが特定の日付(たとえば2022年4月1日)以降であるアドレスと、その初回受信日時とを抽出してください。

じつはこれ、実際のお客様からいただいたご質問の一部[^1]。現実のビジネス要件をAdobe Campaignのクエリーが解決した好例としてぜひご紹介したかったケースです。

みなさんお考えいただけたでしょうか？　本シリーズをずっとお読みだったら余裕だったかもしれませんね。でも、できなかったとしても大丈夫。いっしょに以下、見ていきましょう。
* * *
まずいつものように「クエリ」アクティビティの作成。これまでと同様、アドレスに対するメール履歴について関心があるのでディメンジョンは「受信者配信ログ」です。
フィルター条件を設定するのですが、ここに特定日付を指定しようとしちゃった方、いませんか？
![2022-12-06_15-19-38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2516149/3f8bd7d3-78e5-a5b7-8ec8-27a0509469fc.png)
はいアウト！　要件が「特定日付以降に受け取ったメールのうちの最初」であればこれでいいんですが、題意は「最初に受け取ったメールが特定日付以降」です。たとえばあなたが3月30日に初メールを、4月3日に次メールを受け取っていたとします。上掲図条件だとあなたが受けた次メールがマッチしてしまいますが、3月に初メールを受け取っているのでほんとはマッチさせたくないんですよね。というわけで日付を指定するのはここじゃありません。フィルター条件としては前回と同じく、ステータスが送信済みであるとだけしておきましょう:
![2022-11-01_16-21-20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2516149/b64f10b7-0a07-4620-9520-7c0a4c6f005a.png)
ではどこで日付を指定するのか。ここで[前々回](https://qiita.com/JJSlider/items/da6b567d13adb5f5500a "Adobe Campaignクエリー道場(2)〜重複値を探す")を思い出してください。どうしたかというと──画面下部の「データグループ化句を追加」をチェック:
![add-group-by-clause.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2516149/3b60f4bd-3b70-0c10-c4ef-394a0e5e7060.png)
「グループ化の条件」で「追加」をクリック。「式」の上をクリックし、「Min(＠eventDate)」と手入力します[^2]:
![2022-12-06_16-06-08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2516149/f9a27fa8-d5ec-6997-882d-54c7df2a37be.png)
「初回受信日付」がほしいので、集計関数「Min()」により日付の最小値を取ろうというわけです。
続いて「演算子」で「次より大きいか等しい」を選択[^3]:
![2022-12-06_16-10-48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2516149/e281f58e-337d-04fc-36c2-a2036f00ea5d.png)
「値」に条件の特定日付を与えます。ここでは2022年4月1日とするので文字列で「'2022-04-01'」と入力しましょう[^4]:
![2022-12-06_16-38-31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2516149/1a5cc99f-fe4e-729c-5227-0a4d5bdd758c.png)
えっ文字列でいいの？　そう思ったあなたは鋭い！　その感覚、大事にしてください。でもこの場合、Campaignがよしなに判断してくれるので、「'2022-04-01'」とか「'2022/04/01'」とかでＯＫなのです[^5]。ただ１点、単一引用符「'」で囲むことを注意してくださいね。
以上により「グループ化の条件」が指定できたら「完了」をクリックして「クエリ」画面に戻ります。そうしたら例によって、「データを追加...」をクリック:
![2022-12-06_17-25-44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2516149/41f5dcac-d7fa-a00b-5a25-2c7fcbfd9265.png)
「追加データ」画面まで遷移して「使用可能フィールド」で「アドレス」をダブルクリックし「出力列」に追加。「グループ」のチェックボックスをチェックして「はい」にします:
![2022-12-06_17-35-11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2516149/804752a7-6cca-61d0-2390-bda79ba8ee3f.png)
「イベントの日付」もダブルクリックして「出力列」に追加したら、「式」カラムをクリックして「Min(＠eventDate)」と手編集します:
![2022-12-06_17-40-47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2516149/a45f8122-0383-0d68-6a5d-81be709e513b.png)
あれ、「Min(＠eventDate)」ってさっきもやったじゃん──はい、そうですね。でも、やるんだよ！　理由はあとでわかります。
いったん「完了」で戻り「追加データを編集...」をクリック:
![2022-12-06_17-48-54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2516149/3037f4e8-8225-ed36-2578-bdfab18aad9f.png)
やはり例によって「詳細設定パラメーター...」をクリック:
![2022-12-06_17-53-17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2516149/2aabec73-09eb-ef52-f1b6-ebc1c5b2810a.png)
いつものように「重複行を削除(DISTINCT)」をアンチェック、「ターゲティングディメンジョンのプライマリキーの自動追加を無効にする」をチェック:
![2022-11-18_15-53-12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2516149/d884ae9f-2a88-5c9f-162f-cfc9379167ec.png)
「OK」を続けてダイアログを閉じていきワークフローキャンバスに戻って完成！ 今回も一時的に「2つの実行間の中間母集団の結果を保持」を有効化すると:
![2022-12-06_18-43-15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2516149/ef44f249-6b4d-8adc-6c1e-a01edb540ad9.png)
初めてCampaignからのメールを受け取ったのが2022年4月1日以降であるアドレスとその初回受信日時とを抽出できました！

ここで毎度の、生SQLを見てみましょう:
```
SELECT   B0.sAddress, Min(B0.tsEvent) FROM NmsBroadLogRcp B0 WHERE (B0.iStatus = 1) AND ((B0.iBroadLogId > 0 OR B0.iBroadLogId < 0)) GROUP BY B0.sAddress HAVING (Min(B0.tsEvent) >= N'2022-04-01')
```
何をやってるかというと:
1. アドレスごとにデータを束ね(GROUP BY B0.sAddress)
1. 最小の日付を取り(SELECT B0.sAddress, Min(B0.tsEvent))
1. それが特定日付以降のものを選ぶ(HAVING (Min(B0.tsEvent) >= N'2022-04-01'))

１番の「GROUP BY B0.sAddress」は「追加データ」画面で「アドレス」の「グループ」をチェックしたグループ化の効果です。２番のSELECT句の「Min(B0.tsEvent)」は「追加列」画面で手編集した「Min(＠eventDate)」。３番の「HAVING (Min(B0.tsEvent) >= N'2022-04-01')」は、フィルター条件指定画面で「データグループ化句を追加」して指定したものです。手順中「Min(＠eventDate)」を２回も指定して変じゃね？　と思われたかもしれませんが、SQL上SELECT句とHAVING句との両方に「Min(B0.tsEvent)」が必要だったからでした。

SQLの組み立て順とクエリー作成ステップの流れとが合っていなかったり、「HAVING」という言葉がインターフェイス上あらわれていなかったりと、直観的にはいかないところがなかなか曲者でしたね。ぜひクエリー手順とSQLとを対照させて熟読ガン見してください。
* * *
いかがでしょうか。「データグループ化句を追加」や集計関数など、本シリーズですでにご紹介済みのテクニックの総まとめで与えられたビジネス要件を解決できることがおわかりになったかと思います。

自力で正解だった方、ＧＪ！　残念だった方、Ｗ杯日本チームと同様、健闘を讃えます。挑戦が明日の糧になる──次回の栄光に向けて、Campaignクエラーへの道をさらに共に歩んでいきましょう!!

::: note warn
本稿の内容は筆者のオンプレミス型デモ環境(Adobe Campaign Classic 9342@6583a8a・PostgreSQL 11.14)上で実施した検証に基づきます。別環境における同様の動作を保証するものではありません。またデータは架空のものであり、既存の配信や実在の組織とはいっさい関係がありません。
:::
[^1]: もちろん特定できないよう、具体的なデータや条件は改変しています。
[^2]: 前々回のように「式を編集」ボタンをクリックして画面画から「最小値」集計関数を選んでもＯＫです。
[^3]:「式」が日付型を返すとCampaignがいったん認識すれば、「演算子」の選択肢表示が「以降」に変化します。表示は異なりますが意味は同じです。
[^4]: 注３の状況では「値」カラムにカレンダーコントロールが表示され、最初から日付型として指定可能になります。
[^5]: 本稿のデータソースPostgreSQL環境では、「'20220401'」「'April 1, 2022'」「'01-Apr-22'」などでもＯＫでした。とはいえお勧めは「'2022-04-01'」です。