情報はある。転記の手間もある
データベースの調査では、CREATE TABLEを開いてカラム名を一覧化し、型や桁数、NULL制約、PRIMARY KEYを確認する。コメントがあれば、そこから論理名も拾う。
必要な情報はDDLに書かれている。それでも、Excelの設計資料や調査メモへ整理し直す時には、何度も同じところを見てコピーすることになる。
例えばNUMBERの括弧から精度とスケールを抜き出し、離れた場所にある主キーの定義をカラム一覧へ反映する。一つずつは簡単でも、テーブル数が増えると地味に時間がかかる。
そこでUTILIZABLEに、5つ目のツールとしてDDL Analyzerを追加した。DDLを貼り付け、人間が読めるテーブル定義へ整理するための道具だ。
EXP-007のCSV Diffではデータの新旧比較を扱った。今回は、そのデータが入るテーブルの定義を扱う。サイト全体を作った記録はEXP-006にまとめている。
DDLを貼って、Analyzeする
入力欄へCREATE TABLE文を貼り付け、DBを選んでAnalyzeを押す。Exampleボタンには、次のOracle DDLを用意した。
CREATE TABLE CUSTOMER (
CUSTOMER_ID VARCHAR2(10) NOT NULL,
CUSTOMER_NAME VARCHAR2(100) NOT NULL,
CUSTOMER_TYPE CHAR(1) DEFAULT 'A',
SALES_AMOUNT NUMBER(12,2) DEFAULT 0 NOT NULL,
CREATED_AT TIMESTAMP DEFAULT SYSTIMESTAMP,
CONSTRAINT PK_CUSTOMER PRIMARY KEY (CUSTOMER_ID)
);
COMMENT ON TABLE CUSTOMER IS '顧客';
COMMENT ON COLUMN CUSTOMER.CUSTOMER_ID IS '顧客ID';
COMMENT ON COLUMN CUSTOMER.CUSTOMER_NAME IS '顧客名';
COMMENT ON COLUMN CUSTOMER.CUSTOMER_TYPE IS '顧客区分';
COMMENT ON COLUMN CUSTOMER.SALES_AMOUNT IS '売上金額';
COMMENT ON COLUMN CUSTOMER.CREATED_AT IS '登録日時';
解析すると、テーブル名CUSTOMER、Table Comment「顧客」、カラム数5が表示される。カラムごとの情報は次のような表になる。
| Column | Comment | Type | Length | Precision | Scale | Nullable | Default | PK |
|---|---|---|---|---|---|---|---|---|
| CUSTOMER_ID | 顧客ID | VARCHAR2 | 10 | NO | YES | |||
| CUSTOMER_NAME | 顧客名 | VARCHAR2 | 100 | NO | NO | |||
| CUSTOMER_TYPE | 顧客区分 | CHAR | 1 | YES | 'A' | NO | ||
| SALES_AMOUNT | 売上金額 | NUMBER | 12 | 2 | NO | 0 | NO | |
| CREATED_AT | 登録日時 | TIMESTAMP | YES | SYSTIMESTAMP | NO |
スキーマ名が付いていれば、それも別に表示する。SQLの見た目を整えるところから、定義を一覧として読めるところまで進めた。
まずはOracleとPostgreSQL
初期版の対象はOracleとPostgreSQL。DB選択欄にはAuto、Oracle、PostgreSQLを用意した。
OracleのVARCHAR2やNUMBER、TIMESTAMP、SYSTIMESTAMPを使うDEFAULT式、PostgreSQLのcharacter varyingやnumeric、timestamp、jsonbなど、普段見かける型や表記を扱う。
AutoではVARCHAR2やJSONBなどの手がかりから、可能な範囲で方言を推定する。共通構文だけでDBを判定できなければ、Common / undeterminedとして解析する。numericだけでPostgreSQLだと断定するような扱いにはしていない。
完全なSQL構文検証器を作ることが目的ではない。テーブル定義を調査し、資料へ整理するための実用ツールとして、対応範囲を決めている。
NUMBER(12,2)のカンマは、区切りではない
最初に気をつけたのは、カラム定義をカンマで単純にsplitしないことだった。
SALES_AMOUNT NUMBER(12,2) DEFAULT 0
単純にカンマで分割すると:
SALES_AMOUNT NUMBER(12
2) DEFAULT 0
型の引数が、別のカラムのように切れてしまう。PostgreSQLのnumeric(12,2)でも同じことが起きる。
さらに、型の指定は複数行かもしれない。DEFAULTに関数が入り、その引数にもカンマがあるかもしれない。文字列の中にカンマや括弧が書かれている場合もある。
AMOUNT NUMBER(
12,
2
) DEFAULT round(coalesce(1, 2), 2),
LABEL VARCHAR2(30) DEFAULT 'A,B'
この例でカラムを分けるのは、round式の後ろにあるカンマだけだ。NUMBERの中、関数の中、文字列の中のカンマは残さなければならない。
実装では、まず文字列や引用識別子を一まとまりのトークンとして読み取る。シングルクォートの文字列と、ダブルクォートの識別子を、SQLの区切り記号と混同しないようにする。
その上で、開き括弧と閉じ括弧を数えて深さを追う。CREATE TABLEの外側の括弧を除いた定義部分で、深さ0にあるカンマだけをカラム定義の区切りとして扱う。
引用符内の文字 → 内容として保持
開き括弧 → 深さを増やす
閉じ括弧 → 深さを減らす
深さ0のカンマ → 定義を区切る
改行の位置に頼らないので、複数行の型指定にも対応できる。SQLコメントやエスケープされた引用符も考慮し、DEFAULTの表示には元の入力中の位置を使って原文を取り出すようにした。
型名とサイズを別の欄にする
DDLでは一続きになっている型の情報を、設計書で使う項目へ分ける。
| DDL | Type | Length | Precision | Scale |
|---|---|---|---|---|
| VARCHAR2(100) | VARCHAR2 | 100 | ||
| NUMBER(12,2) | NUMBER | 12 | 2 | |
| numeric(12,2) | NUMERIC | 12 | 2 |
文字型のLengthと、数値型のPrecision / Scaleを分けておけば、Excelのテーブル定義書へ転記する時に毎回括弧を外す必要がない。明示されていない長さや精度は空欄にする。
引用識別子も扱い、カラム名やテーブル名の大文字・小文字は表示上そのまま保持する。識別子を照合する時は、選択したDBの通常識別子と引用識別子の違いを考慮する。
主キーの書き方は一つではない
PRIMARY KEYは、カラム定義に直接書かれる場合と、テーブル制約として後からまとめて書かれる場合がある。
-- カラム定義
ID NUMBER PRIMARY KEY
-- テーブル制約
CONSTRAINT PK_CUSTOMER
PRIMARY KEY (CUSTOMER_ID)
-- 複合主キー
PRIMARY KEY (
COMPANY_CD,
CUSTOMER_ID
)
どの書き方でも対象のカラムへ反映する。複合主キーなら、COMPANY_CDとCUSTOMER_IDの両方をPKとして表示する。
NOT NULL、NULL、DEFAULTも読み取り、PRIMARY KEYのNullableはNOにする。主キーが存在しないカラムを参照している場合などは、エラーとして扱う。
DEFAULTの0、'A'、CURRENT_TIMESTAMP、SYSTIMESTAMPなどは、式を実行せずに元の記述を保持する。時刻を今の値に置き換えたり、関数の戻り値を推測したりはしない。
コメントを、日本語の定義へつなぐ
CREATE TABLEの後ろには、COMMENT ON TABLEやCOMMENT ON COLUMNが続いていることがある。ここには、日本語の論理名や項目説明が入っていることも多い。
COMMENT ON COLUMN CUSTOMER.CUSTOMER_ID IS '顧客ID';
この文を解析し、CUSTOMER_IDのCommentへ「顧客ID」を反映する。物理名と論理名を横に並べられると、DDLだけを眺めるより調査しやすい。
コメント文はCREATE TABLEの基本解析とは分けて処理する。対象テーブルやカラムを一意に特定できない場合は警告を出し、似た名前のカラムへ勝手に付けない。
DDLから日本語のテーブル定義へ移す時、この対応付けを手でやらずに済むのは助かる。
まとめて読み、テーブルごとに見る
セミコロンで区切られた複数のCREATE TABLEを、一つの入力欄で解析できるようにした。解析後は、テーブル選択欄で表示を切り替える。
SQLファイルやスキーマ定義の一部をまとめて調べる場面を想定している。ただし、ブラウザに無制限の入力を処理させるつもりはない。
大きな定義は分割して使う。通常のDDLを手早く調べるための道具として、扱う範囲を決めた。
分からないところを、分かったことにしない
DDL Analyzerは完全なSQLパーサーではない。外部キーやCHECK、生成列、独自型、テーブルオプションなど、初期版で完全には解釈しない構文もある。
未対応部分を検出した場合は、Warningとして「完全な解析結果ではない」と表示する。取得できないNULL制約はUNKNOWNとし、一部の文で失敗した場合も、全体が成功したようには見せない。
設計資料やDB調査に使うなら、空欄や警告にも意味がある。分からないものを推測して埋めるより、原文を確認すべき場所が分かる方がいい。
警告のある結果も出力できるため、利用する時はDDLと照合する。警告がない場合も、DBによる構文検証や実行を済ませたという意味ではない。
一覧を、次の仕事へ持っていく
解析結果には3つの出力方法を用意した。
- Copy as TSV:Excelなどへ貼り付ける
- Copy as Markdown:技術資料やREADMEへ転記する
- Download CSV:UTF-8 BOM付きのファイルとして保存する
いずれも、選択中のテーブルのカラム一覧を出力する。Physical NameとLogical Name / Commentに加え、型、長さ、精度、スケール、Nullable、Default、PKを含める。
CSVは日本語コメントをExcelで扱いやすいようBOM付きにした。TSVではセル内のタブや改行を引用符で保護し、Markdownでは表を壊す区切り文字などをエスケープする。
TSV / CSVには表計算ソフト向けの数式対策も用意した。元の文字列をそのまま出力したい時はOFFにできる。
DDLを解析できることに加え、その結果を次の作業へ持っていけることを重視した。独自のExcelファイル生成までは行わず、普段使っている資料へ貼り付けるところまでを担当する。
テーブル名も、外へ送らない
DDLには、実際のテーブル名やカラム名、業務上の説明が含まれる可能性がある。そのため、このツールでもBrowser-firstを基本にした。
入力DDLはブラウザ内だけで解析する。UTILIZABLEサーバ、Midnight Benchサーバ、外部API、AIサービスへ送信しない。
サイトではCloudflare Web Analyticsを利用しているが、入力DDLそのものをAnalyticsへ送信するものではない。ページ閲覧に伴う通常のアクセス情報と、ツールに渡したDDLは分けて扱う。
データの取り扱いはUTILIZABLEのPrivacyにも記載している。
括弧と引用符の境界を確認する
DDL Analyzer用に19件のテストを追加し、既存17件と合わせて全36テストが成功した。
Oracle / PostgreSQLの型、VARCHAR2、NUMBER(12,2)、numeric(12,2)、DEFAULT、NOT NULL、PRIMARY KEY、複合PRIMARY KEY、quoted identifier、COMMENT ON TABLE / COLUMN、不正DDLなどを対象にしている。
特に、型や関数の引数にあるカンマを誤って分割しないこと、文字列中の引用符や改行を保持することを確認した。出力したCSVやMarkdownでコメントの内容が壊れないことも検証している。
実ブラウザではExampleの5カラムと日本語コメント、コピー、CSV出力、複数テーブル切り替え、警告、Resetを確認した。スマートフォン幅でも、表や入力欄がページ全体を押し広げないようにしている。
5つ目も、普段の手間から
UTILIZABLEのツールは5つになった。
- 01 — SQL Formatter
- 02 — SQL IN Generator
- 03 — CSV Inspector
- 04 — CSV Diff
- 05 — DDL Analyzer
データエンジニアリングの現場で発生する、小さいけれど面倒な作業をブラウザで片付ける。その方向は変えず、実際に使う場面から次の道具を考えていく。
今回減らしたかったのは、DDLと設計書を行ったり来たりする時間だ。まずは、テーブル定義の転記に使いながら改善していきたい。