
【全29テーブルの全体配線図】工場の制御盤屋が組んだ、競馬ブックKD3とSQLiteの「複合主キー」設計思想!Alma Core Pro開発記 #4
どうも、アルマコアです!
本業は工場の自動化機械やシーケンサー(PLC・制御盤)の設計に向き合っている、ただの現役機械エンジニアです。
前回の第3回にも、たくさんの「スキ」やフォローをいただき本当にありがとうございます!
「東京11Rを引いたはずが新潟が飛び出してくる」という、競馬ブック独自コードの洗礼に共感の声をいただき感無量です(笑)。
さて、固定長バイナリの切り出し(第2回)に成功し、マスタ統合(第3回)によって「00=京都」「-0051=GⅠ」といった暗号も美しい日本語として意味が通るようになりました。
「よっしゃ! これで部品の加工と規格変換はぜんぶ終わったで!」
……と、深夜に達成感に浸りながらSQLiteの中身を眺めていた私ですが、ふと冷静になって手が止まりました。
目の前にあるのは、
出馬表(kol_den1・kol_den2)
競走成績(kol_sei1・kol_sei2・kol_sei3)
トラックマンのコメント・次走メモ(kol_com1)
確定・予想オッズ(kol_kod・kol_ods・kol_ods_detail 等)
競走馬マスタ(kol_uma)や血統マスタ(kol_ket) ……などなど、合計29個ものテーブルがただバラバラに転がっているだけの光景だったのです。
機械設計の世界で例えるなら、「公差±0で精密加工した29個のギアやプーリー、ベアリングが工場の床一面に転がっているだけで、まだ1本のシャフト(回転軸)も通っていない状態」です。
部品単体がいくらピカピカに仕上がっていても、軸を通してガチッと噛み合わせなければ、機械は1ミリも動きません!
今回は、このバラバラな29個のテーブルに「共通のシャフト(複合主キーと結合キー)」を貫通させ、コンマ数秒で連動する「1つの生き物(リレーショナルデータベース)」へと組み上げた、エンジニア魂のリレーション設計をお届けします!
1. 工場の床に転がる「全29テーブル」の正体
まず、「なんで競馬のデータごときでテーブルが29個にも分かれとんねん!」と思われるかもしれません(笑)。
でも、競馬ブック(KD3)の生データを実用レベルで動かすために正規化していくと、必然的にこの数に達するのです。
実際に私が『Alma Core Pro』のSQLite内に削り出した全29テーブルの内訳がこちらです。
① レース・出馬表・成績・コメント系(8テーブル)
kol_den1:出馬表(レース条件・コース・展開予想などレース単位の親情報)
kol_den2:出馬表(枠番・馬番・負担重量・調教タイム・短評・SP指数など出走馬単位の子情報)
kol_sei1:競走成績(天候・馬場・ラップタイム lap_time_01〜18・払戻金などレース単位の親情報)
kol_sei2:競走成績(確定着順・走破タイム・前半3F first_3f_time・上り3F last_3f_time・通過順など馬単位の子情報)
kol_sei3:競走成績(制裁内容 sanction_details などレース付帯情報)
kol_tok1:特別登録(レース条件などの親情報)
kol_tok2:特別登録(登録馬ごとの子情報)
kol_com1:コメント(騎手・厩舎談話 jockey_stable_comment、次走へのメモ memo_next_race 等)
② オッズ親テーブル&明細テーブル(10テーブル)
kol_kod / kol_kod2 / kol_kod3:確定オッズ親テーブル(単複枠・馬連ワイド馬単・3連複3連単)
kol_ods / kol_ods2:予想オッズ親テーブル
kol_kod_detail / kol_kod2_detail / kol_kod3_detail:確定オッズ明細(組番ごとのオッズ実数値)
kol_ods_detail / kol_ods2_detail:予想オッズ明細(組番ごとの予想オッズ実数値)
③ 競走馬・人物・血統マスタ系(5テーブル)
kol_uma:競走馬マスタ(血統登録番号、父母名、芝・ダート・距離別の通算着度数など)
kol_kis:騎手マスタ(騎手コード、所属、通算・本年成績など)
kol_kyu:厩舎(調教師)マスタ(厩舎コード、所属、成績など)
kol_syu:種牡馬マスタ(種牡馬コード、距離適性、重馬場適性など)
kol_ket / kol_ket5:3代・5代血統マスタ
④ コード変換マスタ&拡張指数・管理系(6〜7テーブル)
7大コード表マスタ:mst_venue(場所) / mst_condition(条件) / mst_condition_addition(条件付加) / mst_horse_mark(馬記号) / mst_coat_color(毛色) / mst_breed(血種) / mst_birthplace(産地)※さらに予想ペース mst_expected_pace、脚質 mst_running_style 等
kol_stride:ストライド指数テーブル(先行力 senko、追走力 tsuiso、持続力 jizoku、瞬発力 shunpatsu、SAV、STなど)
dl_history:インポート履歴管理テーブル
……どうですか、この圧倒的な物量(笑)!
これだけの精密部品が、最初は全部バラバラに存在しているわけです。
2. 壁:何を「軸(シャフト)」にして29個を串刺しにするか?
私が自作GUI画面(『Alma MDI Pro』)で絶対に実現したかったのは、こういうことでした。
「出馬表に並んでいるこの馬の、過去走の走破時計と、今回の調教タイムと、トラックマンの次走メモと、リアルタイムの予想オッズと、父馬の重馬場適性を、1画面に0.03秒で同時に呼び出したい!」
これを実現するには、バラバラのテーブル同士を寸分の狂いもなく繋ぎ止める「共通のシャフト(主キー / 結合キー)」を設計しなければなりません。
「1つのテーブルの中で、何を主キー(PRIMARY KEY)に据えればデータが重複せず、かつ他のテーブルと一瞬で結合できるのか?」
仕様書と睨めっこしながら導き出した結論は、すべてのデータを「3本のメインシャフト(主軸)」に分類して串刺しにする設計でした。
① 第1の軸:「レース単位」で貫く5連複合キー
まず、レース全体の条件を持つ親テーブル(kol_den1、kol_sei1、kol_sei3、kol_tok1、kol_ods 等)です。
これらを一意(ユニーク)に特定するため、私は以下の5つのカラムを組み合わせた「5連複合主キー」を定義しました。
SQL
PRIMARY KEY (year, venue_code, times, day, race_number)
year:開催年(例:2026)
venue_code:場所コード(例:04=東京)
times:回次(第何回開催か)
day:日次(何日目か)
race_number:レース番号(例:11=11R)
「開催日(race_date)だけじゃアカンの?」と思うかもしれませんが、同日に東京・京都・新潟の3場開催があるため日付だけでは一意になりません。
さらにKD3の仕様上、年・場所・回・日・Rの5点セットこそが、すべてのテーブルに共通して刻まれている「絶対座標(位置決めピン)」だったのです!
② 第2の軸:「レース×馬番」で貫く6連複合キー
次に、1つのレースに最大18頭(データ仕様上は最大28頭枠)が出走する子テーブル(kol_den2、kol_sei2、kol_com1)です。
ここは先ほどの5連キーの末尾に、horse_number(馬番)を1本追加した「6連複合主キー」でガチッと固定しました。
SQL
-- kol_den2(出馬表・馬単位)、kol_sei2(成績・馬単位)、kol_com1(コメント)の主キー
PRIMARY KEY (year, venue_code, times, day, race_number, horse_number)
さらに、オッズ明細テーブル(kol_ods_detail や kol_kod_detail)では、同じレースの中に単勝・複勝・馬連などの券種と組番がぶら下がるため、
SQL
PRIMARY KEY (year, venue_code, times, day, race_number, odds_type, combination)
という「7連複合主キー」を採用しました(※特別登録 kol_tok2 は枠順確定前で馬番がないため、horse_code を使った6連キーにしています)。
これで、当日の出馬表(kol_den2)に単勝予想オッズ(kol_ods_detail)を横付けするときも、5連キー+馬番(combination)を噛み合わせるだけで、1ミリの狂いもなくピタリと同期します!
③ 第3の軸:「時間軸(過去〜現在)」を貫く個体識別シャフト
そして、一番重要なのが「今走っているこの馬の、過去の成績やコメントをどうやって引っ張ってくるか?」です。
過去のレースは当然、開催年(year)も場所(venue_code)も馬番(horse_number)も毎回バラバラです。
ここで時間軸を超えて1頭の競走馬を縦串で貫く絶対のシャフトが、horse_code(血統登録番号)です!
競走馬マスタ(kol_uma)や血統マスタ(kol_ket / kol_ket5)は、この horse_code 単体を PRIMARY KEY として設計しました。人物系・種牡馬系も同様に単一主キーで整理しています。
競走馬マスタ(kol_uma):PRIMARY KEY (horse_code)
騎手マスタ(kol_kis):PRIMARY KEY (jockey_code)
厩舎マスタ(kol_kyu):PRIMARY KEY (stable_code)
種牡馬マスタ(kol_syu):PRIMARY KEY (sire_code)
3. 機械エンジニア流「総組立図(ER図)」と魂のJOINクエリ
部品の規格とシャフトの径が決まれば、あとは工場の制御盤の「全体配線図」や機械の「総組立図(アセンブリ図)」を描くように、29テーブルの結合ルート(ER図)とインデックス(CREATE INDEX)を張り巡らせていきます。
縦の配線(当日のレース分析):kol_den1(親) ⇔ kol_den2(出走馬) ⇔ kol_ods_detail(予想オッズ) ⇔ mst_venue / mst_condition(日本語変換マスタ)を、レース複合キーで直結!
横の配線(過去走・適性の深掘り):kol_den2 の horse_code から ⇔ kol_sei2(過去成績) ⇔ kol_com1(過去の次走メモ・厩舎コメント) ⇔ kol_uma(通算成績・馬場適性)を、horse_code インデックス(idx_kol_sei2_horse 等)で超高速結合!
この総組立図通りにインデックスを張り、記念すべき最初の結合テスト(JOIN クエリ)を走らせたときのコードがこちらです。
SQL
SELECT
v.venue_name AS "競馬場",
d1.race_number AS "R",
d1.special_race_name_7 AS "レース名",
d2.bracket_number AS "枠",
d2.horse_number AS "馬番",
d2.horse_name AS "馬名",
d2.jockey_name_short AS "騎手",
o.odds_value / 10.0 AS "予想単勝オッズ",
d2.training_short_comment AS "調教短評",
d2.speed_index_1_race_ago AS "前走SP指数",
u.dirt_heavy_aptitude AS "ダ重適性",
c.memo_next_race AS "前走時・次走メモ"
FROM kol_den2 d2
-- ① レース親情報(kol_den1)を5連複合キーで結合
INNER JOIN kol_den1 d1
ON d2.year = d1.year
AND d2.venue_code = d1.venue_code
AND d2.times = d1.times
AND d2.day = d1.day
AND d2.race_number = d1.race_number
-- ② 場所マスタ(mst_venue)でコードを日本語に変換
LEFT JOIN mst_venue v
ON d2.venue_code = v.venue_code
-- ③ 予想オッズ明細(kol_ods_detail)を6連キー+券種で結合
LEFT JOIN kol_ods_detail o
ON d2.year = o.year
AND d2.venue_code = o.venue_code
AND d2.times = o.times
AND d2.day = o.day
AND d2.race_number = o.race_number
AND d2.horse_number = o.combination
AND o.odds_type = '01'
-- ④ 競走馬マスタ(kol_uma)をhorse_codeで結合
LEFT JOIN kol_uma u
ON d2.horse_code = u.horse_code
-- ⑤ 前走のコメントテーブル(kol_com1)から最新メモを取得
LEFT JOIN kol_com1 c
ON d2.horse_code = c.horse_code
AND c.race_date = (
SELECT MAX(c2.race_date) FROM kol_com1 c2
WHERE c2.horse_code = d2.horse_code AND c2.race_date < d2.race_date
)
WHERE d2.race_date = '20261011'
AND d2.venue_code = '04'
AND d2.race_number = '11'
ORDER BY CAST(d2.horse_number AS INTEGER);
エンターキーを叩いた瞬間でした。
コンマ数秒(0.03秒の世界)で、画面にズラッと18頭の「馬名」「予想オッズ」「調教短評」「前走SP指数」「重馬場適性」「トラックマンの次走メモ」が、1行にピシッと整列して飛び出してきたのです!
「よっしゃーーーッ……!! 完全に繋がりよったわ……!!」
バラバラだった29個の金属パーツが、1台の精密機械として完全に噛み合って動き出した瞬間です!
工場の現場で、何百本もの配線を終えた制御盤のメインブレーカーを上げ、すべてのサーボモーターとセンサーが設計図通りに一斉に同期運転を始めた瞬間とまったく同じです。
あの背筋がゾクッとするような、エンジニア冥利に尽きる感動が深夜の自室に蘇りました。
4. ExcelのVLOOKUPでは絶対不可能。『Alma Core Pro』が提供する真の価値
少し想像してみてください。
もし、この競馬ブック(KD3)の生データを個人で手に入れて、Excelの VLOOKUP や XLOOKUP で繋ごうとしたらどうなるでしょうか?
数百万行ある過去成績(kol_sei2)や93万件を超えるコメント(kol_com1)を開いた瞬間、Excelはメモリ不足でフリーズして強制終了します(笑)。
そして何より、29種類もあるファイルの中から「どのファイルとどのファイルを、5連キーで繋ぐのか? 6連キーなのか? horse_code なのか?」という複雑怪奇なリレーション構造を自力で解読できなければ、データの海で100%遭難します。
第2回の「固定長バイナリの切り出し」、第3回の「独自コードのマスタ変換」、そして今回の「全29テーブルの複合キー&リレーション設計」。
この3つの巨大な壁があるからこそ、競馬ブックの濃密な生データを個人でデータベース化できる人間は世の中にほとんど存在しないのです。
だからこそ、私が心血を注いで開発したデータベース構築アプリ『Alma Core Pro』には、他にはない圧倒的な価値があります!
『Alma Core Pro』を導入すれば、
全29テーブルの生成(DDL)、主キー設定、7大マスタの初期登録、そして高速検索用インデックス(idx_kol_sei2_horse 等)の構築まで、初回起動時に全自動で完了します。
ユーザーは「どのキーとどのキーを繋ぐか」といった小難しい配線図を一切意識する必要はありません。
すでにすべてのシャフトが通り、完璧に配線された状態のSQLiteデータベース(keiba.db)が手に入るため、最初から「勝つためのデータ抽出・分析」だけに100%集中できるのです!
5. 次回予告:毎週落ちてくる「大量の圧縮ファイル」をどうやって流し込む?
さあ、これで全29テーブルの「強固な器」と「完璧な配線(リレーション)」が仕上がりました!
どんな複雑な条件検索にもコンマ数秒で応える、最強の心臓部の完成です。
……しかし、ここで次の現実的な問題が襲いかかります。
競馬のデータは、1回入れて終わりではありません。
毎週・毎開催ごとに、競馬道OnLineから大量の「出馬表」「成績」「オッズ」「マスタ更新分」のファイルが落ちてきます。
しかもそのファイル形式は、「.zip を解凍したら中から昭和の圧縮形式 .lzh が出てきて、さらにそれを剥いたら .kd3 が出てくる」という、あの悪魔のマトリョーシカ構造です(笑)!
何十個、何百個もあるこの二重圧縮ファイルを、まさか毎週手動で解凍する?
29個のテーブルへ1つずつ手作業で振り分けてインポートするの……?
「うん! そうそう!」
「こうやって、1つずつ皮剥いて!」
「電子レンジで、チ〜ンて!」
「アホかーーーッッ!!!」
「そんなん毎週やっとったら死んでまうし! 予想する時間なくなるわ!」
「ほな君、機械屋やろ! やったらここも完全自動化せんかい!」
深夜2時、ハイテンションの私は一人で激しい乗りツッコミをしていました(笑)。
次回、
【Alma Core Pro開発記 NO.5】昭和の圧縮ファイル「.lzh」を全自動で剥く!大量ファイル処理と29テーブル自動仕分けラインの構築劇。
二重圧縮ファイルをメモリ・一時領域で全自動解体し、ファイル名から対象テーブルを自動判別して29テーブルへ高速仕分けする「自動搬送ライン」の構築劇をお届けします!
どうぞお楽しみに!
☕ 最後まで読んでいただき、ホンマにありがとうございました!
「29テーブルの設計図、マニアックすぎて最高!」「複合キーとJOINの威力、めちゃくちゃ勉強になった!」と思っていただけたら、ぜひ画面下の「スキ(ハートマーク)」や「フォロー」をポチッと押していただけると、開発と執筆を続ける最高のガソリンになります!
◀️ 前の記事はこちら
👉 【第3回:東京11Rが新潟に化けた!?競馬ブックKD3の独自コード地獄をSQLiteマスタ統合でねじ伏せた執念】
📚 【全話イッキ読み】自作競馬ソフト開発記マガジン
👉 [Alma Core Pro / MDI Pro 開発マガジンを開く]
💡 「なぜExcelでもサーバー型DBでもなくSQLiteなのか?」原点の第1回はこちら
👉 [開発記 第1回を読む]
⏩ 次のお話はこちら
👉 【第5回:昭和の圧縮ファイル「.lzh」を全自動で剥く!自動仕分けラインの構築劇】
🤖 工場のPLC屋が本気で書く!痛快&重厚な自作Web小説も連載中
👉 [『転生したらイーロン・マスクが大統領だった件(笑)』第1話へ]
👉 [本格クライムSFサスペンス『アブダクション・プロトコル』第1話へ]