SlideShare a Scribd company logo
1 of 67
Download to read offline
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

More from Norito Agetsuma

Java EEを補完する仕様 MicroProfile
Java EEを補完する仕様 MicroProfileJava EEを補完する仕様 MicroProfile
Java EEを補完する仕様 MicroProfileNorito Agetsuma
 
CDI2.0アップデート&クックブック #JavaDayTokyo #jdt2016_4c
CDI2.0アップデート&クックブック #JavaDayTokyo #jdt2016_4cCDI2.0アップデート&クックブック #JavaDayTokyo #jdt2016_4c
CDI2.0アップデート&クックブック #JavaDayTokyo #jdt2016_4cNorito Agetsuma
 
JavaOne2015報告会 Java EE アップデート #j1jp
JavaOne2015報告会 Java EE アップデート #j1jpJavaOne2015報告会 Java EE アップデート #j1jp
JavaOne2015報告会 Java EE アップデート #j1jpNorito Agetsuma
 
Java EE パフォーマンスTips #glassfish_jp
Java EE パフォーマンスTips #glassfish_jpJava EE パフォーマンスTips #glassfish_jp
Java EE パフォーマンスTips #glassfish_jpNorito Agetsuma
 
Jbatch実践入門 #jdt2015
Jbatch実践入門 #jdt2015Jbatch実践入門 #jdt2015
Jbatch実践入門 #jdt2015Norito Agetsuma
 
Tomcatの実装から学ぶクラスローダリーク #渋谷Java
Tomcatの実装から学ぶクラスローダリーク #渋谷JavaTomcatの実装から学ぶクラスローダリーク #渋谷Java
Tomcatの実装から学ぶクラスローダリーク #渋谷JavaNorito Agetsuma
 
Javaトラブルに備えよう #jjug_ccc #ccc_h2
Javaトラブルに備えよう #jjug_ccc #ccc_h2Javaトラブルに備えよう #jjug_ccc #ccc_h2
Javaトラブルに備えよう #jjug_ccc #ccc_h2Norito Agetsuma
 
Unixカーネルの設計 7 プロセスの制御
Unixカーネルの設計 7 プロセスの制御Unixカーネルの設計 7 プロセスの制御
Unixカーネルの設計 7 プロセスの制御Norito Agetsuma
 
JJUG 11月ナイトセミナー CDIをはじめよう
JJUG 11月ナイトセミナー CDIをはじめようJJUG 11月ナイトセミナー CDIをはじめよう
JJUG 11月ナイトセミナー CDIをはじめようNorito Agetsuma
 
AeroGear & Java EE 7 で簡単プッシュ
AeroGear & Java EE 7 で簡単プッシュAeroGear & Java EE 7 で簡単プッシュ
AeroGear & Java EE 7 で簡単プッシュNorito Agetsuma
 
プロになるためのJavaScript入門読書会 レジュメ
プロになるためのJavaScript入門読書会 レジュメプロになるためのJavaScript入門読書会 レジュメ
プロになるためのJavaScript入門読書会 レジュメNorito Agetsuma
 
JSR 352 “Batch Applications for the Java Platform”
JSR 352 “Batch Applications for the Java Platform”JSR 352 “Batch Applications for the Java Platform”
JSR 352 “Batch Applications for the Java Platform”Norito Agetsuma
 
Java Batch 仕様 (Public Review時点)
Java Batch 仕様 (Public Review時点) Java Batch 仕様 (Public Review時点)
Java Batch 仕様 (Public Review時点) Norito Agetsuma
 
Lt agetsuma 拡大するcdi
Lt agetsuma 拡大するcdiLt agetsuma 拡大するcdi
Lt agetsuma 拡大するcdiNorito Agetsuma
 

More from Norito Agetsuma (16)

Quarkus入門
Quarkus入門Quarkus入門
Quarkus入門
 
Java EEを補完する仕様 MicroProfile
Java EEを補完する仕様 MicroProfileJava EEを補完する仕様 MicroProfile
Java EEを補完する仕様 MicroProfile
 
CDI2.0アップデート&クックブック #JavaDayTokyo #jdt2016_4c
CDI2.0アップデート&クックブック #JavaDayTokyo #jdt2016_4cCDI2.0アップデート&クックブック #JavaDayTokyo #jdt2016_4c
CDI2.0アップデート&クックブック #JavaDayTokyo #jdt2016_4c
 
JavaOne2015報告会 Java EE アップデート #j1jp
JavaOne2015報告会 Java EE アップデート #j1jpJavaOne2015報告会 Java EE アップデート #j1jp
JavaOne2015報告会 Java EE アップデート #j1jp
 
Java EE パフォーマンスTips #glassfish_jp
Java EE パフォーマンスTips #glassfish_jpJava EE パフォーマンスTips #glassfish_jp
Java EE パフォーマンスTips #glassfish_jp
 
Jbatch実践入門 #jdt2015
Jbatch実践入門 #jdt2015Jbatch実践入門 #jdt2015
Jbatch実践入門 #jdt2015
 
Tomcatの実装から学ぶクラスローダリーク #渋谷Java
Tomcatの実装から学ぶクラスローダリーク #渋谷JavaTomcatの実装から学ぶクラスローダリーク #渋谷Java
Tomcatの実装から学ぶクラスローダリーク #渋谷Java
 
Java EE8 Report
Java EE8 ReportJava EE8 Report
Java EE8 Report
 
Javaトラブルに備えよう #jjug_ccc #ccc_h2
Javaトラブルに備えよう #jjug_ccc #ccc_h2Javaトラブルに備えよう #jjug_ccc #ccc_h2
Javaトラブルに備えよう #jjug_ccc #ccc_h2
 
Unixカーネルの設計 7 プロセスの制御
Unixカーネルの設計 7 プロセスの制御Unixカーネルの設計 7 プロセスの制御
Unixカーネルの設計 7 プロセスの制御
 
JJUG 11月ナイトセミナー CDIをはじめよう
JJUG 11月ナイトセミナー CDIをはじめようJJUG 11月ナイトセミナー CDIをはじめよう
JJUG 11月ナイトセミナー CDIをはじめよう
 
AeroGear & Java EE 7 で簡単プッシュ
AeroGear & Java EE 7 で簡単プッシュAeroGear & Java EE 7 で簡単プッシュ
AeroGear & Java EE 7 で簡単プッシュ
 
プロになるためのJavaScript入門読書会 レジュメ
プロになるためのJavaScript入門読書会 レジュメプロになるためのJavaScript入門読書会 レジュメ
プロになるためのJavaScript入門読書会 レジュメ
 
JSR 352 “Batch Applications for the Java Platform”
JSR 352 “Batch Applications for the Java Platform”JSR 352 “Batch Applications for the Java Platform”
JSR 352 “Batch Applications for the Java Platform”
 
Java Batch 仕様 (Public Review時点)
Java Batch 仕様 (Public Review時点) Java Batch 仕様 (Public Review時点)
Java Batch 仕様 (Public Review時点)
 
Lt agetsuma 拡大するcdi
Lt agetsuma 拡大するcdiLt agetsuma 拡大するcdi
Lt agetsuma 拡大するcdi
 

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