1. はじめに
Simple Data Integrator(SDI) は、データソース間でのデータ連携を実現するツールですが、メール通知機能とスケジューラ機能を組み合わせることで、各種レポートを定期的に自動配信することも可能です。
本記事では、以下のような仕組みの実装手順を紹介します。
先月分の売上データを自動で集計し、読みやすいレイアウトのレポートを月初に自動配信する仕組み。
2. 全体像(仕組みとデータ前提)
2.1 仕組み
- (A) SQL:DB上の売上データを集計し、HTMLの行として出力
-
(B) HTMLテンプレ:
{outputFile}変数に (A) の出力をそのまま差し込み - (C) スケジューラ:毎月決まった日時に SQL → メール送信 を順番実行
2.2 データ前提
売上テーブル
CREATE TABLE "FACT_SALES_DAILY"
( "DATE_KEY" DATE NOT NULL ENABLE,
"STORE_ID" VARCHAR2(32 BYTE) COLLATE "USING_NLS_COMP" NOT NULL ENABLE,
"PRODUCT_ID" VARCHAR2(64 BYTE) COLLATE "USING_NLS_COMP" NOT NULL ENABLE,
"QTY" NUMBER(18,4) DEFAULT 0 NOT NULL ENABLE,
"NET_SALES" NUMBER(18,2) DEFAULT 0 NOT NULL ENABLE,
"TRANSACTIONS" NUMBER(10,0) DEFAULT 0 NOT NULL ENABLE,
"CUSTOMERS" NUMBER(10,0) DEFAULT 0 NOT NULL ENABLE,
CONSTRAINT "PK_FACT_SALES_DAILY" PRIMARY KEY ("DATE_KEY", "STORE_ID", "PRODUCT_ID")
}
商品テーブル
CREATE TABLE "DIM_PRODUCT"
( "PRODUCT_ID" VARCHAR2(64 BYTE) COLLATE "USING_NLS_COMP",
"PRODUCT_NAME" VARCHAR2(200 BYTE) COLLATE "USING_NLS_COMP" NOT NULL ENABLE,
"CATEGORY" VARCHAR2(100 BYTE) COLLATE "USING_NLS_COMP",
"SUBCATEGORY" VARCHAR2(100 BYTE) COLLATE "USING_NLS_COMP",
"BRAND" VARCHAR2(100 BYTE) COLLATE "USING_NLS_COMP",
"UOM" VARCHAR2(20 BYTE) COLLATE "USING_NLS_COMP",
PRIMARY KEY ("PRODUCT_ID")
)
2.3 マッピング処理フロー
3. 実装手順
3.1 コネクション設定
まず、Source および Target への接続を設定します。
本例では、Source は Oracle Database、Target は CSV ファイル です。
設定手順の詳細については、以下の記事を参照してください:
コネクション設定 (ダイレクト接続)
| ソースコネクション名 | ターゲットコネクション名 |
|---|---|
oracle_sales
|
CSV_EXPORT
|
3.2 マッピング設定
3.2.1 .「新規マッピング」ボタンをクリックして、マッピング設定画面を開きます
3.2.2 . ソースコネクション、クエリ、ターゲットコネクション、および CSV ファイル名を設定します
| No | 項目 | 説明 |
|---|---|---|
| 1 | マッピング名 | マッピング名を入力します。管理上ユニークな名前の設定を推奨します。 |
| 2 | ソースコネクション | oracle_salesを選択 |
| 3 | タイプ | クエリを選択 |
| 4 | クエリのSQL詳細 | レポート作成用の SQL を入力します。 ※1 |
| 5 | コネクションコネクション |
CSV_EXPORTを選択 |
| 6 | CSVファイル名 | 出力する CSV ファイル名を入力します。 |
| 7 | カラム一覧取得 |
カラム一覧取得ボタンをクリックし、テーブル定義明細を取得します。 |
| 8 | マッピング対象 | 出力対象とするカラムを選択します。 |
※1 レポート作成用のSQL
WITH t AS (
SELECT
fsd.PRODUCT_ID,
MAX(dp.PRODUCT_NAME) AS PRODUCT_NAME,
ROUND(SUM(fsd.NET_SALES)) AS SALES,
SUM(fsd.QTY) AS QTY
FROM FACT_SALES_DAILY fsd
JOIN DIM_PRODUCT dp ON dp.PRODUCT_ID = fsd.PRODUCT_ID
WHERE fsd.DATE_KEY >= TRUNC(ADD_MONTHS(SYSDATE, -1), 'MM')
AND fsd.DATE_KEY < TRUNC(SYSDATE, 'MM')
GROUP BY fsd.PRODUCT_ID
),
kpi AS ( -- KPIカード用の合計
SELECT
SUM(SALES) AS SUM_SALES,
SUM(QTY) AS SUM_QTY
FROM t
),
topn AS ( -- TOP5をここで確定させる(サブクエリ内でORDER/FETCH)
SELECT
-- HTMLエスケープ(& < > " ')
PRODUCT_NAME AS NAME_HTML,
SALES,
QTY
FROM t
ORDER BY SALES DESC
FETCH FIRST 5 ROWS ONLY
)
-- 1行目:カード(売上合計)
SELECT
'<div class="section">
<div class="kpis">
<div class="card">
<h4>売上合計</h4>
<p class="v">' || TO_CHAR(SUM_SALES, 'FM999G999G999') || '</p>
<div class="delta muted">数量:' || TO_CHAR(SUM_QTY, 'FM999G999G999') || '</div>
</div>
</div>
</div>' AS ROW_HTML
FROM kpi
UNION ALL
-- 2行目 商品テーブル Head
SELECT
'
<div class="section">
<div class="hr"></div>
<h3 style="margin:0 0 10px 0;color:#0f172a">TOP 5 商品</h3>
<p class="muted text-right" style="margin:0 0 10px 0;" >並び順:売上金額(降順)</p>
<table role="presentation" aria-hidden="true">
<thead>
<tr>
<th style="width:56%">商品名</th>
<th style="width:22%" class="num">売上(税抜)</th>
<th style="width:22%" class="num">数量</th>
</tr>
</thead>
<tbody>
' AS ROW_HTML
FROM DUAL
UNION ALL
-- 3行目以降:TOP5の商品行(<tr>…</tr>)
SELECT
'<tr><td>' || NAME_HTML ||
'</td><td class="num">' || TO_CHAR(SALES, 'FM999G999G999') ||
'</td><td class="num">' || TO_CHAR(QTY, 'FM999G999G999') ||
'</td></tr>' AS ROW_HTML
FROM topn
-- 最後Footer
UNION ALL
SELECT
'
</tbody>
</table>
</div>
<div class="foot">
<div class="hr"></div>
<p class="tiny">
このメールはシステムから自動送信されています。
</p>
</div>
</div>
' AS ROW_HTML
FROM DUAL
3.3 . メール送信設定
3.3.1 「詳細設定」ボタンをクリックして、詳細設定画面を開きます
3.3.2 処理フロー画面上の「処理成功時処理」で Yes を選択します。その下の「詳細設定」ボタンをクリックし、メール送信内容を設定します。
3.3.3 メール内容の設定
注意
メール送信を行うためには、送信メールサーバー(SMTP) の設定が必要です。
詳細な設定手順については、以下のページを参照してください。
https://ja.sdi.ricct.com/userguide/mailsetting/
| No | 項目 | 説明 |
|---|---|---|
| 12 | メール送信 | メール送信をチェックします |
| 13 | メール詳細 | 送信先、件名、本文などを設定します。 |
件名:
月間売上サマリー({DateTime.Now.AddMonths(-1).ToString("yyyy年M月")})
本文:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>{DateTime.Now.AddMonths(-1).ToString("yyyy年M月")}月間売上サマリー</title>
<style>
/* ---- 基本 ---- */
body{margin:0;padding:0;background:#f6f7fb;}
.wrap{max-width:720px;margin:0 auto;background:#ffffff;border-radius:14px;
box-shadow:0 4px 18px rgba(15,23,42,.06);overflow:hidden}
.head{padding:22px 26px 8px 26px;background:#0f172a;color:#fff}
.title{margin:0;font-size:20px;line-height:1.35;font-weight:700}
.sub{margin:6px 0 0 0;font-size:12px;opacity:.85}
.muted{color:#64748b;font-size:12px}
.text-right{text-align:right;}
.section{padding:18px 22px}
.hr{height:1px;background:#eef2f7;margin:14px 0}
/* ---- 合計カード ---- */
.kpis{display:flex;gap:10px}
.card{flex:1;border:1px solid #eef2f7;border-radius:12px;padding:12px 12px 10px}
.card h4{margin:0 0 6px 0;color:#64748b;font-size:12px;font-weight:600;letter-spacing:.2px}
.card .v{margin:0;font-size:22px;font-weight:800;color:#0f172a;letter-spacing:.2px}
.delta{font-size:12px;margin-top:4px}
.up{color:#16a34a;font-weight:700}
.down{color:#dc2626;font-weight:700}
/* ---- テーブル ---- */
.table-wrap{overflow:auto;-webkit-overflow-scrolling:touch}
table{border-collapse:collapse;width:100%;min-width:520px}
th,td{border:1px solid #eef2f7;padding:10px 10px;text-align:left;font-size:13px}
th{background:#f8fafc;color:#0f172a}
tr:nth-child(even){background:#fcfdff}
th.num, td.num{ text-align:right; font-variant-numeric: tabular-nums; }
/* ---- バッジ ---- */
.badge{display:inline-block;padding:2px 8px;border-radius:999px;font-size:11px}
.b-primary{background:#e3f2fd;color:#0b6aa3}
/* ---- CSVリンク(ボタン風) ---- */
.btn{display:inline-block;padding:8px 12px;border-radius:8px;background:#0b6aa3;color:#fff;
text-decoration:none;font-size:13px}
/* ---- フッタ ---- */
.foot{padding:14px 22px 24px 22px}
.tiny{font-size:11px;color:#94a3b8}
@media (max-width:560px){
.kpis{display:block}
.card{margin-bottom:8px}
}
</style>
</head>
<body>
<div class="wrap">
<!-- ヘッダ -->
<div class="head">
<div class="badge b-primary">{DateTime.Now.AddMonths(-1).ToString("yyyy年M月")}</div>
<h1 class="title">{DateTime.Now.AddMonths(-1).ToString("yyyy年M月")}月間売上サマリー</h1>
<p class="sub">対象期間:{DateTime(today.Year, today.Month, 1).AddMonths(-1).ToString("yyyy年M月d日")}
–{DateTime(today.Year, today.Month, 1).AddDays(-1).ToString("yyyy年M月d日")}</p>
</div>
{outputFile}
</body>
</html>
メール設定画面および処理フロー画面で 「OK」 ボタンをクリックし、マッピング設定画面に戻ります。
4 . テスト実行
「マッピング実行」 ボタンをクリックして、動作を確認します。

設定した内容のメールが正しく送信されることを確認します。
5 . 保存
「保存」 ボタンをクリックして、マッピング設定を保存します。

6 . スケジューラーへの登録
毎月の月初にメールを自動送信する場合は、スケジューラーを作成してマッピングを登録します。
スケジューラーの設定方法については、以下のページを参照してください。
スケジューラ設定
7. 横展開
本記事では月次売上サマリーを例にしましたが、同じ構成(①SQLでHTML断片生成 → ②テンプレ差し込み → ③スケジューラ配信)で、以下のレポートにそのまま横展開できます。
7.1 期間違いのサマリー
-
週次売上サマリー:期間を「先週の月〜日」に変更
- 件名例:
週間売上サマリー({DateTime.Today.AddDays(-7).ToString("yyyy年M月d日")}週) - スケジュール:毎週月曜 9:00
- 件名例:
-
売上日報:
DATE_KEY = TRUNC(SYSDATE-1)などで前日分- 件名例:
売上日報({DateTime.Today.AddDays(-1).ToString("yyyy年M月d日")}) - スケジュール:毎朝 8:00
- 件名例:
7.2 切り口違いのランキング/集計
-
部門別 / 店舗別:
GROUP BY 部門ID(または店舗ID)を追加し、表見出しを差し替え- 件名例:
店舗別・月次売上サマリー({DateTime.Now.AddMonths(-1).ToString("yyyy年M月")})
- 件名例:
-
担当者別 / ブランド別:
GROUP BYのキーを入れ替え、TOP N をORDER BYで制御
7.3 アラート系(しきい値通知)
-
在庫アラート:在庫率や安全在庫を下回る商品のみ 生成
- 件名例:
在庫アラート({DateTime.Today.ToString("yyyy/MM/dd")}) - 並び順:在庫率の昇順、ステータス(危険/注意/良好)に応じてバッジ色を変更
- 件名例:
-
欠品リスク:売上推移×在庫日数(= 在庫 / 平均販売数量)で閾値判定
- 表示:リードタイム、想定欠品日、代替品リンクなど
7.4 配信の工夫
- セグメント配信:部門長には部門別、店舗責任者には店舗別だけを配信(宛先リストを分割)
- CSV添付 / DLリンク:SQL結果をCSV出力して添付、あるいはオブジェクトストレージのURLを本文ボタンに設定
-
多言語対応:
{DateTime(..., "ja-JP")}/"en-US"で件名・本文の日付ローカライズ
以上です。
SDIのインストール、設定、活用方法に関するその他の情報については、下記のページをご参照ください。
Simple Data Integrator (SDI) 実践入門









