はじめに
この記事はMedley(メドレー)Advent Calendar 2025 24日目の記事です。
医療プラットフォーム本部 歯科診療所事業部でDENTISという歯科診療所向けのプロダクトを開発している牧です。
先日、11月6日、7日に島根県松江市で開催された、RubyWorld Conference 2025にて、私が開発に携わっているDENTISの開発事例について講演させていただきました。その時のRecapと、時間の都合上話せなかった部分について、書かせていただこうかと思います。
なお、メドレーはGoldスポンサーとしてRubyWorld Conferenceをスポンサードしており、弊社社員による参加レポートもでていますので、合わせて御覧ください。
今年は動画配信がなかったのですが、発表資料は既に公開されております。本文だけで内容が分かるように書いているつもりではありますが、もし発表資料も見たいという方がいらっしゃいましたら、下記から参照可能です。
お話した内容について
RubyWorld Conferenceとはご縁もあり、時々お話させていただく機会をいただいているのですが、今回で4回目の登壇となります。6年ぶりの登壇ということで、私がその間、開発していた歯科診療所向けのシステムの開発事例についてお話させていただこうとCFPを提出し、採択していただきました。
6年間やっていることなので、話す内容には困らなかったのですが、話したいことがありすぎて絞り込むのには苦労しました。特に日本の医療を取り巻く問題、難しさなどの全体感をお話したかったので、プロダクトのデモをしながら想像しやすい部分については分かりやすさを強調しつつ、普段触れることのない難しい部分を深堀りした構成にしました。
1. 医療会計の複雑さについて
1つ目は医療会計の複雑さについてお話させていただきました。保険会計も私ぐらいの世代だと3割負担ぐらいしか関わることがないので、他の制度についてはよく知らない方も多かったりするのですが、日本の保険制度は複雑で、保険、全国公費、地方公費、高額療養費、その他諸々の保険制度の組み合わせによって、患者負担金額が決定されます。
特に75歳以上になると加入することになる、後期高齢者医療制度は被保険者の収入によって保険割合が変動したり、高額療養費や難病公費などの他の制度と組み合わせることで、計算がとても複雑になります。
セッション中では75歳後期高齢者(2割負担)、高額療養費利用、難病公費で20万円の医療費がかかった場合に患者負担金額が何円になるかの事例を紹介しましたが、改めてここでも紹介します。
それぞれの医療制度には処理の優先度があり、基本は保険→高額療養費→全国公費→地方公費の順番で適用されます。また全国公費や地方公費の中にも優先度が定められており、複数の公費を適用する場合には、優先度の高い公費から順番に適用して、それぞれで補助される金額を計算します。(基本と書いているのは、例外があるからなのですが、それをお話すると長くなってしまうので省略します。)
| 処理順 | 保険・公費 | 内容 | 補助される金額 | 残金額 |
|---|---|---|---|---|
| 医療費合計 | 200,000 | |||
| 1 | 後期高齢者医療 | 75歳から加入する医療制度。被保険者の収入区分に応じて負担割合が決定される。今回の例では2割になるものとして計算する | 160,000 | 40,000 |
| 2 | 高額療養費 | 収入に応じて、患者の自己負担上限金額が設定される。今回の例では18,000円が上限 | 22,000 | 18,000 |
| 3 | 難病公費 | 特定の難病に対して適用できる制度。収入に応じて自己負担金額上限が設定される。今回のケースでは5,000円が上限 | 13,000 | 5,000 |
| $\hspace{3em}$ | $\hspace{8em}$ | $\hspace{8em}$ | $\hspace{4em}$ | $\hspace{3em}$ |
このような感じで、複数の医療制度を組み合わせると最終的に患者負担金額は5,000円になりますが、国に医療費を請求する際には、それぞれの医療制度ごとに金額を算出して提出する必要があり、様々な医療制度の計算に対応する必要があります。
講演では時間の都合もあり、これ以上複雑になる事例をお話しできなかったのですが、地方公費の制度が含まれると更に複雑になります。
地方公費について
地方公費は、地方自治体ごとに独自の行政サービスとして実施している医療公費で、全国的にあるもので、よく知られた身近なものとしては「乳幼児医療費制度」などがあります。乳幼児医療費は、保険の乳幼児における患者負担割合が2割となる残りの2割を乳幼児医療費制度が負担することで乳幼児の医療費が無料となります。先ほどと同じフォーマットで記載してみましょう。
| 処理順 | 保険・公費 | 内容 | 補助される金額 | 残金額 |
|---|---|---|---|---|
| 医療費合計 | 20,000 | |||
| 1 | 国民保険 or 社会保険 | 6歳までの医療費は2割負担となる | 16,000 | 4,000 |
| 2 | 乳幼児医療費 | 患者負担金額の残りを全て負担する | 4,000 | 0 |
| $\hspace{3em}$ | $\hspace{8em}$ | $\hspace{8em}$ | $\hspace{4em}$ | $\hspace{3em}$ |
乳幼児医療費などは、全国的にほぼ同じ制度で運用されており、大体全額負担となりますが、自治体によって様々な医療費助成制度があり、それぞれ計算式や適用条件が異なります。例えば、6歳以上の子供に適用される子ども医療費などは、完全に無料になるケース、患者負担金額が最大XXX円になるまで補助してくれるケース、また適用範囲も15歳までだったり18歳までだったりと様々です。
より、地域の独自性を出した地方公費もあります。例えば以下のようなものです。
-
大垣市 老人医療費助成制度
大垣市に住む71歳〜74歳までに適用される医療費助成制度。負担金額の1割を負担(月額上限18,000円まで補助) -
京都市 学童う蝕対策事業
京都市に住む小学生の虫歯治療に対して、健康保険の自己負担額を助成する。虫歯の治療以外には使用できない。
このような独自色のある地方公費が全国的に散在しており、それぞれが少しずつ異なるため、プログラムとして一纏めにできない、という問題があります。現在デジタル庁を中心にこれらの地方公費情報をマイナンバーに紐づけるPMH(Public Medical Hub)という施策が進んでいますが、こちらもまだ地方公費の網羅には至っておらず、また地方公費を使用した際に自治体へ提出する独自フォーマットの帳票類については従来通りの運用、となっていることもあり、当面複雑さは解消されない見込みです。
このあたりは、自治体の人口分布や財政状況とも関係することもあり、一律制度を揃えるのが難しいところではありますが、せめて制度をいくつかのパターンに類型し、フルオーダーではなくパターンオーダーにするぐらいの足並みに揃えて欲しいところではあります。
2. 保険医療の複雑さについて
日本の保健医療は標準医療として国が定めたものであり、一つ一つの医療行為に対して算定できる要件と保険点数が定められています。そのため保険医療を行う際には、処置した行為一つ一つを記録する必要があります。例えば、歯科における歯周病の治療を受けた際には、概ね以下のような処置が記録されます。
| 処置行為 | 処置内容 | 保険点数 |
|---|---|---|
| 初診 or 再診 | 医師が患者の訴えから病名を診断し、治療計画を立てるのを「初診」、以降立てた治療計画に基づき、患者を診察するのを「再診」として算定する | 初診時 257点、再診時 58点 |
| 歯周基本検査 | 患者の歯周ポケットの深さを測定し、歯肉からの出血、歯のぐらつき、歯垢の付着状況、歯肉の炎症状態などを確認し、歯周病の進行度を評価する検査、残存する歯の数によって点数が異なる | 20歯以上の場合200点、10〜19歯の場合100点、10歯未満の場合50点 |
| 歯科エックス線撮影(全額撮影) | エックス線を用いて口腔全体を撮影する | デジタルの場合402点 |
| 歯科疾患管理料 | 継続的な管理が必要な患者の口腔状態を評価し、再発予防や重症化予防を目的とした計画作成・指導を行った場合に取れる管理料 | 初診月80点、初診月以外100点、文書による提供を行った場合は10点加算 |
| スケーリング | 歯に付着した歯垢を除去する処置 | 72点(1/3顎)、以降1/3の部位を処置するごとに38点加算 |
| 機械的歯面清掃処置 | 専用の機械を用いて歯ブラシでは落としきれない歯垢を除去する処置 | 72点 |
| 歯科衛生士実地指導 | 歯科衛生士による歯の磨き方指導を受けた場合に算定できる管理料 | 80点 |
これらの処置には細かい算定要件ルールや算定回数上限が決まっており、ルールを逸脱すると国への請求時に審査が通らず、医療費が入金されません。ですので、システム導入を決める際に、このエラーチェックのパターンをどれだけ網羅しているか、というのが判断指標として重視している方も多くいます。
この算定要件のルールについては、厚労省からでている通達文書等で明文化されているものもあれば、審査する人によって判断が分かれるようなケースもあり、完全に網羅するのは難しく、現在はよく指摘されるものを中心に順次チェックを追加し、改善を進めている段階となります。
診療報酬改定について
これらの算定ルールや処置点数については、2年に1回見直しが行われ、要件の厳格化・緩和などのルールの変更、点数の増減、処置の追加・廃止、帳票の追加・廃止等が行われます。毎年12月頃から議論が行われ始め、3月ぐらいに各種情報が揃い始め、6月から(2022年までは4月だった)施行されるのですが、変更量が多かったり、中々情報が出揃わなかったりとプロダクト開発においてはスリリングなタイミングがあります。
次回は2026年6月に施行なのですが、穏やかに終わることを祈るばかりです。
処置と加算について
RubyWorld Conferenceでお話した時に、時々発表中に出てきた加算ってなんですか?という質問を受けたので、補足しますと、保険診療の医療行為には基礎点数と加算点数があり、追加の要件を達成すると、加算点数を得られるというものがあります。
例えば、上にも記載していますが「歯科疾患管理料」の基礎点数は80点(初診月)ですが、管理計画を記した紙を渡すと追加で10点算定できます。みなさんも歯科医院で治療を受けた際に、紙を受け取ったことがあるのではないでしょうか。このような形で、追加要件を達成した時に追加で取れる点数のことを加算といいます。
他にも以下のような加算は(医科・歯科関係なく)よく見るので、通院の際に発行される明細書を意識して見てみると興味が湧くかもしれません。
- 医療DX推進体制整備加算 … マイナンバーカードリーダーの設置や電子処方箋導入医療機関など医療機関の医療DXの推進度合いに応じて算定できる点数(現在1〜6まであり、選択肢を表示するだけでも大変なので、個人的にはこれ以上増えて欲しくないと思っている)
- 明細発行体制加算(再診時に+1点加算)… 医療機関が診療報酬明細書を患者に無料で発行する体制を持っている場合に算定出来る加算。必ず明細書を発行する必要はないが、この点数を算定している場合は、明細書をくださいというリクエストに応える義務がある
- ベースアップ評価料 … 医院で働く医療従事者に対して賃上げを行った場合に算定できる加算。賃上げ状況に応じて加算が変動する
3. 歯科特有の情報処理について
医療機関は毎月、実施した保険診療の保険負担分を国に請求するために、集計して国に提出しています。これをレセプトと呼ぶのですが、最近はオンラインによる電子請求が普及してきたこともあり、UKEファイルと呼ばれる電子フォーマットで請求されます。ここでは余談になりますので、詳細は述べませんが、この電子レセプトのフォーマットについては、以前に同じチームの平林が電子レセプトをテーマとしたブログを記載していますので、そちらを参照してください。(こちらも非常に難解な仕様となってます。)
レセプトは医科・歯科・調剤とそれぞれに異なるデータフォーマットが存在するのですが、歯科の特徴としてはこの請求仕様の中で、歯の部位を表現するためのデータフォーマットが規定されているため、この表現仕様をベースにして、口腔内のデータフォーマットを定義しています。
歯のデータ構造について
保険診療における人間の歯は永久歯32本、乳歯20本と定義されていて、それぞれの歯は部位コードと呼ばれる4桁の数値で表現されます。ここで社会保険診療報酬支払基金のサイトで公開されている「電子レセプト作成の手引き」から部位コードと歯の対応関係を示した図を引用します。

(社会保険診療報酬支払基金「電子レセプトの作成手引き」から引用)
これに状態コード1桁と部分コード1桁を加えた6桁の数値を歯式コードと呼び、歯を表現するデータフォーマットとなります。
状態コードは歯の状態を表現します。例えば、右側上顎前歯で歯がある状態だと101100、歯がない状態(欠損歯)だと、101120と表現されます。
部分コードは歯の一部を示す部分的な表現として用いられます。私もこの仕事をするまで知らなかったのですが、歯の治療の中には、歯を2つに分割して一部分を残して使うケースがあり、そのような場合に用います。(ただしどの歯にも使われるわけではなく、保険診療上は第1大臼歯のみが対象となります。)
例えば、ヘミセクションと呼ばれる処置があるのですが、これは歯を分割し、分割した片方を抜歯する処置です。
詳細を話すとややこしくなるので、簡単な例だけ紹介すると、下顎の第1大臼歯を2つに分割した場合は遠心根と近心根に分かれるので、歯式コードは104607と104608に分かれ、抜歯する方の状態コードを2(欠損歯)にすることで表現できます。
口腔情報の表現について
基本となる歯のデータ仕様について説明を終えたところで、口腔内全体の表現について説明します。
先程の状態コードで表現できるものもありますが、レセプトのデータ仕様には表現できないものもあり、そこは独自のフラグをもたせることで対応しています。
例えば、一部抜粋ですが、DENTISでは以下のような状態を表現できるようなデータ構造にしています。
| 歯の状態 | 説明 | 表現方法 |
|---|---|---|
| 現存歯 | 歯が存在する状態 | 状態コードで表現 |
| 欠損歯 | 歯が抜けた状態 | 状態コードで表現 |
| 残根 | 歯が折れて根元だけ残っている状態 | 状態コードで表現 |
| 未萌出 | まだ歯が生えてない状態 | 独自フラグで表現 |
| 根分 | 歯を半分に割った状態 | 部分コードで表現 |
| クラウン | 歯の治療で被せ物をした状態 | 独自フラグで表現 |
| 充填 | 歯の治療後、充填剤を入れた状態 | 独自フラグで表現 |
| インレー | 歯の治療後、詰め物をした状態 | 独自フラグで表現 |
| ブリッジ | 差し歯をした状態 | 独自フラグで表現 |
口腔情報データの要件
さて、ここで患者の口腔情報を定義するときの要件ですが、システムがカルテとしての役目を果たすには、YYYY年MM月DD日の口腔情報は、このようなデータだったという情報を全て時系列で記録できる必要があります。
例えば12月15日に抜歯(歯を抜くこと)をした場合、12月15日からは口腔情報から対象の歯はなくなりますが、12月14日までは、歯は存在していたわけなので、そのようにデータを返す必要があります。
例
12月15日に抜歯手術を登録。
12月15日時点 抜歯を行ったため、大臼歯が1本欠損の状態となっている。
12月14日時点 抜歯前のため、大臼歯はまだ存在している状態となっている。
この要件を達成するためにどのようなデータ構造を設計したかについて説明します。
原初の設計:口腔情報変更時にスナップショットを取る作戦
当初の設計では、あまり難しいことを考えず、歯の状態が変更されたタイミングで、全ての歯のスナップショットを取得し、JSONで保存するような設計を考えていました。口腔情報が変化したタイミングで、レコードを追加すれば良いので、実装が簡単だったというのと、口腔情報を取得するときの負荷が軽いのが利点です。
ただし、この口腔情報のモデリングは早々に破綻します。例えば、抜歯手術を登録したタイミングでレコードが追加されますが、抜歯手術を取り消す(削除するケース)もあります。例えば以下のようなケースです。
- 登録時に間違えて抜歯した歯と異なる歯を登録していた
- 請求審査時に指摘があり、登録内容を変更する必要がでてきた
このようなケースでは、登録したデータを削除して、新たに別のデータを再登録する可能性があり、それに伴い口腔情報のデータも合わせて修正する必要がでてきます。場合によっては数か月前に遡って修正する可能性もあり、その間に新たな口腔情報データが追加で登録されていた場合は、その期間から現在に至るまでの全てのデータを時系列に沿ってまとめて修正する必要が出てきます。整合性を持ってデータを修正するのは困難ですし、何よりJSONになっているので、修正工程が複雑になります。
上記を踏まえた新方式: 1歯単位で差分を記録する方式
以上の問題点を踏まえて、口腔情報全体のスナップショットを取得する方式には無理があるため、別の方式を検討しました。1歯1歯が独立していないと、過去情報を変更するなどのケースには対応できません。改めてスキーマを見直した結果が以下となります。
新しい方式では、歯1本単位での差分を記録するように作成しているため、以前のような過去に遡ってデータが修正されたとしても、他に影響を与えることなく変更することができます。その代わり取得時には副問合せを含む、少々難しいクエリを書く必要があります。歯の分割する場合などのエッジケースもあるので、本番のSQLはもう少し複雑ですが、以下のような要領で取得します。
select * from 歯の情報 as a
inner join(
select b.部位コード, b.部分コード, max(順番) as 歯単位の順番の最大値 from 歯の情報 as b
inner join(
select 部位コード, 部分コード, max(更新日付) as 歯単位の最新日付 from 歯の情報 as c
where 更新日付 <= 基準日 and 口腔情報ID = 患者.口腔情報ID
group by 部位コード, 部分コード
) as c
on c.部位コード = b.部位コード and c.順番 = b.順番 and c.歯単位の最新日付 = b.更新日付
group by 部位コード, 部分コード
) as d
on a.部位コード = d.部位コード and a.部分コード = d.部分コード and a.順番 = b.歯単位の順番の最大値
簡単にSQLの解説をすると、
- 特定日付以前に登録されたすべての歯を取得し、部位コード、部分コードでgroup byをかけ、更新日付の最大値を返す
- inner joinで結合すると、各部位の最新日付のデータがすべて取得できますが、同一日に登録された複数の歯の履歴があるので、再度部位コード、部分コードでgroup byして、最大の順番を取得する
- この過程で取得したデータを再びinner joinで結合すると、目的のデータが得られる
実際はRuby on Railsで書かれているので、Arelで上記クエリを定義し、find_by_sqlで取得した結果をJSONに変換してクライアントに送っています。
歯式に関する補足
当日の発表内容には盛り込めなかったのですが、この歯式の紙面上での表現は歯科業界特有の記法がありまして、中々面白い仕様になっています。例えば上下顎、右側第二大臼歯から左側第二大臼歯を含めた全ての歯を表現する場合は、以下のような記法になります。(親知らずは抜いています。)
["101700", "101600", "101500", "101400", "101300", "101200", "101100",
"102100", "102200", "102300", "102400", "102500", "102600", "102700",
"104700", "104600", "104500", "104400", "104300", "104200", "104100",
"103100", "103200", "103300", "103400", "103500", "103600", "103700"]
# ちょっとずれるのはご容赦ください
7╂7
━╋━
7╂7
読み方としては、上下・左右を分割する線があり、連続して歯が並ぶ場合はーで省略しても良いという記述ルールになります。このルールは4歯以上連続して同じ歯が並ぶ場合に適用されます。
["101300", "101200", "101100"]
321┃
━━━┛
["101400", "101300", "101200", "101100"]
4─1┃
━━━┛
また、中心をまたぐ場合も同様のルールが適用されます。
["101200", "101100", "102100"]
21┃1
━━┻━
["101200", "101100", "102100", "102200"]
2╂2
━┻━
# 乳歯が混ざるケースもあります
["101200", "101100", "102100", "106200", "102300"]
21┃1B3
━━┻━━━
他にも状態コードによって、◯で囲ったり、歯と歯の間に隙間がある場合は、▲で表現するなど細かいルールがありますが、プログラムの腕試しにはちょうどいい難易度かと思います。興味があれば是非挑戦してみてください。
まとめ
RubyWorld Conference 2025では、歯科システムの開発事例として、保険診療の複雑さ、保健医療の複雑さ、歯科特有の情報処理についてお話させていただく予定でした。元々全部は話しきれない(が、全部話したい)というつもりで臨んだのですが、予想通り時間が足りず、お聞き苦しい点もあったかと思いますが、改めて文字にする機会をいただけたので、多くの人に伝わってくれれば幸いです。
最後に
メドレーでは、歯科に限らず、医療ドメインの複雑さに挑戦し、一緒に社会問題を解決していくメンバーを募集しております。もし興味を持たれた方は、下記からご連絡ください。





