IT技術ブログ

kintone・スプレッドシートの業務データをAWSに集めて可視化する最小構成。S3・Athena・Quick Sightの組み方と、費用が増える所の見分け方

kintoneとGoogleスプレッドシートにある業務データを、AWSのS3・Athena・Quick Sightで集計・可視化する最小構成を実装の目線でまとめます。取り出し方、置き場所の分け方、読む量を減らす表の作り方、費用の考え方まで。

この記事の結論:kintoneとGoogleスプレッドシートにある業務データをAWSに集めて可視化するなら、中小企業の規模では「取り出す(定時に動く小さな関数)」「置く(S3)」「整える(Athena)」「見る(Quick Sight)」の4層で足ります。作る側が最初に決めておくべきなのは、①取り出したデータを手を加えないまま日付ごとに残す場所と、整えた後の場所を分けること、②Athenaの表を日付で区切り、列ごとに読める形式(Parquet)に変えて、クエリが読む量を小さくすること、③Quick Sightは取り込み(SPICE)を定時に更新する形にして、画面を開くたびにAthenaを読みにいかないこと、の3つです。費用は「読む量」「使う人数」「置く量」の3つで増えるので、この3つの増え方を作る前に見積もっておきます。

この記事は、kintoneのアプリやスプレッドシートで回している受注・売上・案件の記録を、部門をまたいで集計したくなった会社の担当の方と、組み立てを受け持つ開発者のために、部品の並べ方を説明するものです。kintoneやスプレッドシートを業務システムに置き換えるかどうかの判断は扱いません(そちらは社内ツールをノーコードで作るか開発するかの分かれ目にまとめています)。ここでは、kintoneやスプレッドシートを入力の道具として使い続けたまま、集計と可視化だけを外に出す形を前提にします。

データ分析基盤の最小構成は「取り出す・置く・整える・見る」の4層でできている

kintoneとGoogleスプレッドシートの業務データをAWSで可視化する最小構成は、4つの層を一方向につなぐだけの形です。入力の道具の側には何も書き込まないので、今の業務の流れは変わりません。

層 役割 AWSで組む場合の部品
取り出す 決まった時刻に、kintoneのアプリやシートからデータを読み出す Amazon EventBridge Scheduler と AWS Lambda
置く 読み出したデータをファイルとして残す Amazon S3
整える ファイルを表として読み、集計しやすい形に変える Amazon Athena と AWS Glue のデータカタログ
見る 集計した表をグラフや一覧にして、社内の人に見せる Amazon Quick Sight(以前の名前は Amazon QuickSight)

この4層には、常に動き続けるサーバーがありません。取り出しの関数は決まった時刻にだけ動き、Athenaはクエリを投げたときにだけ動きます。中小企業の規模でこの構成を勧める一番の理由はここで、使っていない時間の費用がほとんどかからず、止まったときに直す所も少なくなります。

逆に、この構成が向かないのは、数分前の数字を見たい場合です。kintoneやスプレッドシートの入力を、その場でグラフに反映させたいなら、取り出しの回数を増やすより、入力の道具の側の集計機能を使うほうが合っています。

取り出しは、kintoneはカーソルAPI、スプレッドシートはSheets APIで定時に丸ごと写す

取り出しの層では、毎日決まった時刻に、対象のアプリとシートの中身を全件読み出してS3に置きます。変わった分だけを写す形は、件数が増えて全件の取り出しが重くなってからで十分です。

kintoneはカーソルで一括取得し、使い終わったカーソルは消す

kintoneから大量のレコードを読み出すときは、「カーソル」を作って少しずつ受け取るAPIを使います。サイボウズが公開しているREST APIの仕様では、1回に受け取れる件数は1〜500件(省略すると100件)、カーソルの有効期限は作成または最後の取得から10分、1つのドメインで同時に持てるカーソルは10個までです。条件の指定には limit と offset を入れられません。

定時の関数で書くと、流れは次のようになります。

python
import json, os, urllib.request

BASE = os.environ["KINTONE_BASE_URL"]      # https://(サブドメイン).cybozu.com
TOKEN = get_secret("kintone-api-token")     # Secrets Manager から読む

def call(method, path, body=None):
    req = urllib.request.Request(
        f"{BASE}{path}", method=method,
        data=json.dumps(body).encode() if body else None,
        headers={"X-Cybozu-API-Token": TOKEN, "Content-Type": "application/json"},
    )
    with urllib.request.urlopen(req) as res:
        return json.load(res)

def fetch_all(app_id):
    cur = call("POST", "/k/v1/records/cursor.json", {"app": app_id, "size": 500})
    rows, done = [], False
    try:
        while True:
            page = call("GET", f"/k/v1/records/cursor.json?id={cur['id']}")
            rows.extend(page["records"])
            if not page["next"]:
                done = True   # 全件を受け取るとカーソルは自動で消える
                return rows
    finally:
        if not done:
            # 途中で失敗したときは、カーソルを自分で消して残さない
            call("DELETE", "/k/v1/records/cursor.json", {"id": cur["id"]})

サイボウズの仕様では、全件を受け取り終えたカーソルは自動で消えます。消しておく必要があるのは途中で止まったときで、この削除がないと、関数が途中で失敗するたびに期限切れまでカーソルが残り、ほかの連携が同じドメインでカーソルを作れなくなることがあります。APIトークンは関数の設定値に直接書かず、AWS Secrets Manager に置いて実行時に読み出します。トークンは読み出しに必要な権限(レコードの閲覧)だけを付けて発行します。

スプレッドシートは、表示用の文字ではなく値で受け取る

Googleスプレッドシートは、Sheets API の spreadsheets.values.get で範囲を指定して読み出します。気をつけたいのは、値の受け取り方の指定です。何も指定しないと、セルに表示されている文字(カンマ区切りの金額や、書式の付いた日付)がそのまま返ってきます。valueRenderOption に UNFORMATTED_VALUE を指定すると書式を外した値が返り、日付は dateTimeRenderOption の既定(SERIAL_NUMBER)でシリアル値になります。

指定 金額のセルの返り方 日付のセルの返り方 向いている使い方
指定なし(FORMATTED_VALUE) 画面と同じ文字 画面と同じ文字 人が読む一覧への転記
UNFORMATTED_VALUE と SERIAL_NUMBER 数値 シリアル値 集計に使う。日付は取り込み側で変換する
UNFORMATTED_VALUE と FORMATTED_STRING 数値 書式どおりの文字 日付の書式がシートの中でそろっている場合

集計に使うなら、2行目の組み合わせで受け取り、日付への変換は取り出しの関数の中で1か所にまとめます。シートごとに日付の書式がばらばらでも、シリアル値なら同じ変換で済むからです。

スプレッドシートは、列の追加や並べ替えが誰にでもできます。取り出しの関数では、1行目の見出しの並びを毎回確かめ、想定と違えば取り込みを止めて担当者に知らせる作りにしておきます。黙って列がずれたまま取り込むと、売上の列に別の数字が入った集計が何日も続きます。

S3には、取り出したままのデータと整えた後のデータを別の場所に置く

S3の中は、手を加えていないデータの場所と、Athenaで整えた後のデータの場所を、最初から分けて作ります。

s3://(会社名)-analytics/
  raw/                         … 取り出したまま。上書きしない
    kintone/app=12/dt=2026-10-04/records.json
    sheets/sales/dt=2026-10-04/values.json
  curated/                     … Athenaで整えた後。Parquet形式
    sales_daily/dt=2026-10-04/
  athena-results/              … Athenaのクエリ結果の置き場所

raw の側を上書きしない理由は2つあります。1つは、整える処理に誤りが見つかったとき、raw から作り直せることです。もう1つは、kintoneやスプレッドシートで後から書き換えられた数字について、ある日の時点で何が入っていたかを確かめられることです。入力の道具の側は今の状態しか持っていないので、日ごとの状態を残せるのはこの層だけです。

バケットはブロックパブリックアクセスを有効にしたまま使い、読み書きできるのは取り出しの関数、Athena、Quick Sightに付けた役割だけにします。raw には個人の名前や連絡先が入ることがあるので、保存期間を決め、期限を過ぎたものはライフサイクルのルールで自動的に消すか、安い保存の種類へ移します。

Athenaでは、日付で区切った表とParquetへの変換で読む量を減らす

Athenaのクエリの料金は、基本的にそのクエリが読み込んだデータの量で決まります。AWSの料金ページでも、圧縮と、列ごとに読める形式(Apache Parquet など)への変換で読む量が減り、その分だけ費用が下がると説明されています。表は次の2段で作ります。

まず、raw のファイルを読む表を、日付の区切り(パーティション)付きで定義します。日付の区切りを毎日登録する手間を省くため、パーティション射影(partition projection)を使います。

sql
CREATE EXTERNAL TABLE raw_kintone_app12 (
  record_id  string,
  department string,
  amount     bigint,
  ordered_on string,
  updated_at string
)
PARTITIONED BY (dt string)
ROW FORMAT SERDE 'org.openx.data.jsonserde.JsonSerDe'
LOCATION 's3://(会社名)-analytics/raw/kintone/app=12/'
TBLPROPERTIES (
  'projection.enabled' = 'true',
  'projection.dt.type' = 'date',
  'projection.dt.format' = 'yyyy-MM-dd',
  'projection.dt.range' = '2026-10-01,NOW',
  'storage.location.template' = 's3://(会社名)-analytics/raw/kintone/app=12/dt=${dt}/'
);

(kintoneのレコードは項目ごとに型と値を入れ子で持つ形で返ってくるので、取り出しの関数の中で「1行に1レコード、項目名と値だけ」の形に平らにしてから書き出す前提の例です。列の名前と型は、例として置いたものです。)

次に、その日の分だけを、集計に使う列にしぼってParquetに変えて curated に書き出します。毎日の定時の処理で、INSERT INTO を使って1日分ずつ足していく形にすると、読むのはその日の raw だけで済みます。

集計の画面が読むのは curated の表だけにします。raw の表は、作り直しや調べものをするときにだけ使います。

パーティション射影には、AWSのドキュメントにも書かれた注意があります。範囲の外の日付や、ファイルのない日付を指定してもエラーにならず、0件が返るだけです。取り出しが失敗した日のグラフが「売上0」に見えることがあるので、取り出しの関数が成功した日を別の小さな表に記録し、画面に「最終更新日」として出しておきます。

読む量の上限は、Athenaのワークグループで決めておきます。ワークグループには、1回のクエリが読める量と、ワークグループ全体で一定の期間に読める量の上限を設定できます。誰かが curated ではなく raw の全期間に条件なしのクエリを投げても、上限で止まります。

可視化はQuick Sightの取り込み(SPICE)を定時に更新し、見せる範囲は行単位で絞る

Quick Sightのデータセットは、Athenaに直接問い合わせる形と、データを取り込み(SPICE)に入れる形を選べます。この構成では取り込みを使い、更新は取り出しと整える処理が終わった後の時刻に1日1回入れます。画面を開くたびにAthenaを読みにいかないので、Athenaの読む量が「1日1回の更新の分」で決まり、予想しやすくなります。

AWSのドキュメントによると、更新の予定は日次・週次・月次を選べ、Enterprise エディションでは1時間ごとや、日付の列を使って直近の期間だけを入れ直す差分の更新も使えます。予定の時刻から10分以内に取り込みが始まるとされているので、取り出しの完了との間には余裕を持たせます。

部門ごとに見てよい数字が違う場合は、行単位のセキュリティを使います。「利用者またはグループ」と「見てよい部門の値」を並べた表をデータセットに結び付けると、同じダッシュボードでも、開いた人の所属に合う行だけが表示されます。部門ごとにダッシュボードを複製する形は、グラフを直すたびに全部を直すことになるので避けます。

費用は「読む量」「使う人数」「置く量」の3つで増える

この構成の費用は、固定の月額よりも、使い方に比例して増える部分がほとんどです。具体的な金額はAWSの料金ページと為替で変わるので、ここでは何に比例して増えるかだけを整理します。

部品 何に比例して増えるか 増えにくくする手立て
Athena クエリが読み込んだデータの量 Parquetへの変換、日付の区切り、ワークグループの上限
Quick Sight 作る人・見る人の数と役割、取り込み(SPICE)の容量 見るだけの人は閲覧の役割にする。取り込む列を絞る
S3 置いているデータの量と、読み書きの回数 raw の保存期間を決め、ライフサイクルで消す・移す
Lambda と EventBridge Scheduler 動いた回数と時間 取り出しは1日1回から始める

中小企業の規模で一番大きくなりやすいのは、多くの場合Quick Sightの利用者の数です。作る前に「グラフを作る人は誰か」「見るだけの人は何人か」を書き出し、AWSの料金ページで役割ごとの料金を確かめておくと、費用の見積もりの大半はそこで決まります。Athenaは、curated の表だけを読む作りにしておけば、件数が増えても急には膨らみません。

AWSのデータ分析基盤は、作るより保つほうに手間がかかります。

株式会社bundlyzeでは普段からAWSでデータ分析の基盤を構築する仕事をしており、この記事の層の分け方も、その仕事で置いている前提を中小企業の規模に合わせて書き直したものです。構成の見直しや、監視・保守まで含めた運用の整え方は、クラウドの構築と運用のご相談で受け付けています。

作る順番は、1つのアプリと1つのグラフから始める

最初の版は、kintoneのアプリ1つと、スプレッドシート1つ、グラフ1つで作ります。目的は、毎日の取り出しから表示までが止まらずに回ることを確かめることです。

  1. 見たい数字を1つ決める。 「部門別の月ごとの受注額」のように、元のアプリとシートが決まるものにする
  2. 取り出しの関数を作る。 kintoneはカーソルの削除まで、スプレッドシートは見出しの確かめまで入れる。失敗したら担当者に通知する
  3. S3の場所を分ける。 raw・curated・athena-results の3つを作り、raw の保存期間を決める
  4. Athenaの表を作る。 raw の表にパーティション射影を設定し、curated へのParquetの書き出しを毎日の処理に入れる。ワークグループに読む量の上限を入れる
  5. Quick Sightのデータセットとグラフを作る。 取り込みの更新を取り出しの後の時刻にし、最終更新日を画面に出す
  6. 2週間ほど毎日見る。 取り出しの失敗、見出しの変更、数字の食い違いがないかを、元のアプリとシートの集計と突き合わせて確かめる

この6つが回れば、アプリやシートを増やす作業は、取り出しの対象と表の定義を足すことだけになります。

参照した公式の資料(確認日:2026年10月3日)

料金の体系やAPIの上限は改定されることがあるので、組み始める時点の内容を各ページで見直してください。

よくある質問

kintoneやスプレッドシートのデータを、AWSに移さずに集計することはできませんか?

件数が少なく、見る人が限られているうちは、それぞれの集計機能で足ります。複数のアプリやシートをまたいで数字を突き合わせたい、過去の状態を日ごとに残したい、見る人ごとに範囲を変えたい、のどれかが出てきたら、別の置き場所に写して集計する構成を考える時期です。

取り出したデータは、毎日全件を写すのと、変わった分だけ写すのと、どちらがよいですか?

中小企業の規模なら、最初は毎日全件を日付ごとの場所に写す形をおすすめします。仕組みが単純で、取り出しに失敗しても次の日にやり直せば元に戻り、過去のある日の状態も残ります。件数が増えて取り出しに時間がかかるようになってから、更新日時で絞る形に切り替えます。

Athenaの費用は、何を見ておけば予想しやすくなりますか?

Athenaのクエリの料金は、基本的にクエリが読み込んだデータの量で決まります。1回のクエリが読む量と、1日にクエリが走る回数をかけたものが目安になります。Quick Sightの画面を開くたびにAthenaへ問い合わせる設定にすると回数が見えにくくなるため、取り込み(SPICE)を使って、Athenaを読むのは定時の更新のときだけにしておくと予想しやすくなります。