Chap.04 リレーション編
前回はデータの取得、追加、更新、削除について書きました。 今回は、より複雑なデータの扱いについて書きます。
今回も先にサンプルコードを実行した動画をあげておきます。
リレーションとは?
直訳すると関係という意味になります。
第二回の記事に書いたように、 SQLiteはRDBMSというデータベース管理システムです。 なので、IDなどの値を使ってテーブル間を紐付けることができます。
例えば「学生のテーブル」と「大学のテーブル」を紐付けて 「所属する大学」を表現しようとすると以下のようになります。
studentsテーブル
id | name | age | university_id |
---|---|---|---|
1 | 太郎 | 22 | 1 |
2 | 花子 | 23 | 2 |
3 | 次郎 | 20 | 1 |
universitiesテーブル
id | name | address |
---|---|---|
1 | X大学 | A県a市1-1-1 |
2 | Y大学 | B府a市1-1-1 |
3 | Z大学 | C都a区1-1-1 |
-- studentsテーブルとデータを作成する
create table students(id integer primary key autoincrement, name text, age integer, university_id integer);
insert into students(name, age, university_id) values('太郎', 22, 1);
insert into students(name, age, university_id) values('花子', 23, 2);
insert into students(name, age, university_id) values('次郎', 20, 1);
-- universitiesテーブルとデータを作成する
create table universities(id integer primary key autoincrement, name text, address text);
insert into universities(name, address) values('X大学', 'A県a市1-1-1');
insert into universities(name, address) values('Y大学', 'B府a市1-1-1');
insert into universities(name, address) values('Z大学', 'C都a区1-1-1');
-- テーブルを結合して取得する
select students.id, students.name, universities.name
from students
inner join universities on students.university_id = universities.id;
students.id | students.name | universities.name |
---|---|---|
1 | 太郎 | X大学 |
2 | 花子 | Y大学 |
3 | 次郎 | X大学 |
他にも外部結合や交差結合など、
join
にもいくつかの方法があるので、
詳しくはこちらの参考ページを見たり調査してみてください。
https://www.javadrive.jp/sqlite/join/
このようなリレーションについて、 ドローイングソフトを例に説明していきます。
テーブル設計
まずは線のテーブルと線を構成する点のテーブルを考えます。
linesテーブル
id |
---|
1 |
2 |
pointsテーブル
id | x | y | line_id |
---|---|---|---|
1 | 10.0 | 200.89 | 1 |
2 | 50.2 | 10.25 | 1 |
3 | 100.5 | 300.44 | 2 |
4 | 299.3 | 43.2 | 2 |
これで「線を構成する点」を表現できます。
点を線にグルーピングするだけならlinesテーブルは無くてもいい気はしますが、 線ごとに色や描いたユーザのIDが設定できるように切り分けておきます。
サーバ
実装したWeb APIは以下の通りです。
GET /lines
: 全てのlines
を取得するlines
を構成するpoints
の情報も取得する
POST /lines
: 線を追加するlines
テーブルに一行追加する- 作成した
lines
を一件取得する - 取得した
lines
を構成するpoints
を追加する
こちらがSQLでデータを取得して、 JSでデータを整形するコードです。
const query = `
select lines.id as 'line_id', points.id as 'point_id', points.x as 'x', points.y as 'y'
from lines
inner join points on lines.id = points.line_id`;
db.all(query, (err, rows) => {
if (err) {
throw err
}
const lineIds = Array.from(new Set(rows.map(row => row.line_id)))
const lines = lineIds.map(lineId => {
const points = rows.filter(row => row.line_id === lineId)
.map(row => {
return {
id: row.point_id,
x: row.x,
y: row.y,
}
})
return {
id: lineId,
points: points,
}
})
callback(lines)
})
クエリの結果はこのようになっています。
line_id | point_id | x | y |
---|---|---|---|
1 | 1 | 235.0 | 65.0 |
1 | 2 | 224.0 | 65.0 |
1 | 3 | 209.0 | 66.0 |
1 | 4 | 194.0 | 71.0 |
1 | 5 | 185.0 | 79.0 |
1 | 6 | 175.0 | 90.0 |
1 | 7 | 171.0 | 100.0 |
またコメントアウトしてあるコードは、 SQLで取得してくる段階でJSONにフォーマットするサンプルです。 こちらの方がJSの処理は少なくなりますが、SQLite側の処理が増えます。
// *** Formatting with SQLite **
const query = `
select json_object(
'id', lines.id,
'points', json_group_array(
json_object('id', points.id, 'x', points.x, 'y', points.y)
)
) line
from lines
inner join points on lines.id = points.line_id
group by lines.id`;
db.all(query, (err, rows) => {
const lines = rows.map(row => JSON.parse(row.line))
callback(lines)
})
クエリの結果はこのようになっています。
line |
---|
{"id":1,"points":[{"id":1,"x":235.0,"y":65.0},{"id":2,"x":224.0,"y":65.0},{"id":3,"x":209.0,"y":66.0},{"id":4,"x":194.0,"y":71.0},{"id":5,"x":185.0,"y":79.0}]} |
{"id":2,"points":[{"id":23,"x":324.0,"y":330.0},{"id":24,"x":308.0,"y":321.0},{"id":25,"x":297.0,"y":309.0},{"id":26,"x":291.0,"y":299.0},{"id":27,"x":290.0,"y":289.0},{"id":28,"x":296.0,"y":277.0},{"id":29,"x":314.0,"y":273.0},{"id":30,"x":335.0,"y":273.0}]} |
{"id":3,"points":[{"id":49,"x":471.0,"y":115.0},{"id":50,"x":471.0,"y":126.0},{"id":51,"x":471.0,"y":142.0},{"id":52,"x":468.0,"y":157.0},{"id":53,"x":460.0,"y":168.0},{"id":54,"x":449.0,"y":176.0},{"id":55,"x":433.0,"y":181.0}]} |
{"id":4,"points":[{"id":81,"x":125.0,"y":383.0},{"id":82,"x":123.0,"y":371.0},{"id":83,"x":123.0,"y":360.0},{"id":84,"x":139.0,"y":368.0},{"id":85,"x":156.0,"y":381.0},{"id":86,"x":172.0,"y":392.0},{"id":87,"x":184.0,"y":400.0}]} |
{"id":5,"points":[{"id":106,"x":582.0,"y":308.0},{"id":107,"x":566.0,"y":310.0},{"id":108,"x":551.0,"y":318.0},{"id":109,"x":568.0,"y":332.0}]} |
どちらも一長一短ですが、前者の方がDBの役割とサーバの役割が分離できていると思うので 自分は前者をコード選ぶと思います。
スケッチ
こちらがProcessingスケッチです。
マウスをドラッグ&ドロップすることで線を描区ことができます。
マウスリリースのタイミングで、
POST /lines
にリクエストして線を追加し、
GET /lines
にリクエストして線を取得しています。
感想
軽い気持ちで記事を連載してしましました。 いつネタ切れが来るかわかりませんが、 外部サービスを使ってインターネット越しに連動するところまでを目標にしたいと思ってます。
次の記事はこちらに書く予定です。