会員台帳をスプレッドシートで持っていて、行が増え、触る人が増え、見せたくない列が出てきたころに「kintoneに移したほうがいいのか」という話が出ます。この記事では、スプレッドシート・AppSheet・kintoneの3つを同じ物差しで比べ、どこでkintoneへ移すかの目安を書きます。そのうえで、サンプル料理教室の会員台帳をkintoneに移し、講師が入れる出席はスプレッドシートのまま残して、GASで両方を同期するところまでを通しで作ります。kintoneのアカウントがあり、スプレッドシートの台帳がすでにある前提です。
この記事の内容
先に決めること
全部移すか、一部だけ移すか
スプレッドシートの台帳を全部kintoneに移す必要はありません。サンプル料理教室では、会員台帳はkintoneへ、出席と月謝の請求はスプレッドシートのままにします。会員の情報は代表と受付が細かく管理したい一方で、出席は講師が毎日のレッスンの後に入れるだけで、すでにAppSheetのアプリで回っているからです。動いているものを全部作り直すと、移行の手間のわりに得るものが少なくなります。
どちらを正にするか
両方に会員の名前が載る以上、どちらの値が正しいかを決めておきます。正(マスター)でない側で書き換えた値は、次の同期で上書きされて消えます。サンプル料理教室では次のように分けます。
- 会員番号・氏名・連絡先・コース・在籍状況はkintoneが正。スプレッドシートには写しを置くだけで、そこでは直さない
- 出席の記録はスプレッドシートが正。kintoneには月ごとの出席回数だけを送る
この決め方を、スプレッドシートの写しのシートの1行目に「このシートはkintoneから自動で上書きされます。直すときはkintoneで」と書いておくと、受付がうっかり直す事故が減ります。
何で2つを結ぶか
会員を両方で同じ人と分かるように、変わらない番号を1つ決めます。サンプル料理教室では M-0001 の形の会員番号です。氏名で結ぶと、同姓同名や結婚による改姓で崩れます。kintone側では、この会員番号のフィールドに「値の重複を禁止する」を入れておきます。これがあるとAPIやCSVでの更新のキーに使えます。
kintoneのコース
kintoneのライトコースは、外部サービスとの連携やプラグインといった拡張機能に対応していません(2026年10月時点)。GASからREST APIで同期するなら、スタンダードコース以上が要ります。また、ライトとスタンダードは10ユーザーからの契約です(ワイドは1,000ユーザーから)。スタッフ6名の教室でも10ユーザー分の料金がかかります。
スプレッドシート・AppSheet・kintoneの比較
| 比べるところ | スプレッドシート | AppSheet | kintone |
|---|---|---|---|
| 料金(2026年10月時点・税抜) | Google Workspaceに含まれる(Business Starterは1ユーザー月800円) | AppSheet CoreはGoogle Workspaceのほとんどの有料プランに含まれる | ライト1,000円・スタンダード1,800円(1ユーザー月額、どちらも10ユーザーから) |
| 権限 | ファイル単位の共有(閲覧者・閲覧者(コメント可)・編集者)と、範囲の保護 | アプリごとに使う人を決め、見せる行や列を絞れる | アプリ・レコード・フィールドの3段で設定できる |
| 同時に触るとき | 同じファイルを何人でも同時に編集できるが、同じセルは後から書いた人が勝つ | 1件ずつ画面で入れる。データはスプレッドシート側にたまる | 1件ずつ開いて保存する。誰がいつ何を変えたかがレコードごとに残る |
| 件数 | 1ファイル2,000万セルまたは100MBまで | データ元がスプレッドシートならその上限。AppSheetのデータベースはCoreで1つ2,500行まで | 件数の上限より、API(スタンダードは1アプリ1日1万リクエスト)と動作の重さで考える |
| スマホ | アプリで見られるが、横に長い表は入力しにくい | スマホで使う前提のアプリが作れる | 公式のスマホアプリで入力・閲覧できる |
| 自動化 | GAS・n8n | AppSheetの自動化(Automation) | プロセス管理・通知・JavaScript・プラグイン(スタンダード以上) |
| 向く使い方 | 集計・一覧・数人で回す台帳 | 現場で決まった項目を入れる入力アプリ | 何人もが触る顧客台帳、1件ずつ履歴を積む管理 |
スプレッドシートは計算と集計が得意で、関数とピボットで月ごとの数字をすぐ出せます。kintoneは1件の会員に対して、問い合わせ・面談・振替のやりとりを何件もぶら下げ、誰がいつ変えたかを残すのが得意です。AppSheetはその間で、スプレッドシートをデータ置き場にしたまま、スマホの入力画面を作る道具です。3つは競合というより、役割が違います。
スプレッドシートからkintoneへ移る目安
次のうち2つ以上に当てはまったら、kintoneへ移すことを考えます。1つだけなら、スプレッドシートの工夫で済むことが多いです。
| 起きていること | スプレッドシートでの手当て | kintoneに移すと |
|---|---|---|
| 3人以上が同じ台帳を毎日書き換え、上書きの事故が起きる | 担当ごとにシートを分ける、範囲の保護 | 1件ずつ保存するので、他の人の行を巻き込まない |
| 列ごとに見せたくない人がいる(月謝の金額を講師に見せない) | ファイルを分けてIMPORTRANGEでつなぐ | フィールドのアクセス権で、講師には金額のフィールドを見せない |
| 1人の会員に、やりとりの記録を何件も残したい | セルにメモを追記していく | コメントや関連レコードで、時系列に積める |
| 行が数千を超え、関数が重くなってきた | 計算列を減らす、古い年を別ファイルへ | 一覧と絞り込みで探す使い方に変わる |
| 誰が変えたかを会員1人ごとに追いたい | セルの編集履歴を1つずつ見る | レコードの変更履歴で追える |
| 「入会手続き中」「在籍」「休会」の流れを決まった順で動かしたい | ドロップダウンと条件付き書式 | プロセス管理で、誰が次に動かすかまで決められる |
| 外出先から講師や受付がスマホで台帳を見る | AppSheetで入力画面を作る | 公式のスマホアプリで見られる |
逆に、台帳を触るのが2人だけで、行数が数百、見せたくない列も無いうちは、スプレッドシートのままが速くて安上がりです。実際に運用していると、kintoneに移したあとで「集計だけはやっぱりスプレッドシートで見たい」となる会社が多く、片方に寄せきるより、役割で分けたほうが長続きします。
手順1:会員台帳をCSVでkintoneに移す
最初の移行はCSVで一度に移します。GASで1件ずつ送るより速く、間違いにも気づきやすいからです。
スプレッドシート側で整える
移す前に、スプレッドシートの会員台帳を整えます。ここを飛ばすと、kintoneで読み込みのエラーが山ほど出ます。
- 会員台帳のファイルをコピーして、作業用の控えを作る
- 1行目を項目名だけにし、結合したセルを解除する
- 会員番号の列に空欄や重複が無いことを確かめる(
=COUNTIF(A:A,A2)>1を隣の列に入れてTRUEを探す) - 金額の列から「円」と桁区切りのカンマを外す。表示形式を「数値」にして、小数点以下を0桁にする
- 日付の列を
2026-11-14の形にそろえる(表示形式の「カスタム日時」でyyyy-mm-dd) - 担当講師の列は、kintoneのユーザーにするならkintoneのログイン名に書き換える。ドロップダウンにするなら、表記のゆれ(「田中」「田中先生」)をそろえる
- 「ファイル」→「ダウンロード」→「カンマ区切り形式(.csv)」を選ぶ

スプレッドシートから書き出したCSVはUTF-8です。Excelで一度開いて保存し直すと文字コードが変わることがあるので、そのままkintoneに読み込みます。
CSVからkintoneのアプリを作る
まだkintoneにアプリが無いなら、CSVからアプリを作れます。
- kintoneのポータルの「アプリ」の欄で「+」(アプリを作成する)を押す
- 「kintoneアプリストア」の「新しくアプリをつくる」で「CSVを読み込んで作成」を押す
- 「作成を開始する」を押し、注意事項を確かめて「アップロードへ進む」を押す
- 「参照」から書き出したCSVを選ぶ
- 「プレビューを確認する」で文字化けや列のずれが無いかを見る。化けていたら「文字コード」を変える
- 「アプリの作成を開始する」で、列ごとに自動で選ばれたフィールドの種類を確かめる。要らない列は「(この列を無視する)」にする
- 右下の「作成」を押し、確認の画面で「OK」を押す

読み込めるのは1ファイル100MB・10万行・500列までです。作ったあと、アプリの設定のフォームで次を直します。
- 会員番号のフィールドを開き、「値の重複を禁止する」と「必須項目にする」にチェックを入れる
- 各フィールドのフィールドコードを、日本語の項目名と同じにそろえる(
会員番号氏名コース在籍状況など)。GASのコードはフィールドコードで書くので、ここで決めた名前を後で変えない - 「今月の出席回数」という数値のフィールドを足す(出席の同期で使う)
- 「アプリを更新」を押す
すでにアプリがあるときは「ファイルから読み込む」
アプリを先に作ってある場合や、2回目以降の読み込みは、レコードの一覧の画面から入れます。
- レコードの一覧の右上の「オプション」(3つの点)から「ファイルから読み込む」を選ぶ
- 「ファイルを選択」でCSVを選ぶ
- 「先頭行をファイルの列名として扱う」にチェックを入れる
- 「アプリへの反映方法」で、初回は「レコードの追加のみ」を選ぶ。2回目以降に既存の会員を直すなら「レコードの更新と追加」を選び、「更新キー」に会員番号を選ぶ
- エラーのときの処理は「読み込みを中止する(エラー行以降の行は読み込みません)」を選ぶ
- フィールドごとに「ファイルの列」を対応させる。入れないフィールドは「(指定しない)」
- 右下の「読み込む」を押す

最初の読み込みで「中止する」を選ぶのは、エラーの行だけ飛ばして進むと、何件が入らなかったのかを後から探す手間が大きいからです。止まったら、エラーの行をスプレッドシートで直してもう一度読み込みます。
計算のフィールドや、ほかのフィールドから値を持ってくるフィールドには読み込めません。数値の列は、桁区切りの書き方に合わせて「桁区切りと小数点の記号」を選びます。日付の形式も選べますが、スラッシュ以外の区切りは年・月・日の順で読まれます。
手順2:GASでkintoneとスプレッドシートを同期する
移したあとも、出席のアプリ(AppSheet)は会員の名前の一覧をスプレッドシートから読んでいます。そこで、kintoneの会員台帳を毎朝スプレッドシートの「会員」シートに写し、月末には出席シートの回数をkintoneに書き戻します。
kintoneでAPIトークンを作る
- 会員台帳のアプリを開き、右上の歯車「アプリを設定」を押す
- 「設定」タブの「カスタマイズ/サービス連携」にある「APIトークン」を押す
- 「生成する」を押す
- アクセス権で「レコード閲覧」と「レコード編集」にチェックを入れる
- メモに
GAS 出席同期と入れる - 右下の「保存」を押す
- 右上の「アプリを更新」を押し、確認の画面でも「アプリを更新」を押す

最後の「アプリを更新」を忘れると、トークンが効かずにエラーになります。APIトークンでした操作は、kintoneの上では「Administrator」が更新したものとして記録されます。誰が変えたかを追いたいときは、更新者がAdministratorのものはGASの同期だと読み替えます。
スクリプトにトークンを入れる
- 出席のスプレッドシートを開き、「拡張機能」→「Apps Script」を開く
- 左の「プロジェクトの設定」(歯車)を開く
- 「スクリプト プロパティ」で「スクリプト プロパティを追加」を押す
- プロパティに
KINTONE_API_TOKEN、値にAPIトークンを入れて保存する
トークンをコードに直接書かないのは、スクリプトをコピーして人に渡したときにトークンまで渡ってしまうからです。
同期のコード
スプレッドシートには「会員」シート(A列から会員番号・氏名・コース・在籍状況・担当講師)と、「出席」シート(A列から日付・会員番号・氏名・レッスン・出欠)があるものとします。
const KINTONE_DOMAIN = 'sample-cooking.cybozu.com';
const MEMBER_APP_ID = 15;
const TZ = 'Asia/Tokyo';
function kintoneRequest_(method, path, body) {
const token = PropertiesService.getScriptProperties().getProperty('KINTONE_API_TOKEN');
const options = {
method: method,
headers: { 'X-Cybozu-API-Token': token },
muteHttpExceptions: true
};
if (body) {
options.contentType = 'application/json';
options.payload = JSON.stringify(body);
}
const res = UrlFetchApp.fetch('https://' + KINTONE_DOMAIN + path, options);
const json = JSON.parse(res.getContentText());
if (res.getResponseCode() !== 200) {
throw new Error('kintone ' + res.getResponseCode() + ': ' + json.message);
}
return json;
}
// kintoneの会員台帳を「会員」シートに写す(毎朝)
function pullMembers() {
const rows = [];
let lastId = 0;
while (true) {
const query = '$id > ' + lastId + ' order by $id asc limit 500';
const path = '/k/v1/records.json?app=' + MEMBER_APP_ID +
'&query=' + encodeURIComponent(query);
const records = kintoneRequest_('get', path).records;
records.forEach(function (r) {
rows.push([
r['会員番号'].value,
r['氏名'].value,
r['コース'].value,
r['在籍状況'].value,
r['担当講師'].value
]);
});
if (records.length < 500) break;
lastId = Number(records[records.length - 1]['$id'].value);
}
const sheet = SpreadsheetApp.getActive().getSheetByName('会員');
const last = sheet.getLastRow();
if (last > 1) sheet.getRange(2, 1, last - 1, 5).clearContent();
if (rows.length > 0) sheet.getRange(2, 1, rows.length, 5).setValues(rows);
}
// 今月の出席回数をkintoneに書き戻す(毎日の夜か月末)
function pushAttendanceCounts() {
const ss = SpreadsheetApp.getActive();
const ym = Utilities.formatDate(new Date(), TZ, 'yyyy-MM');
const counts = {};
ss.getSheetByName('会員').getDataRange().getValues().slice(1).forEach(function (row) {
if (row[0]) counts[row[0]] = 0;
});
ss.getSheetByName('出席').getDataRange().getValues().slice(1).forEach(function (row) {
const date = row[0], memberNo = row[1], status = row[4];
if (!(date instanceof Date) || status !== '出席') return;
if (Utilities.formatDate(date, TZ, 'yyyy-MM') !== ym) return;
if (memberNo in counts) counts[memberNo] += 1;
});
const records = Object.keys(counts).map(function (no) {
return {
updateKey: { field: '会員番号', value: no },
record: { '今月の出席回数': { value: counts[no] } }
};
});
for (let i = 0; i < records.length; i += 100) {
kintoneRequest_('put', '/k/v1/records.json', {
app: MEMBER_APP_ID,
records: records.slice(i, i + 100)
});
}
}
MEMBER_APP_ID はkintoneのアプリのURL https://sample-cooking.cybozu.com/k/15/ の数字です。pullMembers は500件ずつ取り、取ってきた最後のレコードの番号より後を次に取ります。kintoneは1回に500件までしか返さず、offset の指定は1万件までなので、件数が増えても止まらないように番号で区切っています。
pushAttendanceCounts は「会員」シートの全員を0回から数え始めるので、今月1回も来ていない会員もkintoneで0回に直ります。kintoneの更新は1回100件までなので、100件ずつに分けて送ります。100件の中に1件でも失敗があると、その100件はすべて取り消されます。会員番号がkintoneに無い行が混じると全部止まるのはこのためです。
動かして、トリガーを入れる
- 関数の選択で
pullMembersを選び、「実行」を押す - 初回は承認の画面が出るので、自分のアカウントを選んで許可する
- 「会員」シートにkintoneの会員が並んだことを確かめる
- 続けて
pushAttendanceCountsを実行し、kintoneの「今月の出席回数」が入ったことを確かめる

- 左の「トリガー」(時計)を開き、「トリガーを追加」を押す
pullMembers、イベントのソース「時間主導型」、「日付ベースのタイマー」、「午前6時〜7時」で保存する- 同じように
pushAttendanceCountsを「午後11時〜午前0時」で保存する

出席回数を毎晩送っておけば、月の途中でもkintoneで「今月まだ1回も来ていない会員」を絞り込めます。受付はその一覧を見て、休みがちな会員に声をかけます。
応用
kintoneの変更をすぐにスプレッドシートへ流す
毎朝の写しでは、昼に入会した会員が夜のレッスンの出席一覧に出てきません。すぐに反映したいときは、kintoneのWebhookを使い、レコードが追加・編集されたときにn8nへ知らせて、n8nからスプレッドシートの行を足すか書き換えます。kintoneのWebhookは送るだけで、GASのWebアプリに直接受けさせることもできますが、失敗の記録が残るn8nを挟んだほうが追いやすくなります。この組み方は「Google Workspaceとn8nをつなぐ」の記事で書いています。
フォームの申込をkintoneに入れる
体験申込のGoogleフォームの回答も、kintoneの顧客台帳に入れられます。GASのフォーム送信時のトリガーから POST /k/v1/record.json を呼ぶか、n8nでシートの行の追加を拾って送ります。体験の段階ではスプレッドシートの予約一覧で受付が対応状況を付け、入会が決まった人だけkintoneに入れる、という分け方もできます。台帳を体験の人で埋めたくないなら、こちらが向きます。
集計はスプレッドシートで見る
kintoneのグラフでも月ごとの入会数は出せますが、出席や月謝と組み合わせた集計はスプレッドシートとデータポータル(旧Looker Studio)のほうが速く作れます。pullMembers で写した「会員」シートに入会日と退会日の列も足しておけば、データポータルで月ごとの在籍数が見られます。
できないことと、その代わり
| できないこと | 理由 | 代わりの手 |
|---|---|---|
| ライトコースでGASから同期する | ライトコースは外部サービス連携に対応していない | スタンダードコースにする。ライトのままならCSVの書き出しと読み込みで手で同期する |
| スプレッドシートとkintoneの両方で同じ項目を直す | 次の同期で片方の値が消える | 項目ごとに正を決め、正でない側は書き換えない |
| APIトークンで「誰が」変えたかを残す | トークンの操作はAdministratorとして記録される | 変えた人を残したいなら、スプレッドシートに「更新者」の列を作って一緒に送る |
| 計算のフィールドやステータスにAPIやCSVで値を入れる | kintone側で計算・管理される項目は外から入れられない | 元になるフィールドに値を入れて、kintone側で計算させる |
| 1回で何千件も更新する | 1回の更新は100件まで | 100件ずつに分けて送る。GASの実行は1回6分までなので、件数が多いときは日を分ける |
| 6名だけで契約する | ライトとスタンダードは10ユーザーから(ワイドは1,000ユーザーから) | 10ユーザー分の料金で考えるか、触る人が少ないならスプレッドシートとAppSheetで続ける |
| kintoneのレコードをスプレッドシートで直接編集する | 2つは別のデータ | 編集はkintoneで。スプレッドシートは写しとして見るだけにする |
よく詰まるところ
| 症状 | 原因 | 直し方 |
|---|---|---|
GASの実行ログに kintone と状態コードの付いた認証のエラーが出て止まる | トークンの生成後に「アプリを更新」を押していない、または別アプリのトークン | アプリの設定で「アプリを更新」を押す。トークンを作ったアプリとIDが合っているか確かめる |
| 403で「権限がありません」 | トークンのアクセス権に「レコード編集」が無い | APIトークンの画面でチェックを足し、保存して「アプリを更新」 |
| CSVの読み込みで文字化けする | Excelで開いて保存し直し、文字コードが変わった | スプレッドシートから書き出したCSVをそのまま読み込むか、「文字コード」をUTF-8にする |
| 金額の列でエラーになる | 「12,000円」のように円や桁区切りが入っている | スプレッドシートで数値だけにしてから書き出す |
| 「更新キー」に会員番号が出てこない | 会員番号のフィールドに「値の重複を禁止する」が入っていない | フィールドの設定でチェックを入れて「アプリを更新」 |
| 更新したつもりが、同じ会員が2件に増えた | 「レコードの追加のみ」で読み込んだ | 増えた分を消し、次からは「レコードの更新と追加」と更新キーを使う |
pushAttendanceCounts が全部失敗する | kintoneに無い会員番号が1件混じり、その100件がまとめて取り消された | 「会員」シートを pullMembers で写し直してから実行する。会員番号はkintoneで作る |
TypeError: Cannot read properties of undefined (reading 'value') | コードのフィールドコードとkintoneのフィールドコードが違う | アプリの設定のフォームでフィールドコードを確かめ、コードを合わせる |
| 日付がずれて前の日になる | スプレッドシートやスクリプトのタイムゾーンが日本でない | 「ファイル」→「設定」とApps Scriptの「プロジェクトの設定」でタイムゾーンを東京にする |
担当講師の列が [object Object] になる | 担当講師をkintoneのユーザー選択にしていて、値が配列で返る | r['担当講師'].value.map(function (u) { return u.name; }).join('、') に書き換える |
| 1万件を超えたあたりで取得が止まる | offset を使って取っていて、上限の1万件に当たった | この記事のコードのように $id で区切って取る |
| 朝の同期が時々失敗している | 一時的な通信の失敗や、同じ時刻の大量のAPIでの混雑 | トリガーの失敗通知をメールで受け、続くようなら実行の時刻をずらす |