今回はノベルゲームを対象としてデータベースを作った。
キャラクターにシリアル番号を付け、画像と関連付けるために使用した。
index.ssql
では開発会社とゲームの一覧を出力させてる。出力されたHTMLで開発会社の名前をクリックすれば公式サイトへ飛ぶことが出来る。また、ゲームのタイトルをクリックすればゲームの詳細を確認することが出来る。
GENERATE HTML {
{"ゲーム一覧"} @{width=420, font-weight=bold, style="background-color: orange"}!
{
{"会社名"} @{width=75, font-weight=bold},
{"設立"} @{width=60, font-weight=bold},
{"作品"} @{width=225, font-weight=bold},
{"発売年"} @{width=60, font-weight=bold}
} @{width=420, style="background-color: gold"}!
{[
{anchor({(asc2)c.name} @{width=75}, c.website)} @{width=75},
{(asc1)c.year} @{width=60},
{[{link(g.title, "game.ssql", g.id)} @{width=225},
{(asc)g.year} @{width=60}
]!} @{width=285}
]!} @{width=420}
} @{width = 420, cssfile="jscss/style.css"}
FROM game g
INNER JOIN company c ON g.company = c.id;
game.ssql
はゲームの詳細情報を出力する。出力されるHTMLにはキャラクターの一覧とテーマソングの一覧があり、キャラクター名をクリックすればそのキャラクターの詳細情報を確認できる。
FOREACH g.id
GENERATE HTML {
{"ゲーム情報"} @{font-weight=bold, style="background-color: orange"}!
{"キャラクター"} @{font-weight = bold, style="background-color: gold"}!
{{[link(c.name, "character.ssql", c.id)]!} @{width=500}}!
{"テーマソング"} @{font-weight = bold, style="background-color: gold"}!
{
{"曲名"} @{width=200, font-weight: bold},
{"歌手"} @{width=100, font-weight: bold},
{"作曲者"} @{width=100, font-weight: bold},
{"作詞者"} @{width=100, font-weight: bold}
} @{style="background-color: yellow"}!
[
{s.title} @{width=200},
{s.singer} @{width=100},
{s.composer} @{width=100},
{s.lyrics} @{width=100}
]!
} @{cssfile="jscss/style.css"}
FROM
character c
INNER JOIN game g ON c.game = g.id,
song s
INNER JOIN game g2 ON s.game = g2.id
WHERE g.id = g2.id;
character.ssql
はキャラクターの詳細情報を出力する。出力されたHTMLでキャラクターの声優名をクリックすれば声優の詳細情報を、ゲーム名をクリックすればそのキャラクターが登場したゲームの詳細情報を見ることが出来る。また、このページでは検索エンジンから自動で収集された画像が表示される。画像は全てPNG形式になっていて、ファイル名には各キャラクターに割り当てたシリアル番号を使用してる。
FOREACH c.id
GENERATE HTML {
{"キャラクター情報"} @{font-weight=bold, style="background-color: orange"}!
{
{
{"名前"} @{height=25, font-weight=bold}!
{"性別"} @{height=25, font-weight=bold}!
{"誕生日"} @{height=25, font-weight=bold}!
{"ゲーム名"} @{height=25, font-weight=bold}!
{"声優"} @{height=25, font-weight=bold}
} @{width=100, style="background-color: gold"},
{
{c.name} @{height=25}!
{c.gender} @{height=25}!
{c.birthday} @{height=25}!
{link(g.title, "game.ssql", g.id)} @{height=25}!
{link(v.name, "voice.ssql", v.id)} @{height=25}
} @{width=360}
} @{width=460}!
{image(c.id".png", "./img")} @{width=460}
} @{cssfile="jscss/style.css"}
FROM character c
INNER JOIN voice v ON c.voice = v.id
INNER JOIN game g ON c.game = g.id;
voice.ssql
は声優の詳細情報を出力する。出力されたHTMLには演じたキャラクターの一覧が表示され、キャラクター名をクリックすればキャラクターの詳細情報を見ることが出来る。
FOREACH v.id
GENERATE HTML {
{"声優情報"} @{width=200, font-weight=bold, style="background-color: orange"}!
{
{"名前"} @{width=200, font-weight=bold},
{"性別"} @{width=60, font-weight=bold},
{"誕生日"} @{width=100, font-weight=bold}
} @{width=360, style="background-color:gold"}!
{[
{v.name} @{width=200},
{v.gender} @{width=60},
{v.birthday} @{width=100}]!
} @{width=360}!
{"出演キャラクター"} @{width=360, font-weight=bold, style="background-color: gold"}!
[{link(c.name, "character.ssql", c.id)} @{width=260}]!
} @{cssfile="jscss/style.css"}
FROM character c
INNER JOIN voice v ON c.voice = v.id;
SuperSQL質問文は極力読みやすいように記述するよう心がけた。しかし、SuperSQLが出力するHTMLの性質的に装飾情報を分離することが困難であったため、CSSの情報はほとんどSuperSQL質問文と一緒に記述してる。唯一表全体の見栄え関する記述はstyle.css
にまとめてる。margin
の設定では表を中央に配置し、border-collapse
の設定では表の枠が何重にも表示されないようにして見栄えを改善してる。
table {
margin: 0 auto;
border-collapse: collapse;
}
この課題の題材にするテーマを決定した当初、声優が複数のゲームのキャラクターの役として現れることを期待して声優のテーブルを作った。しかし、特にゲームの場合は声優が複数の名前を使い分ける場合が多く情報もバラバラで分散していて効率的にそれらをまとめることは困難であった。結果、声優が演じるキャラクターの数が見かけの上で少なくなってしまっている。逆にテーマソングのアーティストは予想に反して重複が多かったため、アーティストを1つのエンティティとしてテーブルを作ったほうがデータのつながりが増えてより興味深くなったと考えられる。
SQLから直接人間にわかりやすい出力を得ようとする試みは大変興味深く感じた。ただ今回SuperSQLを使用して2つほどバグに遭遇したので、ここに書いておく。
一つはHTMLを出力した時、おかしなタグが挿入されてしまう。今回出力したHTMLにもほとんどのページで対応する開始タグが無い</TR>
が多数出現していたり、いくつかのページではBODY
の開始タグがいくつも挿入されてる。手元のブラウザーでは正常に表示されることを確認したが、全てのブラウザーで表示できる保証は無いため、問題を引き起こす可能性がある。
またもう一つのバグについて、GENERATE HTML
でHTMLのコードを出力するときにHTMLのタグが全くエスケープされずにそのまま出力される。例えば、userという名前のテーブルに以下のようなデータが保存されていたとする:
id | username | profile |
---|---|---|
0 | taro | 太郎でーす |
1 | jiro | 二郎だよー |
2 | sabro | <script>alert('Sabro!');</script> |
これはあるサービスの利用ユーザー情報を保存したテーブルであるが、3番目の三郎のプロフィールはJavascriptのコードになっている。このテーブルに対し、以下のようなSuperSQLクエリを作成する:
GENERATE HTML
[u.id, u.username, u.profile]!
FROM users u;
すると、出力結果はこちらの通りになる。HTMLがエスケープされてない事によりJavascriptのコードが実行されてしまい、アラートメッセージが表示される。出力するデータ形式に合わせてエスケープを行わない場合、想定とは異なる出力が得られるばかりでなく意図しないコードの実行につながりセキュリティ上問題がある。したがって、このバグは緊急度の高いものといえる。