kintone の一覧は「レコードを並べる」ためのものです。小計を挟む、順位を振る、目標と突き合わせる、階層をたどる — このあたりから先は、標準の一覧では手が出ません。
kSQL Dashboard Pro のデータ表は、SQL が返した結果をそのまま表にします。列名がそのまま見出しになるので、集計の形を決めるのは SQL 側です。この記事では SQL・ペイン設定・表示結果を 1 セットにして、10 種類の表を作ります。SQL はすべてコピペで動きます。
kSQL Dashboard Pro デモ(全 5 回)
⓪ 準備編 / ① KPI カード / ② データ表(この記事) / ③ グラフ / ④ マークダウン**アプリの用意は ⓪ 準備編**にまとめてあります(5 本とも同じアプリを使います)。
- タブ「基本の 3 つ」
- タブ「SQL で作る集計」
- タブ「品質と見せ方」
前提
| 項目 | 本記事の前提 |
|---|---|
| プラグイン | kSQL Dashboard Pro Ver.1 |
| SQL エンジン | kintone-sql-tools 3.66.1 系 |
| kintone | 公式対応ブラウザの最新版(PC)。モバイルは対象外 |
| 対象アプリ | 売上明細(約 1,500 件)/ 月次目標(288 件)/ 商品分類マスタ(40 件) |
アプリの作り方とデータの入れ方は ⓪ 準備編にまとめてあります。 この記事では 3 アプリすべてを使います。
アプリ番号は環境ごとに変わります。 SQL 中の
APP4239などは、取り込み時の確認ダイアログ「アプリ番号の変換」でご自身の環境の番号へ変換できます。
表が返すべき列の形
KPI カードには「1 行しか使わない」という決まりがありましたが、表にはありません。
列名がそのまま見出しになる。明細でも集計でも、返した形がそのまま出る。
裏を返すと、表の見え方を決めるのは SQL です。小計を挟みたければ SQL で小計行を作り、順位を振りたければ SQL で順位を作ります。ペイン側の設定は「見せ方」— 書式・色・幅・集計行 — を担当します。
最小 — 明細をそのまま出す
SELECT 売上日, 顧客名, 商品名, 数量, 金額
FROM APP4239
WHERE 売上日 = THIS_MONTH()
ORDER BY 売上日 DESC
ペインの表示タイプは「表」を選びます(設定画面の表示タイプ欄。この記事では「データ表」と呼びます)。ほかの設定は要りません。列の型は結果から判定され、数値は右寄せ、日付は日付として表示されます。
例 1: 明細表 + 集計行 + レコードを開く
明細に集計行を付け、行からレコードを開けるようにします。
SELECT $id, 売上日, 顧客名, 商品カテゴリ, 商品名, 数量, 単価, 金額, 売上ステータス
FROM APP4239
WHERE 売上日 = THIS_MONTH()
ORDER BY 売上日 DESC
| 設定 | 値 |
|---|---|
| 表示密度 | コンパクト |
列の設定 → $id
|
非表示 |
| 列の設定 → 売上日 | 幅 100 / 日付 M/D
|
| 列の設定 → 単価・金額 | 数値・3 桁区切り |
| 集計行 | 数量 = 合計 / 金額 = 合計 |
| 条件付き書式 | 売上ステータス = 取消 → 注意・行全体
|
$id を返すと、行の左端にレコードを開くアイコンが出ます。 $id 自体は非表示にしてもアイコンは残ります。番号を見せる必要はないけれど遷移はさせたい、という形が普通なので。
集計行は「集計したい列を宣言する」方式
この表では 単価 に集計を指定していません。指定した列だけが集計され、指定の無い数値列は空欄になります。
単価の合計に意味はありません。順位・達成率・単価のような合算に意味のない列へ誤った合計を出さないための作りです。
totalsを 1 列も設定していない表では、従来どおり全数値列を合計します。1 列でも設定した時点で「宣言方式」に切り替わります。
集計行は元の全行から計算します。 後述の「上位 N + その他」でまとめても、合計・平均は変わりません。
例 2: Top 10 ランキング — RANK() を書かずに順位を出す
SELECT 担当者名, 部署, COUNT(*) AS 件数, SUM(金額) AS 売上
FROM APP4239
WHERE 売上日 = THIS_YEAR() AND 売上ステータス = '確定'
GROUP BY 担当者名, 部署
ORDER BY 売上 DESC
LIMIT 10
| 設定 | 値 |
|---|---|
| 行番号列 | オン |
| 列の設定 → 売上 | 右寄せ / 表示スケール 万 / 単位 円 |
| 集計行 | 件数 = 合計 / 売上 = 合計 |
行番号列をオンにすると、先頭に 1, 2, 3… が振られます。番号は表示順なので、列見出しで並べ替えると振り直されます(Excel の行番号と同じ感覚です)。
- 「その他」行・集計行には付きません(データ行ではないため)
- CSV 出力には含まれません(表示専用)
同順位を 1, 1, 3 と表現したいときだけ
RANK()を書きます。 単純な上位 N なら行番号列で足ります。次の例でRANK()を使うので、見比べてみてください。
例 3: ウィンドウ関数 — 順位・累積・前月
月次の推移に、順位・累計・前月を並べます。
CREATE TEMP TABLE #月次 AS
SELECT DATE_FORMAT(売上日, '%Y-%m') AS 年月, SUM(金額) AS 売上
FROM APP4239
WHERE 売上ステータス = '確定' AND 売上日 <= CURRENT_DATE()
GROUP BY DATE_FORMAT(売上日, '%Y-%m');
CREATE TEMP TABLE #キー付 AS
SELECT 年月, 売上, DATE_FORMAT(DATE_ADD(CONCAT(年月, '-01'), -1, 'MONTH'), '%Y-%m') AS 前月キー
FROM #月次;
SELECT c.年月, c.売上,
RANK() OVER (ORDER BY c.売上 DESC) AS 売上順位,
SUM(c.売上) OVER (ORDER BY c.年月 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS 累計,
p.売上 AS 前月
FROM #キー付 c LEFT JOIN #月次 p ON c.前月キー = p.年月
ORDER BY c.年月 DESC;
| 設定 | 値 |
|---|---|
| 列の設定 → 売上・累計・前月 | 右寄せ / 表示スケール 万 |
| 単位を見出しへ | オン |
| 条件付き書式 | 売上順位 <= 3 → 良好・太字 |
3 つのことをやっています。
① 順位は RANK()
RANK() OVER (ORDER BY 売上 DESC) で、同じ値には同じ順位が付きます。行番号列との違いはここです。
② 累積はフレームを明示する
SUM(c.売上) OVER (ORDER BY c.年月 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
ROWS BETWEEN … を省くと既定の RANGE になり、ORDER BY の値が同じ行はすべて同じ累計値になります。行ごとの積み上げがほしいならフレームを明示します。省くと警告も出ます。
③ 前月は LAG を使わない
前の行を取るなら LAG() が素直ですが、この形では警告が出ます。
前月 の ORDER BY は全順序でないため、同順内の前後関係は未規定です。
年月 は集計キーなので実際には一意ですが、CTE を経るとエンジンからは一意だと証明できません。無視してよい警告ではあるものの、閲覧者の画面に出るのは望ましくありません。
年月から「前月キー」を作って自己 JOIN すれば、順序に依存しないので警告そのものが出ません。
DATE_FORMAT(DATE_ADD(CONCAT(年月, '-01'), -1, 'MONTH'), '%Y-%m') AS 前月キー
2026-08 → 2026-08-01 にしてから 1 か月引き、また YYYY-MM に戻しています。値は LAG() と同じです。
ウィンドウ関数をやめる話ではありません。順位と累積はウィンドウ関数のままで警告は出ません。
LAGだけ置き換えます。
例 4: 小計・総計を SQL で作る — ROLLUP / GROUPING
SELECT
CASE WHEN GROUPING(大分類) = 1 THEN '総計' ELSE 大分類 END AS 大分類,
CASE WHEN GROUPING(大分類) = 1 THEN ''
WHEN GROUPING(商品カテゴリ) = 1 THEN '小計'
ELSE 商品カテゴリ END AS 商品カテゴリ,
SUM(金額) AS 売上,
CASE WHEN GROUPING(大分類) = 1 THEN 2
WHEN GROUPING(商品カテゴリ) = 1 THEN 1
ELSE 0 END AS 集計段
FROM APP4239
WHERE 売上日 = THIS_YEAR() AND 売上ステータス = '確定'
GROUP BY ROLLUP(大分類, 商品カテゴリ)
ORDER BY GROUPING(大分類), 大分類, GROUPING(商品カテゴリ), 売上 DESC
| 設定 | 値 |
|---|---|
| 列の設定 → 集計段 | 幅 90 / 中央 / 表示名「段」 |
| 条件付き書式 | 集計段 = 2 → 良好・行全体・太字(総計) |
| 条件付き書式 | 商品カテゴリ = 小計 → 良好・太字 |
GROUP BY ROLLUP(大分類, 商品カテゴリ) と書くと、明細に加えて大分類ごとの小計と全体の総計の行が増えます。
そのままでは小計と総計を見分けられない
小計・総計の行では、集計から外れたキーが空になります。 素直に書くと、小計も総計も - の行になって区別が付きません。
GROUPING(列) はその行でその列が集計から外れていれば 1 を返します。これを使ってラベルと段を作ります。
- 総計 →
大分類に「総計」 - 小計 →
商品カテゴリに「小計」 - 段 →
0明細 /1小計 /2総計
GROUPING同士は足せません。GROUPING(a) + GROUPING(b)は構文エラーになるので、CASEで段を作ります。
並び順も読み順に合わせる
ORDER BY GROUPING(大分類), 大分類, GROUPING(商品カテゴリ), 売上 DESC
GROUPING を先に置くと、小計・総計が必ずその段の末尾へ回ります。結果は「大分類ごとに 明細(売上の降順)→ 小計、最後に総計」という読み順になります。
ORDER BYにSUM(金額)は書けません。別名の売上を使います。
集計行との使い分け
ここが分かれ目です。
| ほしいもの | 使うもの |
|---|---|
| 総計だけを末尾に固定したい | ペインの集計行(例 1) |
| 階層の小計も欲しい / 小計を並べ替えの対象にしたい | SQL の ROLLUP |
集計行は「表の下に貼り付く 1 行」で、スクロールしても残ります。ROLLUP はデータそのものなので、並べ替えにも CSV 出力にも乗ります。
例 5: 構成比と「上位 N + その他」
CREATE TEMP TABLE #g AS
SELECT 商品カテゴリ, SUM(金額) AS 売上
FROM APP4239
WHERE 売上日 = THIS_YEAR() AND 売上ステータス = '確定'
GROUP BY 商品カテゴリ;
SET @total = (SELECT SUM(売上) FROM #g);
SELECT 商品カテゴリ, 売上, ROUND((売上 * 100) / @total, 1) AS 構成比
FROM #g
ORDER BY 売上 DESC;
| 設定 | 値 |
|---|---|
| 上位 N + その他 | 8 |
| 列の設定 → 売上 | 右寄せ / 万 / 円 |
| 列の設定 → 構成比 | 右寄せ / % / 小数 1 桁 |
| 集計行 | 売上 = 合計 |
| 条件付き書式 | 売上 → ヒートマップ |
構成比は全体をいったん変数に入れてから割ります。SET @total = (SELECT …) の複文です。
ペインの「上位 N + その他」と、SQL で作る「その他」
この例ではペインの機能を使っています。挙動に決まりがあります。
-
並べ替えはしません。 上から N 行を残し、残りを 1 行にまとめます。順序は
ORDER BYと初期ソートで決めます - 「その他」は列見出しで並べ替えても最下段に固定されます
- 集計行は元の全行から計算するので、まとめても合計は変わりません
- 「その他」行の各列は集計行と同じ集計で埋まります。集計を指定していない数値列は空欄です
① KPI カードで作った「金額付きのその他」は SQL 側の話でした。あちらはタイル表示に「他 N 件」しか出せない制約への対処です。表ではペインの機能で足ります — こちらは「その他」の行にもちゃんと合計が入るので。
例 6: 別アプリと突き合わせる — 目標達成率
SELECT s.担当者名, SUM(s.金額) AS 実績, MAX(g.目標金額) AS 目標,
ROUND((SUM(s.金額) * 100) / MAX(g.目標金額), 1) AS 達成率
FROM APP4239 s INNER JOIN APP4240 g ON s.担当者名 = g.担当者名
WHERE s.売上日 = LAST_MONTH() AND s.売上ステータス = '確定'
AND g.年月 = DATE_FORMAT(DATE_ADD(CURRENT_DATE(), -1, 'MONTH'), '%Y-%m')
GROUP BY s.担当者名
ORDER BY 達成率 DESC
| 設定 | 値 |
|---|---|
| 列の設定 → 実績・目標 | 右寄せ / 万 |
| 単位を見出しへ | オン |
| 集計行 | 実績 = 合計 / 目標 = 合計 |
| 条件付き書式 | 達成率 → ヒートマップ(赤青)・基準 100 |
JOIN ON に書ける等式は 1 つだけです。担当者で結び、月の絞り込みは WHERE 側に書きます。
MAX(g.目標金額) としているのは、明細 1 行ごとに目標行が付くためです。同じ担当者・同じ月の目標は 1 つなので、MAX でも MIN でも同じ値になります。
ヒートマップは「基準」で意味が変わる
達成率は 100 を境に意味が変わる列です。赤青(diverging) に 基準 = 100 を指定すると、100 より上が青、下が赤に塗り分けられます。
-
青/赤(既定)— 大きいほど濃い。「大きいほど悪い」列(遅延日数・不良件数)は赤 -
赤青— 基準の上下で意味が変わる列に使う。上側と下側をそれぞれ別に正規化するので、片側だけ幅が広くても狭い側の差が潰れません
なぜ「先月」なのか。 今月は月の途中なので全員が同じくらいの達成率になり、色が付きません。色の説明をする図としては、月が締まっている先月のほうが適切です。
例 7: 階層をたどる — WITH RECURSIVE
商品分類マスタは「親 → 子」の 1 レコードが 1 本の枝です。これを経路の文字列に展開します。
WITH RECURSIVE 階層木 AS (
SELECT 分類コード, 分類名, 親コード, 1 AS 深さ, 分類名 AS 経路
FROM APP4241
WHERE 親コード = ''
UNION ALL
SELECT c.分類コード, c.分類名, c.親コード, p.深さ + 1, CONCAT(p.経路, ' > ', c.分類名)
FROM 階層木 p INNER JOIN APP4241 c ON c.親コード = p.分類コード
)
SELECT 深さ, 経路, 分類コード
FROM 階層木
ORDER BY 経路
前半(UNION ALL の上)が出発点 — 親を持たない最上位です。後半が1 段掘る規則で、これが自分自身を参照します。
1 OA 機器
2 OA 機器 > PC 周辺機器
3 OA 機器 > PC 周辺機器 > 27 インチ液晶モニター
深さが何段になるかをあらかじめ知らなくてよいのが再帰の利点です。段数が決まっているなら自己 JOIN を並べるほうが速いですが、データ次第で変わるならこちらです。
再帰には歯止めがあります。深さ 100・累計 10,000 行・展開 100,000 回を超えると停止します(いずれも既定値)。循環している親子関係を入れても、無限には回りません。
例 8: データ品質チェック — 直すべき行だけ出す
集計の前に、そもそも直すべきデータを洗い出す表です。
SELECT $id, 伝票番号, 売上日, 顧客名, 商品名, 数量, 金額,
CASE WHEN 顧客名 = '' THEN '顧客名なし'
WHEN 数量 < 0 THEN '数量がマイナス'
WHEN 金額 = 0 THEN '金額ゼロ'
WHEN 売上日 > CURRENT_DATE() THEN '未来日付'
ELSE '' END AS 指摘
FROM APP4239
WHERE 顧客名 = '' OR 数量 < 0 OR 金額 = 0 OR 売上日 > CURRENT_DATE()
ORDER BY 指摘, 売上日
| 設定 | 値 |
|---|---|
| 条件付き書式 | 数量 < 0 → 重大・太字 |
| 条件付き書式 | 顧客名 が空 → 重大 |
$id を返しているので、指摘された行からそのままレコードを開いて直せます。「一覧に出す → 開く → 直す」が 1 画面で完結します。
条件付き書式の演算子には empty / notEmpty があり、空欄そのものを条件にできます。
このデモデータには、練習用に壊れたレコードを 22 件(全体の 1.5%)混ぜてあります。実際のアプリでも、まずこの表を作ってから集計に入ると手戻りが減ります。
例 9: 見せ方の総覧
最後に、表の見せ方をまとめて 1 枚に載せます。
CREATE TEMP TABLE #月次 AS
SELECT DATE_FORMAT(売上日, '%Y-%m') AS 年月, SUM(金額) AS 売上, COUNT(*) AS 件数
FROM APP4239
WHERE 売上ステータス = '確定' AND 売上日 <= CURRENT_DATE()
GROUP BY DATE_FORMAT(売上日, '%Y-%m');
CREATE TEMP TABLE #キー付 AS
SELECT 年月, 売上, 件数, DATE_FORMAT(DATE_ADD(CONCAT(年月, '-01'), -1, 'MONTH'), '%Y-%m') AS 前月キー
FROM #月次;
SELECT c.年月, c.売上, c.件数,
CASE WHEN p.売上 = '' THEN '' ELSE c.売上 - p.売上 END AS 前月差,
CASE WHEN p.売上 = '' OR p.売上 = 0 THEN '' ELSE ROUND((c.売上 - p.売上) * 100 / p.売上, 1) END AS 前月比
FROM #キー付 c LEFT JOIN #月次 p ON c.前月キー = p.年月
ORDER BY c.年月 DESC;
| 設定 | 値 |
|---|---|
| 列の設定 → 売上 | 右寄せ / 万 / データバー |
| 列の設定 → 前月差 | 右寄せ / 万 / 増減記号(増えると良い) |
| 列の設定 → 前月比 | 右寄せ / % / 小数 1 桁 |
| 単位を見出しへ | オン |
| マルチヘッダー | 金額 = [売上, 前月差] / 件数・伸び = [件数, 前月比] |
| 条件付き書式 | 前月比 → ヒートマップ(赤青)・基準 0 |
4 つ入っています。
データバー — セルの背景に、列の最大値を 100% とした棒を敷きます。数字の羅列から大小を読むには、色の濃淡より長さのほうが速いからです。
増減記号 — 正なら ▲、負なら ▼ を付けて色も分けます。「増えると良い / 悪い」を選べるので、コストや不良率も正しい向きで色が付きます。記号を添えるので白黒印刷でも読めます。
単位を見出しへ — 表示スケールを設定した数値列で、セルは素の数字だけにし、単位を「売上(万)」のように見出しへ 1 回だけ出します。隣に置いたグラフと数字の見た目が揃います。
マルチヘッダー — 2 階層のグループ見出しです。
マルチヘッダーを使うときは、
columnGroupsの並びに SQL の列順を合わせておくと安全です。順序が食い違っても表示は揃いますが、SQL を読む人にとっては列順とグループが一致しているほうが分かりやすくなります。
ゼロ除算とのつきあい方
前月比 に 2 段のガードが入っています。
CASE WHEN p.売上 = '' OR p.売上 = 0 THEN '' ELSE ROUND(…) END
いちばん古い月には前月がありません。LEFT JOIN で一致しなかった値は 0 ではなく空文字です。そして空文字は算術では 0 になるのに、比較では 0 と等しくありません。
つまり = 0 だけのガードは空文字をすり抜けて NaN を表に載せます。= '' OR = 0 の両方が要ります。
| 除数の作り方 | 値が無いとき |
= 0 だけで足りるか |
|---|---|---|
SUM / COUNT / AVG を通した値 |
0 |
足りる |
MIN / MAX を通した値 |
空文字 | 足りない |
素の列 / LEFT JOIN の未一致 |
空文字 | 足りない |
踏みやすいのは「率」より「〜あたり」です。在庫日数・単価・1 件あたり・回転率 — いずれも対象が止まっていると分母が消えます。エラーは出ず、
NaNが表に載ります。
1,000 行を超えるとき
行数が 1,000 を超えると、表は行仮想化(既定)に切り替わり、見えている範囲だけを描きます。ページング(100 行 / 頁)へ変えることもできます。
ただし、それ以前に取得上限があります。既定 10,000 件・最大 50,000 件で、超えると集計はエラーになります(明細は打ち切り表示)。
WHERE で絞る書き方を選ぶのがいちばんの対策です。相対日付(THIS_MONTH() など)やステータスの条件は kintone のサーバー側へ渡り、該当分だけが返ってきます。
つまずきポイント
ウィンドウ関数の結果を同じ SELECT で使えない
-- × これはエラー
SELECT ROUND(SUM(売上) OVER (), 1) AS 累計 FROM #月次
段を分けます。 ウィンドウの結果をいったん列として出し、それを使う式は次の CTE か一時テーブルに書きます。エンジンのエラーメッセージが正しい書き方まで教えてくれます。
ORDER BY に集計関数を書けない
ORDER BY SUM(金額) DESC は通りません。SELECT で付けた別名(ORDER BY 売上 DESC)を使います。
GROUPING 同士を足せない
GROUPING(a) + GROUPING(b) は構文エラーです。CASE で段を作ります(例 4)。
LEFT JOIN の未一致は 0 ではなく空文字
上の「ゼロ除算とのつきあい方」のとおりです。= 0 だけのガードはすり抜けます。
列名の英字が小文字になる
別名に英数字が含まれると、結果の列名は小文字化されます(AS ランクA → ランクa)。列の設定・集計行・条件付き書式で列名を指定するときは小文字のほうを書きます。日本語だけの別名なら、そのままです。
まとめ
表で押さえるのは 3 つです。
- 表の見え方を決めるのは SQL — 列名がそのまま見出しになる。小計も順位も SQL 側で作る
-
集計行と
ROLLUPは役割が違う — 総計だけなら集計行、階層の小計ならROLLUP - 見せ方はペイン側 — データバー・増減記号・ヒートマップ・単位を見出しへ
行番号列で足りるのに RANK() を書いたり、ROLLUP を書けば済むのに集計行で粘ったり、というのが遠回りの典型です。どちらの側で解くかを先に決めると、SQL も設定も短くなります。
関連記事
- ⓪ 準備編 — デモアプリを 15 分で用意する
- ① KPI カード — 数字 1 つを、意味のある 1 つに
- ③ グラフ — 9 種あるが、SQL の形は 4 通り
- ④ マークダウン — 数字を、文章に埋める
- kSQL Dashboard Pro 製品紹介 — 表示タイプ・設定画面・テーマなど機能全般
- SFA パック編 / 在庫管理パック編 — 業務アプリに実際に組んだ例
- rex0220 kSQL 言語リファレンス — SQL の文法。プラグインで使えるのは読み取り系だけです
付録 — 配布ファイル
GitHub に一式を置いています
アプリテンプレート・データ投入スクリプト・設定 JSON をまとめてあります。
https://github.com/rex0220/ksql-dashboard-pro-demo
設定はまとめて 1 回で取り込めます。 settings/型別-全一覧-見本.json に 5 一覧・43 ペイン(連載 4 本ぶん + 4 型を組み合わせた見本)が入っています。この記事のぶんだけでよければ、下の JSON を使ってください。
**アプリの用意とデータの入れ方は ⓪ 準備編**にまとめてあります。4 本とも同じアプリを使うので、一度作れば全部動きます。
設定 JSON(この記事の 3 タブ 10 ペイン)
GitHub の settings/型別-表-見本.json と同じものです。コピーして使えるよう全文を貼ります。
- 折りたたみを開き、コードブロック右上のコピーボタンでコピー
- テキストエディター(メモ帳等)に貼り付け、
型別-表-見本.jsonとして保存(文字コード UTF-8) - 設定画面の ツール → インポート でそのファイルを選択
SQL 中の
APP4239などは、取り込み時の確認ダイアログ「アプリ番号の変換」でご自身の環境の番号へ変換できます(JSON を手で書き換える必要はありません)。
型別-表-見本.json — 3 タブ 10 ペイン
{
"date": "2026-08-15 00:00:00",
"pluginName": "kSQL Dashboard Pro",
"pluginVersion": "1",
"engineVersion": "3.66.1",
"appId": 4239,
"appName": "売上明細",
"sqlApps": [
{
"appId": 4239,
"appName": "売上明細"
},
{
"appId": 4240,
"appName": "月次目標"
},
{
"appId": 4241,
"appName": "商品分類マスタ"
}
],
"config": {
"schemaVersion": 2,
"edition": "pro",
"views": {
"13335808": {
"name": "高度データ表の見本",
"viewName": "【型別】表",
"layout": [],
"panes": [],
"tabs": [
{
"id": "tab1",
"name": "基本の 3 つ",
"layout": [
{
"i": "pane1",
"x": 0,
"y": 0,
"w": 30,
"h": 6
},
{
"i": "pane2",
"x": 30,
"y": 0,
"w": 30,
"h": 6
},
{
"i": "pane3",
"x": 0,
"y": 6,
"w": 60,
"h": 6
}
],
"panes": [
{
"id": "pane1",
"type": "table",
"title": "最小 — 明細をそのまま出す",
"sql": "SELECT 売上日, 顧客名, 商品名, 数量, 金額\nFROM APP4239\nWHERE 売上日 = THIS_MONTH()\nORDER BY 売上日 DESC"
},
{
"id": "pane2",
"type": "table",
"title": "明細表 + 集計行(レコードを開ける)",
"sql": "SELECT $id, 売上日, 顧客名, 商品カテゴリ, 商品名, 数量, 単価, 金額, 売上ステータス\nFROM APP4239\nWHERE 売上日 = THIS_MONTH()\nORDER BY 売上日 DESC",
"options": {
"density": "compact",
"columns": {
"$id": {
"hidden": true
},
"売上日": {
"width": 100,
"format": {
"kind": "date",
"pattern": "M/D"
}
},
"単価": {
"format": {
"kind": "number",
"thousandSep": true,
"decimals": 0
}
},
"金額": {
"format": {
"kind": "number",
"thousandSep": true,
"decimals": 0
}
}
},
"totals": {
"数量": "sum",
"金額": "sum"
},
"rules": [
{
"kind": "threshold",
"column": "売上ステータス",
"op": "=",
"value": "取消",
"tone": "warn",
"scope": "row"
}
]
}
},
{
"id": "pane3",
"type": "table",
"title": "Top 10 ランキング(行番号列)",
"sql": "SELECT 担当者名, 部署, COUNT(*) AS 件数, SUM(金額) AS 売上\nFROM APP4239\nWHERE 売上日 = THIS_YEAR() AND 売上ステータス = '確定'\nGROUP BY 担当者名, 部署\nORDER BY 売上 DESC\nLIMIT 10",
"options": {
"rowNumbers": true,
"columns": {
"売上": {
"align": "right",
"format": {
"kind": "number",
"scale": "man",
"suffix": "円",
"decimals": 0
}
}
},
"totals": {
"件数": "sum",
"売上": "sum"
}
}
}
]
},
{
"id": "tab2",
"name": "SQL で作る集計",
"layout": [
{
"i": "pane4",
"x": 0,
"y": 0,
"w": 30,
"h": 6
},
{
"i": "pane5",
"x": 30,
"y": 0,
"w": 30,
"h": 6
},
{
"i": "pane6",
"x": 0,
"y": 6,
"w": 16,
"h": 6
},
{
"i": "pane7",
"x": 16,
"y": 6,
"w": 22,
"h": 6
},
{
"i": "pane8",
"x": 38,
"y": 6,
"w": 22,
"h": 6
}
],
"panes": [
{
"id": "pane4",
"type": "table",
"title": "ウィンドウ関数と自己 JOIN — 順位・累積・前月",
"sql": "/* 前月は LAG ではなく自己 JOIN で取る。\n LAG は ORDER BY が全順序と証明できず「同順内の前後関係は未規定」の警告が出る。\n 年月をキーに突き合わせれば順序に依存しないので、警告そのものが出ない */\nCREATE TEMP TABLE #月次 AS\n SELECT DATE_FORMAT(売上日, '%Y-%m') AS 年月, SUM(金額) AS 売上\n FROM APP4239\n WHERE 売上ステータス = '確定' AND 売上日 <= CURRENT_DATE()\n GROUP BY DATE_FORMAT(売上日, '%Y-%m');\nCREATE TEMP TABLE #キー付 AS\n SELECT 年月, 売上, DATE_FORMAT(DATE_ADD(CONCAT(年月, '-01'), -1, 'MONTH'), '%Y-%m') AS 前月キー\n FROM #月次;\n/* 累積と順位はウィンドウ関数のまま。こちらは警告が出ない\n (累積はフレームを明示する。既定の RANGE だと同値の行が同じ値になる) */\nSELECT c.年月, c.売上,\n RANK() OVER (ORDER BY c.売上 DESC) AS 売上順位,\n SUM(c.売上) OVER (ORDER BY c.年月 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS 累計,\n p.売上 AS 前月\nFROM #キー付 c LEFT JOIN #月次 p ON c.前月キー = p.年月\nORDER BY c.年月 DESC;",
"options": {
"columns": {
"売上": {
"align": "right",
"format": {
"kind": "number",
"scale": "man",
"decimals": 0
}
},
"累計": {
"align": "right",
"format": {
"kind": "number",
"scale": "man",
"decimals": 0
}
},
"前月": {
"align": "right",
"format": {
"kind": "number",
"scale": "man",
"decimals": 0
}
},
"売上順位": {
"align": "right",
"width": 90
}
},
"unitInHeader": true,
"rules": [
{
"kind": "threshold",
"column": "売上順位",
"op": "<=",
"value": 3,
"tone": "good",
"bold": true
}
]
}
},
{
"id": "pane5",
"type": "table",
"title": "小計・総計を SQL で作る(ROLLUP / GROUPING)",
"sql": "/* GROUPING は小計・総計の行で 1 を返す。**そのままだとキーが空になり、\n 小計と総計がどちらも「-」で見分けが付かない。**ラベルと段を作って区別する */\nSELECT\n CASE WHEN GROUPING(大分類) = 1 THEN '総計' ELSE 大分類 END AS 大分類,\n CASE WHEN GROUPING(大分類) = 1 THEN ''\n WHEN GROUPING(商品カテゴリ) = 1 THEN '小計'\n ELSE 商品カテゴリ END AS 商品カテゴリ,\n SUM(金額) AS 売上,\n /* GROUPING 同士は足せない。CASE で 0=明細 / 1=小計 / 2=総計 にする */\n CASE WHEN GROUPING(大分類) = 1 THEN 2\n WHEN GROUPING(商品カテゴリ) = 1 THEN 1\n ELSE 0 END AS 集計段\nFROM APP4239\nWHERE 売上日 = THIS_YEAR() AND 売上ステータス = '確定'\nGROUP BY ROLLUP(大分類, 商品カテゴリ)\n/* 並び順も読み順に合わせる。大分類ごとに 明細(売上の降順)→ 小計、最後に総計。\n GROUPING を先に置くと、小計・総計が必ずその段の末尾へ回る。\n ORDER BY に SUM(金額) は書けないので別名の 売上 を使う */\nORDER BY GROUPING(大分類), 大分類, GROUPING(商品カテゴリ), 売上 DESC",
"options": {
"density": "compact",
"columns": {
"売上": {
"align": "right",
"format": {
"kind": "number",
"scale": "man",
"suffix": "円",
"decimals": 0
}
},
"集計段": {
"width": 90,
"align": "center",
"label": "段"
}
},
"rules": [
{
"kind": "threshold",
"column": "集計段",
"op": "=",
"value": 2,
"tone": "good",
"scope": "row",
"bold": true
},
{
"kind": "threshold",
"column": "商品カテゴリ",
"op": "=",
"value": "小計",
"tone": "good",
"bold": true
}
]
}
},
{
"id": "pane6",
"type": "table",
"title": "構成比 + 上位 8 と「その他」(ペインの topN)",
"sql": "/* 構成比は SET @total で全体を先に出す(D19) */\nCREATE TEMP TABLE #g AS\n SELECT 商品カテゴリ, SUM(金額) AS 売上\n FROM APP4239\n WHERE 売上日 = THIS_YEAR() AND 売上ステータス = '確定'\n GROUP BY 商品カテゴリ;\nSET @total = (SELECT SUM(売上) FROM #g);\nSELECT 商品カテゴリ, 売上, ROUND((売上 * 100) / @total, 1) AS 構成比\nFROM #g\nORDER BY 売上 DESC;",
"options": {
"topN": 8,
"columns": {
"売上": {
"align": "right",
"format": {
"kind": "number",
"scale": "man",
"suffix": "円",
"decimals": 0
}
},
"構成比": {
"align": "right",
"format": {
"kind": "number",
"suffix": "%",
"decimals": 1
}
}
},
"totals": {
"売上": "sum"
},
"rules": [
{
"kind": "heatmap",
"column": "売上"
}
]
}
},
{
"id": "pane7",
"type": "table",
"title": "先月の目標達成率(月次目標と JOIN・ヒートマップ)",
"sql": "SELECT s.担当者名, SUM(s.金額) AS 実績, MAX(g.目標金額) AS 目標,\n ROUND((SUM(s.金額) * 100) / MAX(g.目標金額), 1) AS 達成率\nFROM APP4239 s INNER JOIN APP4240 g ON s.担当者名 = g.担当者名\n/* JOIN ON の等式は 1 つまで。月の絞り込みは WHERE 側に書く */\nWHERE s.売上日 = LAST_MONTH() AND s.売上ステータス = '確定'\n AND g.年月 = DATE_FORMAT(DATE_ADD(CURRENT_DATE(), -1, 'MONTH'), '%Y-%m')\nGROUP BY s.担当者名\nORDER BY 達成率 DESC",
"options": {
"density": "compact",
"columns": {
"担当者名": {
"width": 110
},
"実績": {
"align": "right",
"format": {
"kind": "number",
"scale": "man",
"decimals": 0
}
},
"目標": {
"align": "right",
"format": {
"kind": "number",
"scale": "man",
"decimals": 0
}
},
"達成率": {
"align": "right",
"width": 90,
"format": {
"kind": "number",
"suffix": "%",
"decimals": 1
}
}
},
"unitInHeader": true,
"totals": {
"実績": "sum",
"目標": "sum"
},
"rules": [
{
"kind": "heatmap",
"column": "達成率",
"scheme": "diverging",
"center": 100
}
]
}
},
{
"id": "pane8",
"type": "table",
"title": "階層の展開(WITH RECURSIVE)",
"sql": "WITH RECURSIVE 階層木 AS (\n SELECT 分類コード, 分類名, 親コード, 1 AS 深さ, 分類名 AS 経路\n FROM APP4241\n WHERE 親コード = ''\n UNION ALL\n SELECT c.分類コード, c.分類名, c.親コード, p.深さ + 1, CONCAT(p.経路, ' > ', c.分類名)\n FROM 階層木 p INNER JOIN APP4241 c ON c.親コード = p.分類コード\n)\nSELECT 深さ, 経路, 分類コード\nFROM 階層木\nORDER BY 経路",
"options": {
"density": "compact",
"columns": {
"深さ": {
"width": 70,
"align": "center"
},
"分類コード": {
"width": 110
}
},
"rules": [
{
"kind": "threshold",
"column": "深さ",
"op": "=",
"value": 1,
"tone": "good",
"scope": "row",
"bold": true
}
]
}
}
]
},
{
"id": "tab3",
"name": "品質と見せ方",
"layout": [
{
"i": "pane9",
"x": 0,
"y": 0,
"w": 30,
"h": 8
},
{
"i": "pane10",
"x": 30,
"y": 0,
"w": 30,
"h": 8
}
],
"panes": [
{
"id": "pane9",
"type": "table",
"title": "データ品質チェック — 直すべき行だけ出す",
"sql": "SELECT $id, 伝票番号, 売上日, 顧客名, 商品名, 数量, 金額,\n CASE WHEN 顧客名 = '' THEN '顧客名なし'\n WHEN 数量 < 0 THEN '数量がマイナス'\n WHEN 金額 = 0 THEN '金額ゼロ'\n WHEN 売上日 > CURRENT_DATE() THEN '未来日付'\n ELSE '' END AS 指摘\nFROM APP4239\nWHERE 顧客名 = '' OR 数量 < 0 OR 金額 = 0 OR 売上日 > CURRENT_DATE()\nORDER BY 指摘, 売上日",
"options": {
"density": "compact",
"columns": {
"$id": {
"hidden": true
},
"売上日": {
"width": 110,
"format": {
"kind": "date",
"pattern": "YYYY/MM/DD"
}
},
"金額": {
"align": "right",
"format": {
"kind": "number",
"thousandSep": true,
"decimals": 0
}
}
},
"rules": [
{
"kind": "threshold",
"column": "数量",
"op": "<",
"value": 0,
"tone": "crit",
"bold": true
},
{
"kind": "threshold",
"column": "顧客名",
"op": "empty",
"tone": "crit"
}
]
}
},
{
"id": "pane10",
"type": "table",
"title": "見せ方の総覧(データバー・増減記号・ヒートマップ・単位を見出しへ)",
"sql": "/* 前月差は自己 JOIN で取る(LAG の警告を避ける。pane4 と同じ理由) */\nCREATE TEMP TABLE #月次 AS\n SELECT DATE_FORMAT(売上日, '%Y-%m') AS 年月, SUM(金額) AS 売上, COUNT(*) AS 件数\n FROM APP4239\n WHERE 売上ステータス = '確定' AND 売上日 <= CURRENT_DATE()\n GROUP BY DATE_FORMAT(売上日, '%Y-%m');\nCREATE TEMP TABLE #キー付 AS\n SELECT 年月, 売上, 件数, DATE_FORMAT(DATE_ADD(CONCAT(年月, '-01'), -1, 'MONTH'), '%Y-%m') AS 前月キー\n FROM #月次;\n/* 最古の月は相手がいない。LEFT JOIN の未一致は 0 ではなく空文字なのでガードする */\nSELECT c.年月, c.売上, c.件数,\n CASE WHEN p.売上 = '' THEN '' ELSE c.売上 - p.売上 END AS 前月差,\n CASE WHEN p.売上 = '' OR p.売上 = 0 THEN '' ELSE ROUND((c.売上 - p.売上) * 100 / p.売上, 1) END AS 前月比\nFROM #キー付 c LEFT JOIN #月次 p ON c.前月キー = p.年月\nORDER BY c.年月 DESC;",
"options": {
"density": "compact",
"columns": {
"年月": {
"width": 90
},
"売上": {
"align": "right",
"bar": true,
"format": {
"kind": "number",
"scale": "man",
"decimals": 0
}
},
"件数": {
"align": "right"
},
"前月差": {
"align": "right",
"delta": "up-good",
"format": {
"kind": "number",
"scale": "man",
"decimals": 0
}
},
"前月比": {
"align": "right",
"format": {
"kind": "number",
"suffix": "%",
"decimals": 1
}
}
},
"unitInHeader": true,
"columnGroups": [
{
"label": "金額",
"columns": [
"売上",
"前月差"
]
},
{
"label": "件数・伸び",
"columns": [
"件数",
"前月比"
]
}
],
"rules": [
{
"kind": "heatmap",
"column": "前月比",
"scheme": "diverging",
"center": 0
}
]
}
}
]
}
]
}
},
"common": {}
}
}














