SlideShare a Scribd company logo
1
Ⅰ部
データベース論理設計のアンチパターン
2
第7章
マルチカラムアトリビュート
(複数列属性)
3
7.1 目的 : 複数の値を持つ属性を格納する
✔ 1つのバグに対して、複数のタグを付与したい。
✔ バグが影響するコンポーネント : 印刷、電子メール
✔ 欠陥の種類の分類 : crash performance cosmetic
4
7.2.1 デメリット : 値の検索が大変
✔ 特定のタグが付けられたバグの検索が難しい。
✔ 全てのタグ列に対する OR検索 が必要。
bug_id description tag1 tag2 tag3
1234 保存処理でクラッシュする crash NULL NULL
3456 パフォーマンスの向上 printing performance NULL
SELECT * FROM Bugs
WHERE tag1 = 'performance'
OR tag2 = 'performance'
OR tag3 = 'performance'
クエリで取得できる列
5
7.2.1 デメリット : 値の検索が大変
✔ performance と printing の両方が付いてるのを探す
✔ やっぱり大変。
bug_id description tag1 tag2 tag3
1234 保存処理でクラッシュする crash NULL NULL
3456 パフォーマンスの向上 printing performance NULL
SELECT * FROM Bugs
WHERE (tag1 = 'performance' OR tag2 = 'performance' OR tag3 = 'performance')
AND (tag2 = 'printing' OR tag2 = 'printing' OR tag3 = 'printing')
クエリで取得できる列
6
7.2.1 デメリット : IN で少しだけ楽に
✔ 複数タグ条件の検索は IN を使うと多少は短くなる
✔ 普通のINは tag1 IN ('performance', 'printing')
『列名 IN (値1, 値2, ...)』 なので珍しい書式。
SELECT * FROM Bugs
WHERE 'performance' IN (tag1, tag2, tag3)
AND 'printing' IN (tag1, tag2, tag3);
1つの値に対して、複数の列を検索する時点で危険な兆候では?
7
7.2.2 デメリット : 値の追加が大変
✔ 値の追加を行うためには、先に空き列を確認する必要。
✔ アトミックに実行しないと不整合発生 (ロックが必要)
SELECT * FROM Bugs Where bug_id = 3456;
UPDATE Bugs SET tag2 = 'performance' WHERE bug_id = 3456;
bug_id description tag1 tag2 tag3
1234 保存処理でクラッシュする crash NULL NULL
3456 パフォーマンスの向上 printing NULL NULL
① SELECT で対象バグを検索
“performance”
② NULL列に値を設定
8
7.2.2 デメリット : 値の削除も大変
✔ performance タグを削除したい。
✔ NULLIF関数は、値が等しいと NULL を返す。
bug_id description tag1 tag2 tag3
1234 保存処理でクラッシュする crash NULL NULL
3456 パフォーマンスの向上 printing performance NULL
UPDATE Bugs
SET tag1 = NULLIF(tag1, 'performance'),
tag2 = NULLIF(tag2, 'performance'),
tag3 = NULLIF(tag3, 'performance')
WHERE bug_id = 3456;
NULL
9
7.2.2 一番先頭の NULL 列に値を入れたい
✔ COALESCE (こーあれす:合体) 関数 を使う。
✔ COALESCEは NULL でない最初の引数を返す関数。
UPDATE Bugs
SET tag1 = CASE
WHEN 'performance' IN (tag2, tag3) THEN tag1
ELSE COALESCE(tag1, 'performance') END,
tag2 = CASE
WHEN 'performance' IN (tag1, tag3) THEN tag2
ELSE COALESCE(tag2, 'performance') END,
tag3 = CASE
WHEN 'performance' IN (tag1, tag2) THEN tag3
ELSE COALSCE(tag3, 'performance') END
WHERE bug_id = 3456;
tag2, tag3に既に'performance'が入っている場合はそのままにして、
最初にNULL以外が来る場合、すなわちtag1がNULLの場合は、
値 'perfoamance' が設定される。
10
7.3.3 一意性の保証 が難しい
✔ 列をまたいだ UNIQUE 制約は設定できない為。
INSERT INTO Bugs(description, tag1, tag2, tag3)
VALUES(印刷処理が遅い, printing, performance, performance);
重複したタグが設定されてしまう
11
7.2.4 増加する値の処理
✔ 最大タグ数を3つから4つに変更したい時
✔ このやり方ではいつか破綻がくる。(列数の上限の壁)
bug_id description tag1 tag2 tag3 tag4
1234 保存処理でクラッシュする crash NULL NULL
3456 パフォーマンスの向上 printing performance NULL
列追加
ALTER TABLE Bugs ADD COLUM tag4 VACHAR(20);
12
7.2.4 列追加による3つの問題
✔ 変更作業中のアクセスを防ぐために、テーブル全体を
ロックする必要があるかもしれない。
✔ 新規テーブル定義し、既存テーブルのデータをコピーして、
最後に既存テーブルを削除する (PostgreSQLでは大丈夫)
✔ このテーブルを使うアプリのSQLに修正が入るかも。
SELECT * FROM Bugs
WHERE tag1 = 'performance'
OR tag2 = 'performance'
OR tag3 = 'performance'
OR tag4 = 'performance'; /* 追加 */
13
7.3 アンチパターンの見つけ方
✔ 『サポートすべき最大数はいくつ?』
● テーブルにいくつ列を定義するか悩むこと自体が危険。
✔ 『SQLで同時に複数を検索する方法は?』
● 1つの論理的な属性値が複数の列に格納されてるかも。
14
コラム : アンチパターンに共通するパターン
bug_id description tags
1234 保存処理でクラッシュする mail, crash
3456 パフォーマンスの向上 printing, performance
複数の値を持つ属性をどう格納するかがポイント。
✔ ジェイウォーク : 1つの列に複数の値を詰め込む
✔ マルチカラムアトリビュート : 同じ意味のデータを複数列に
bug_id description tag1 tag2 tag3
1234 保存処理でクラッシュする mail crash NULL
3456 パフォーマンスの向上 printing performance NULL
15
7.4 アンチパターンを用いてもよい場合
✔ 列の順番が意味を持つ場合は良い。
bug_id description reported_by assigned_to verified_by
1234 xxx 001 (bob) 002 (tom) 003 (michael)
3456 xxx 002 (tom) 003 (michael) 005 (jecy)
3列ともに『アカウントID』を示すが、
意味合いはそれぞれの列で異なる。この場合はOK。
16
7.4 アンチパターンを用いてもよい場合 その2
✔ 従属テーブルを作る場合。
✔ しかし、この例では5章のEntity Atribute Value となる。
bug_id description
1234 xxx
3456 xxx
Bugsテーブル
account_id name
001 bob
002 tom
Accountsテーブル
associates テーブル (従属テーブル)
bug_id account_id relation
1234 001 'reported_by'
1234 002 'assigned_to'
本来の属性が、データに紛れ込んでいる。
このアンチパターンは 5章のEAV 参照のこと。
17
7.4 アンチパターンを用いてもよい場合 その2
✔ Entity Attribute Value をなおす
✔ 1つの列には1つの意味しか入れない
bug_id description
1234 xxx
3456 xxx
Bugsテーブル
account_id name
001 bob
002 tom
Accountsテーブル
associates テーブル (従属テーブル)
bug_id reported_by assigned_to verified_by
1234 001 002 003
1234 002 001 003
各列には単一の意味を示すデータのみ格納される。
これにより、relation 列に banana を入れられることもなくなる。
18
7.5 解決策 : 従属テーブルを作成する
bug_id description
1234 保存処理でクラッシュする
3456 パフォーマンスの向上
Bugs テーブル
bug_id description tag1 tag2 tag3
1234 保存処理でクラッシュする mail crash NULL
3456 パフォーマンスの向上 printing performance NULL
✔ 修正前
✔ 修正後 : Tagsテーブルの追加
bug_id tag
1234 mail
1234 crash
3456 printing
3456 performance
Tags テーブル
19
7.5 あるタグを付けられたバグを検索
SELECT * FROM Bugs
WHERE tag1 = 'performance'
OR tag2 = 'performance'
OR tag3 = 'performance'
✔ before
✔ after
SELECT * FROM Bugs JOIN Tags USING (bug_id)
WHERE tag = 'performance';
20
7.5 複数タグが付与されているバグを検索する
✔ before
✔ after
SELECT * FROM Bugs
INNER JOIN Tags AS t1 USING (bug_id)
INNER JOIN Tags AS t2 USING (bug_id)
WHERE t1.tag = 'printing' AND t2.tag = 'performance';
SELECT * FROM Bugs
WHERE (tag1 = 'performance' OR tag2 = 'performance' OR tag3 = 'performance')
AND (tag2 = 'printing' OR tag2 = 'printing' OR tag3 = 'printing')
afterは管理タグ数が増えても、クエリが変わらないのがポイント。
21
7.5 値の追加も簡単に
✔ before
SELECT * FROM Bugs Where bug_id = 3456;
UPDATE Bugs SET tag2 = 'performance' WHERE bug_id = 3456;
✔ after
INSERT INTO Tags (bugs_id, tag) VALUES (1234, 'save');
22
7.5 値の削除も簡単に
✔ before
UPDATE Bugs
SET tag1 = NULLIF(tag1, 'performance'),
tag2 = NULLIF(tag2, 'performance'),
tag3 = NULLIF(tag3, 'performance')
WHERE bug_id = 3456;
✔ after
DELETE FROM Tags WHERE bug_id = 3456 AND tag = 'performance';
23
同じ意味を持つ値は、
1つの列に格納するようにしましょう。
第7章 マルチカラムアトリビュート
24
7章の所感 : DBA > プログラマ をどう乗り越えるか
✔ 開発PJではDB設計は DB担当者 が行うことが多い
✔ クエリの実装は プログラマ が行うことが多い
✔ 巷を漂う DB管理者 > プログラマ の風潮
✔ 間違ってても泥を被るのはプログラマ
間違ってる! と言いたくても、プログラマには指摘する機会もない。
少ない機会を手にしたとき、このSQLアンチパターンを基に
間違ってる!を論理的に言えるようになりたい。
25
第8章
メタデータトリブル
(メタデータ大増殖)
26
はじめに : トリブルとは何か?
✔ この毛むくじゃらなのが トリブル というペット。
27
ある日、顧客管理テーブルがあった。
customer_id contact_info business_type revenue
000000001 昨日、初めて会った 商社 17.23
000000002 去年買ってくれた。 通信事業社 16.02
28
上司『年ごとの営業収益も追加しておいて』
customer_id contact_info business_type revenue
000000001 昨日、初めて会った 商社 17.23
000000002 去年買ってくれた。 通信事業社 16.02
29
結果、列が増えた
customer_id contact_info business_type revenue
2002
revenue
2003
revenue
2004
000000001 昨日、初めて会った 商社 17.23 18.21 NULL
000000002 去年買ってくれた。 通信事業社 NULL NULL NULL
✔ revenue は注目している顧客の分しか入力されない。
✔ 毎年この列を1つ追加する必要がある。
✔ 会議、データ以降の検討、表領域の再編成 … 。
30
8.1 目的 : スケーラビリティを高める
✔ 行数が増えると必然的にクエリ実行速度は低下する。
✔ データが増加し続けるテーブルにどう立ち向かうか?
31
8.2 アンチパターン : テーブルや列をコピーする
✔ 行数の多いテーブルを、複数のテーブルを分割する
● Bugs_2008テーブル、Bugs_2009テーブル ...
✔ 列を複数列に分割する
● revenue2008列、 revenue2009列 ...
上記パターンは”2008”や”2009”など、データとして行で管理される
データが、テーブル名や列名などのメタデータに入っている。
『メタデータへのデータの混入』が発生すると、様々な弊害がある。
32
8.2.1 テーブルの増殖
✔ 毎年テーブルが1つ増える => 破綻の兆候
bug_id description
1234 xxx
1235 xxx
Bugs_2008 テーブル
bug_id description
3456 xxx
3457 xxx
Bugs_2009 テーブル
bug_id description
4567 xxx
4568 xxx
Bugs_2010 テーブル
33
8.2.1 テーブルの増殖
✔ 挿入する値に応じて、挿入先テーブルを選択する手間。
bug_id description
1234 xxx
1235 xxx
Bugs_2008 テーブル
bug_id description
3456 xxx
3457 xxx
Bugs_2009 テーブル
bug_id description
4567 xxx
4568 xxx
Bugs_2010 テーブル
INSERT INTO Bugs_2010 (…, date_reported, …)
VALUES (…, '2010-06-01', …);
34
8.2.1 テーブルの増殖
✔ 年明けにテーブルを作り忘れてエラーが発生。
bug_id description
1234 xxx
1235 xxx
Bugs_2010 テーブル
INSERT INTO Bugs_2011 (…, date_reported, …)
VALUES (…, '2011-01-01', …);
×
Bugs_2011 は未定義
35
8.2.2 データの整合性を管理する
✔ 2009年のバグテーブルに2011年のデータが混入
✔ データの整合性が管理できてない
bug_id description date_reported
1234 xxx 2009-01-02
1235 xxx 2010-10-30
Bugs_2009 テーブル
SELECT * FROM Bugs_2009
WHERE
date_reported NOT BETWEEN '2009-01-01' AND '2009-12-31'
36
8.2.2 整合性はCheck制約で対策可能だが、手間がかかる
✔ 各テーブルでCheck制約を定義する必要がある。
CREATE TABLE Bugs_2009
…
date_reported DATE
CHECK (EXTRACT(YEAR FROM date_reported) = 2009)
);
CREATE TABLE Bugs_2010
…
date_reported DATE
CHECK (EXTRACT(YEAR FROM date_reported) = 2010)
);
37
8.2.3 データの同期
✔ 2010年のバグを2009年に移そうとした場合。
✔ 本質的にデータの更新にも関わらず、UPDATEが使えない。
INSERT INTO Bugs_2009 (bug_id, date_reported, …)
SELECT bug_id date_reported, …
FROM Bugs_2010
WHERE bug_id = 1234;
DELETE FROM Bugs_2010 WHERE bug_id = 1234;
1. 2009年のバグテーブルに追加してから
2. 2010年のバグテーブルから削除する
38
8.2.4 一意性の保証
✔ テーブルまたぎの一意性の保証にUNIQUE制約は使えない
✔ シーケンスまたは、値生成のためのテーブルを生成する
bug_id description
0001 xxx
0003 xxx
bug_id description
0002 xxx
0005 xxx
bug_id description
0004 xxx
0006 xxx
Bugs_2008 テーブル Bugs_2009 テーブル Bugs_2010 テーブル
CREATE SEQUENCE bug_id_gen;
SELECT
NEXTVAL ('bug_id_gen')
39
8.2.5 テーブルをまたいだクエリ実行
✔ すべての未完了のバグの合計を算出したい場合
✔ UNION が必要
● 同じ列を持つSELECT結果を結合する
● 重複列は DISTINCT を付けていなくても削除される
SELECT b.status, COUNT(*) AS count_per_status FROM (
SELECT * FROM Bugs_2008
UNION
SELECT * FROM Bugs_2009
UNION
SELECT * FROM Bugs_2010 ) AS b
GROUP BY b.status;
40
8.2.6 メタデータの同期
✔ バグ修正時間を記録することにした。
✔ 1つのALTER TABLEでは、2010年しか変更されない。
✔ 年ごとに列構造が違うと UNION できない。
● UNION できる条件は、列が全て同じであること。
ALTER TABLE Bugs_2010 ADD COLUMN hours NUMERIC(9, 2);
41
8.2.7 参照整合性の管理
✔ バグにコメントが付与できることを想定する。
✔ 外部キーは単一テーブルに対してしか設定できない。
bug_id description
0001 xxx
0003 xxx
bug_id description
0002 xxx
0005 xxx
bug_id description
0004 xxx
0006 xxx
Bugs_2008 テーブル Bugs_2009 テーブル Bugs_2010 テーブル
bug_id comment_id comment
0001 0001 バグの原因は?
0002 0001 パッチを送りますね。
? ? ?
複数のテーブルに外部キーは設定できない。
42
8.2.8 メタデータトリブル列の特定
✔ テーブルだけでなく、列もメタデータトリブルになり得る。
✔ 2011年分を追加することが目に見えている。
● 移行検討、ユーザ影響検討、AP修正 など
● 会議、ユーザ調整、会議、ユーザ調整、会議、会議 ...
CREATE TABLE ProjectHistory (
bugs_fixed_2008 INT,
bugs_fixed_2009 INT,
bugs_fixed_2010 INT
);
43
8.3 アンチパターンの見つけ方
✔ じゃあ、〜ごとにテーブル(列)を作る必要があるんだね
● ある値を用いてテーブルや列を分割を試みています。
● 適切な設計ができていれば、データごとの分割はしません。
✔ このデータベースがサポートしているテーブル(列)の最大数は?
● この値が気になる時点で、クエリは1000行を超えるでしょう。
● 某システムでは、実際にO/Rマッパの列数制限の問合せがありました。
✔ 今朝APが新規データの追加に失敗した理由がわかった。
新しい年のデータを格納するためのテーブルを作成し忘れてたんだ。
● 年末年始は毎年会社で過ごすことになるでしょう。
44
8.3 アンチパターンの見つけ方 : 続き
✔ 複数テーブルを1回で検索するためのクエリの実行方法は?
全部のテーブル列は共通しているんだけど。
● 列が共通ならば、始めから1つのテーブルにしてはいかがでしょう。
✔ テーブル名のパラメータをどうやって渡せば良い?
年が動的に付与されるテーブル名にクエリを実行する必要があるんだ。
● テーブル名に年を付与してはいけません。(データのメタデータへの混入)
● これも O/Rマッパの Q&Aサポートしていると良く聞かれる内容です。
45
8.4 アンチパターンを用いても良い場合
✔ 過去データを最新データから分離したアーカイブを作る場合。
✔ よく実行される最新データ向けクエリが高速化できる。
bug_id description
5001 xxx
5002 xxx
Bugs テーブル
bug_id description
0001 xxx
0002 xxx
... ...
4999 xxx
Bugs_Archive テーブル
データ少、クエリ速
(古いデータを検索しなくて済む)
データ多、クエリ遅
(頻度が少ないので問題ではない)
46
8.5 解決策 : パーティショニングと正規化を行う
✔ 水平パーティショニングでテーブル分割
✔ 論理的には1テーブルなので、
● コメントテーブルを作った時に外部キーも設定できる。
● 一意性の保証も主キーによるUNIQUE制約で設定。
● なおってないバグ全部検索も、1テーブルにのみクエリを実行。
CREATE TABLE Bugs (
bug_id SERIAL PRIMARY Key,
– 他の列 …
date_reported DATE
) PARTITION BY HASH ( YEAR(date_reported) )
PERTITION 4;
【MySQL 5.1】
4テーブル分割の
ハッシュパーティショニング
47
8.5.2 垂直パーティショニングの使用
✔ 列でテーブルを分割する手法。
✔ 列の一部が BLOB など非常に大きい場合に効果的。
product_id name release_date installer_
image
5001 xxx 2013-07-01 ver1.0.exe
5002 xxx 2013-07-07 ver1.1.exe
Products テーブル
Installerバイナリ列が大きいので、
クエリに * を使いにくい。
垂直パーティショニング
product_id name release_date
5001 xxx 2013-07-01
5002 xxx 2013-07-07
Products テーブル
product_id installer_image
5001 ver1.0.exe
5002 ver1.1.exe
ProductsInstallers テーブル
48
8.5.3 従属テーブルの導入
✔ 列によるメタデータトリブルには、従属テーブルを導入する。
✔ あくまで、データは行に持たせる。
CREATE TABLE ProjectHistory (
bugs_fixed_2008 INT,
bugs_fixed_2009 INT,
bugs_fixed_2010 INT
);
✔ before ✔ after
CREATE TABLE ProjectHistory (
project_id BIGINT,
year SMALLINT,
bugs_fixed INT,
PRIMARY KEY (project_id, year),
FOREIGN KEY (project_id)
REFERENCES Projects(project_id)
);
49
データにメタデータを
増殖させないように気を付けましょう。
第8章 メタデータトリブル
50
8章の所感 : パーティショニングへの漠然とした恐怖感
✔ パーティショニング使ったことがないので、怖い。
可能であれば使いたくない。
✔ コンサルタントに相談してみる。
● 上妻 : パーティショニングってどうよ?
● コ : 『検証しないと無責任なことは言えません』
● 上妻 : ...。
✔ だけど、テーブルは分割したい。
✔ メタデータトリブル のできあがり!
未知の手段に対する漠然とした恐怖を取り除き、
正しい道に導くためにはどうしたら良いのだろう?
51
Ⅱ部
データベース物理設計のアンチパターン
52
第9章
ラウンディングエラー
(丸め誤差)
53
上司『バグ修正にかかった人件費のレポート作って』
bug_id description
1234 xxx
3456 xxx
Bugsテーブル
account_id name
001 bob
002 tom
Accountsテーブル
小数点以下まで
きっちり頼むぞ!
精度だ!精度!
54
FLOAT列をそれぞれのテーブルに追加
bug_id description hour
1234 xxx 2.3
3456 xxx 1.5
Bugsテーブル
account_id name hourly_rate
001 bob $20.53
002 tom $29.31
Accountsテーブル
ALTER TABLE Bugs ADD COLUMN hours FLOAT;
ALTER TABLE Accounts ADD COLUMN hourly_rate FLOAT;
✔ Bugsテーブルのhour : バグ修正にかかった時間
✔ Accountsテーブルのhourly_rate : 担当者の時給
55
上司 『計算結果が違うぞ!なにやってんだ!』
✔ 怒られた。
http://www.diggingthedirt.com/2011/12/01/diggingthedirts-christmas-appeal/05_flatbed_web-july/
56
9.1 目的 : 整数の代わりに小数値を使用する
✔ PostgreSQLの integer型 では小数点以下が持てない。
✔ 小数点を持ちたい。(例 : 19.95ドル)
✔ 正しく計算を行いたい。
57
9.2 アンチパターン : FLOAT データ型を使用する
✔ SQLのFLOAT型も、ほとんどのプログラミング言語と
同じように IEEE754 標準により保存される。
✔ 浮動小数点の特徴を理解する必要がある。
指数と仮数を保存して、大きな数値を小さなデータ量で扱う。
58
9.2.1 丸めが避けられない
✔ 例えば10進数の 59.95 は2進数では無限精度が必要
✔ FLOAT型の近似値は 59.950000762939 となる。
59
9.2.2 SQLでのFLOATの使用
✔ データベースによっては丸めた値を表示する。
SELECT hourly_date FROM Accounts WHERE account_id = 123;
結果 : 59.95
✔ 10億倍すると、差異が見えてくる。
SELECT hourly_date * 1000000000
FROM Accounts WHERE account_id = 123;
結果 : 59950000762.939
60
9.2.2 値の比較が難しい
✔ 問題 : 誤差により、イコールにならない。
SELECT * FROM Accounts WHERE hourly_rate = 59.95;
結果 : 一致する行はありません。
✔ 対策 : 精度に応じて差分を認める判定を行う。
SELECT * FROM Accounts
WHERE ABS(hourly_rate – 59.95) < 0.000001;
結果 : 該当行あり。
100万分の1の誤差は等価と見なす。
61
9.2.2 集約計算を行うと、誤差は拡大する
✔ 1つずつ小さな誤差でも、SUMで繰り返すと大きくなる。
SELECT SUM (b.hours * a.hourly_rate) AS project_cost
FROM Bugs AS b
INNER JOIN Accounts AS a ON b.assigned_to = a.account_id;
✔ 掛け算による集約計算は、より誤差を大きくする。
● 1に1000回 1 を掛けても、結果は 1 。
● 1に1000回 0.999 を掛けると、結果は 約0.3677。
62
9.3 アンチパターンの見つけ方
✔ 業務APで FLOAT を見つけたら、ほぼアンチパターン
✔ お金を扱う金融系では特に危険。
63
9.4 アンチパターンを用いてもよい場合
✔ INTEGER や NUMERIC の上限を超える場合に使う。
✔ 多くの場合は、誤差が許容される科学技術計算。
✔ 業務システムでは誤差が許容されることはあまりない。
64
9.5 可変長数値データ型を使用する
✔ Oracle : NUMBER 型
✔ PostgreSQL : NUMERIC型
ALTER TABLE Bugs ADD COLUMN hours NUMERIC(9, 2);
ALTER TABLE Accounts ADD COLUMN hourly_rate NUMERIC(9, 2);
65
9.5 等価判定、誤差もなくなる
✔ before
SELECT hourly_rate FROM Accounts WHERE hourly_rate = 59.95;
結果 : 一致する行はありません。
✔ after
SELECT hourly_rate FROM Accounts WHERE hourly_rate = 59.95;
結果 : 59.95
✔ 10億倍しても大丈夫
SELECT hourly_date * 1000000000
FROM Accounts WHERE hourly_rate = 59.95;
結果 : 59950000000
66
できる限り、FLOAT型は使わないようにしましょう。
第9章 ラウンディングエラー
67
9章の所感 : float使う業務システムってどんなもの?
✔ トラフィック管理系のシステムで見かけたような...。
✔ そういえば、他の言語にもfloatの代替策ってあるのかな?
✔ Java の場合。
● java.math.BigDecimalクラスによる誤差の防止。
● DigDecimal使う時はメソッドで計算する。
● new BigDecimal(59.95).add(new BigDecimal(3.2));

More Related Content

SQLアンチパターン読書会 レジュメ