SlideShare a Scribd company logo
1 of 69
Download to read offline
SQLアンチパターン
~スパゲッティクエリ~
CB部ADチーム
板橋正之
2014/06/17
ある事柄を説明するために
必要以上に多くの実体を
仮定するべきではない
~オッカムのウィリアム
オッカムの剃刀
オッカムのウィリアムは
オッカム村生まれのウィリアムさん
ヴィンチ村生まれの
レオナルドさんと同じ意味
目的:
SQLクエリの数を減らす
アンチパターン:
複雑な問題をワンステップで
解決しようとする
解決策:
分割統治を行う
シンプル・イズ・ベスト
SQLコードを簡単に
もうちょっと説明します。
SQLのクエリも
プログラミングと一緒
1つのメソッドで多くの処理
→Bad!!
1つのSQLで複雑な問い合わせ
→Bad!!
複雑なクエリは
クエリの修正、変更、デバッグも
複雑に
クエリの数が減れば
パフォーマンスが上がる
→間違い
必ずしもパフォーマンスは
上がらないし、
逆に複雑すぎて最適化が
働かない可能性も
パフォーマンスチューニングも
難しい。
複雑なメソッドはコードレビューが
難しいのと一緒。
複雑すぎて自分で何を
やっているのかわからなくなる。
1週間後にそのSQLクエリ
読み解けますか?
意図に反した結果
以下の2つのテーブルからproduct_idが1の製品のバグが完了(FIXED) と
未修正(OPEN)の件数を問い合わせたい。
BugsProducts Bugs
SELECT p.product_id, COUNT(f.bug_id) AS count_fixed , COUNT(o.bug_id) AS count_open
FROM BugsProducts p
INNER JOIN Bugs f
ON (p.bug_id = f.bug_id AND f.status = 'FIXED')
INNER JOIN BugsProducts p2 USING (product_id)
INNER JOIN Bugs o
ON (p2.bug_id = o.bug_id AND o.status = 'OPEN')
WHERE p.product_id = 1
GROUP BY p.product_id;
想定では以下のような結果をもとに件数が集計できるはず。
FIXEDの結果 OPENの結果
件数集計
現実
FIXEDの結果 OPENの結果
件数集計
• デカルト積(直積)
–2つのテーブルの間に関連を制限
する条件を持たないときに生まれ
る。
–この制限がないと1つのテーブル
の各行が、もう1つのテーブルのす
べての行と組合せになってしまう。
両方のテーブルの行のすべての組み合わせが
取得されます。
SELECT p.product_id, COUNT(f.bug_id) AS count_fixed , COUNT(o.bug_id) AS count_open
FROM BugsProducts p
INNER JOIN Bugs f
ON (p.bug_id = f.bug_id AND f.status = 'FIXED')
INNER JOIN BugsProducts p2 USING (product_id)
INNER JOIN Bugs o
ON (p2.bug_id = o.bug_id AND o.status = 'OPEN')
WHERE p.product_id = 1
GROUP BY p.product_id;
①
②
①でstatusが'FIXED'
②でstatusが'OPEN'を求め
USINGで両者を関連付けているように見えて関連付けられていないため
①と②の2つの間に関連を制限する条件が持たないのでデカルト積が生じている。
アンチパターンの見つけ方
「ありえないくらいの行数が
返ってきてるんですけどー」
うん、直積(デカルト積)されて
結果が返ってきてるね。
複雑すぎてちゃんと結合条件
書けていないんじゃない?
「この複雑なSQLクエリを
書くのに、丸1日かかった
(ドヤァ!)」
そんなに時間かけるんだったら
もっと簡単にできる
アプローチ探したら?
「このレポート出力には、
もう何も追加できない。
SQLクエリを書き直すのは
手間がかかりすぎる!」
お前、
客先でも同じこと言えんのか?
「このクエリに、
もう1つDISTINCTを
追加してみよう(提案)」
直積で返ってくるのを集約して
ごまかしているだけだろ?
見た目は合うけど、
ソートや重複排除で
パフォーマンス最悪だよ?
アンチパターンを用いても
よい場合
プログラミングフレームワークや
ビジュアルコンポーネントライブラリ、
BIツール(安い)とか使ってる場合は
(仕方がないので)使ってもいいよ。
でも、複雑、難しいことを
無理やりやるより
別アプローチ探す方が
建設的だよ。
後、複数のクエリ結果をまとめて
ソートするような場合は
DBで行う方が効率的な場合が
多いかも。
解決策
ワンステップずつ
SELECT p.product_id, COUNT(f.bug_id) AS count_fixed
FROM BugsProducts p
LEFT OUTER JOIN Bugs f
ON (p.bug_id = f.bug_id AND f.status = 'FIXED')
WHERE p.product_id = 1
GROUP BY p.product_id;
SELECT p.product_id, COUNT(o.bug_id) AS count_open
FROM BugsProducts p
LEFT OUTER JOIN Bugs o
ON (p.bug_id = o.bug_id AND o.status = 'OPEN')
WHERE p.product_id = 1
GROUP BY p.product_id;
SELECT p.product_id, COUNT(f.bug_id) AS count_fixed , COUNT(o.bug_id) AS count_open
FROM BugsProducts p
INNER JOIN Bugs f
ON (p.bug_id = f.bug_id AND f.status = 'FIXED')
INNER JOIN BugsProducts p2 USING (product_id)
INNER JOIN Bugs o
ON (p2.bug_id = o.bug_id AND o.status = 'OPEN')
WHERE p.product_id = 1
GROUP BY p.product_id;
• 望まないデカルト積が生じない。
• 修正・変更が簡単。
• 一般的に単純なクエリの方がス
ムーズかつ確実に実行される。
• コードレビューも容易。
UNIONを用いる
(SELECT p.product_id, 'FIXED' AS status, COUNT(f.bug_id) AS bug_count
FROM BugsProducts p
LEFT OUTER JOIN Bugs f
ON (p.bug_id = f.bug_id AND f.status = 'FIXED')
WHERE p.product_id = 1
GROUP BY p.product_id)
UNION
(SELECT p.product_id, 'OPEN' AS status, COUNT(o.bug_id) AS bug_count
FROM BugsProducts p
LEFT OUTER JOIN Bugs o
ON (p.bug_id = o.bug_id AND o.status = 'OPEN')
WHERE p.product_id = 1
GROUP BY p.product_id)
ORDER BY bug_count DESC;
SELECT p.product_id, COUNT(f.bug_id) AS count_fixed , COUNT(o.bug_id) AS count_open
FROM BugsProducts p
INNER JOIN Bugs f
ON (p.bug_id = f.bug_id AND f.status = 'FIXED')
INNER JOIN BugsProducts p2 USING (product_id)
INNER JOIN Bugs o
ON (p2.bug_id = o.bug_id AND o.status = 'OPEN')
WHERE p.product_id = 1
GROUP BY p.product_id;
• 2つのサブクエリの結果を合わせ
たもの
CASE式とSUM関数を
組み合わせる
SELECT p.product_id,
SUM(CASE b.status WHEN 'FIXED' THEN 1 ELSE 0 END) AS count_fixed,
SUM(CASE b.status WHEN 'OPEN' THEN 1 ELSE 0 END) AS count_open
FROM BugsProducts p
INNER JOIN Bugs b USING (bug_id)
WHERE p.product_id = 1
GROUP BY p.product_id;
SELECT p.product_id, COUNT(f.bug_id) AS count_fixed , COUNT(o.bug_id) AS count_open
FROM BugsProducts p
INNER JOIN Bugs f
ON (p.bug_id = f.bug_id AND f.status = 'FIXED')
INNER JOIN BugsProducts p2 USING (product_id)
INNER JOIN Bugs o
ON (p2.bug_id = o.bug_id AND o.status = 'OPEN')
WHERE p.product_id = 1
GROUP BY p.product_id;
SELECT文の副問い合わせを
用いる
(SQLアンチパターンには
載っていない方法)
SELECT t1.product_id, count_fixed, count_open
FROM
(
SELECT p.product_id, COUNT(f.bug_id) AS count_fixed
FROM BugsProducts p INNER JOIN Bugs f
ON (p.bug_id = f.bug_id AND f.status = 'FIXED')
WHERE p.product_id = 1
GROUP BY p.product_id
) t1,
(
SELECT p2.product_id, COUNT(o.bug_id) AS count_open
FROM BugsProducts p2 INNER JOIN Bugs o
ON (p2.bug_id = o.bug_id AND o.status = 'OPEN')
WHERE p2.product_id = 1
GROUP BY p2.product_id
) t2
WHERE t1.product_id = t2.product_id;
SELECT p.product_id, COUNT(f.bug_id) AS count_fixed , COUNT(o.bug_id) AS count_open
FROM BugsProducts p
INNER JOIN Bugs f
ON (p.bug_id = f.bug_id AND f.status = 'FIXED')
INNER JOIN BugsProducts p2 USING (product_id)
INNER JOIN Bugs o
ON (p2.bug_id = o.bug_id AND o.status = 'OPEN')
WHERE p.product_id = 1
GROUP BY p.product_id;
• FROM句の中にSELECT文を記
述して一時テーブルを作成して
関連を制限する条件で結合する。
• FROM句の中のSELECT文は実
行時にメモリ上等に生成され、イ
ンデックスなどがないため、大き
な結果が返る副問い合わせでは
注意が必要。
求められている問題を
解決する
誰も1つのレポートで
結果くれとは言ってないよ?
SQLを用いたSQLの自動的な記述
複雑なSQLクエリを分割すると
似たようなクエリをいくつも作成す
ることがあります。
よく似たクエリをいくつも書くのは
煩わしいので
シェルやマクロ、場合によっては
SQLクエリ自体でSQLクエリを
生成しましょう。
最新の日付で更新する
SELECT CONCAT(
'UPDATE Inventory SET last_used = ''', MAX(u.usage_date), '''',
' WHERE inventory_id = ', u.inventory_id, ';') AS update_statement
FROM ComputerUsage u
GROUP BY u.inventory_id;
出力された結果をコピペして
SQL文として実行する。
まとめ
SQLでは、1行のコードで複雑な問
題を解決できると思える場合があ
ります。
しかし、状況に応じてクエリを分割
することも検討するようにしましょう。
デカルト積(直積)がどうこうとかで
はなくて、まったく同じ結果セットを
生む2つのクエリを選択できる場合
は、単純なクエリを選ぶべきであ
るということ。
また、クエリの結果セット以前に目
的を達成するためには単純な方
法が優れていることがあるというこ
とです。
以上、
ご清聴ありがとうございました。

More Related Content

What's hot

やってはいけない空振りDelete
やってはいけない空振りDeleteやってはいけない空振りDelete
やってはいけない空振りDeleteYu Yamada
 
イミュータブルデータモデル(世代編)
イミュータブルデータモデル(世代編)イミュータブルデータモデル(世代編)
イミュータブルデータモデル(世代編)Yoshitaka Kawashima
 
雑なMySQLパフォーマンスチューニング
雑なMySQLパフォーマンスチューニング雑なMySQLパフォーマンスチューニング
雑なMySQLパフォーマンスチューニングyoku0825
 
DockerコンテナでGitを使う
DockerコンテナでGitを使うDockerコンテナでGitを使う
DockerコンテナでGitを使うKazuhiro Suga
 
なぜ、いま リレーショナルモデルなのか(理論から学ぶデータベース実践入門読書会スペシャル)
なぜ、いま リレーショナルモデルなのか(理論から学ぶデータベース実践入門読書会スペシャル)なぜ、いま リレーショナルモデルなのか(理論から学ぶデータベース実践入門読書会スペシャル)
なぜ、いま リレーショナルモデルなのか(理論から学ぶデータベース実践入門読書会スペシャル)Mikiya Okuno
 
BigQueryを始めてみよう - Google Analytics データを活用する
BigQueryを始めてみよう - Google Analytics データを活用するBigQueryを始めてみよう - Google Analytics データを活用する
BigQueryを始めてみよう - Google Analytics データを活用するGoogle Cloud Platform - Japan
 
pg_hint_planを知る(第37回PostgreSQLアンカンファレンス@オンライン 発表資料)
pg_hint_planを知る(第37回PostgreSQLアンカンファレンス@オンライン 発表資料)pg_hint_planを知る(第37回PostgreSQLアンカンファレンス@オンライン 発表資料)
pg_hint_planを知る(第37回PostgreSQLアンカンファレンス@オンライン 発表資料)NTT DATA Technology & Innovation
 
ドメイン駆動設計のためのオブジェクト指向入門
ドメイン駆動設計のためのオブジェクト指向入門ドメイン駆動設計のためのオブジェクト指向入門
ドメイン駆動設計のためのオブジェクト指向入門増田 亨
 
関数型・オブジェクト指向 宗教戦争に疲れたなたに送るGo言語入門
関数型・オブジェクト指向宗教戦争に疲れたなたに送るGo言語入門関数型・オブジェクト指向宗教戦争に疲れたなたに送るGo言語入門
関数型・オブジェクト指向 宗教戦争に疲れたなたに送るGo言語入門Tadahiro Ishisaka
 
外部キー制約に伴うロックの小話
外部キー制約に伴うロックの小話外部キー制約に伴うロックの小話
外部キー制約に伴うロックの小話ichirin2501
 
RSA暗号運用でやってはいけない n のこと #ssmjp
RSA暗号運用でやってはいけない n のこと #ssmjpRSA暗号運用でやってはいけない n のこと #ssmjp
RSA暗号運用でやってはいけない n のこと #ssmjpsonickun
 
ネットストーカー御用達OSINTツールBlackBirdを触ってみた.pptx
ネットストーカー御用達OSINTツールBlackBirdを触ってみた.pptxネットストーカー御用達OSINTツールBlackBirdを触ってみた.pptx
ネットストーカー御用達OSINTツールBlackBirdを触ってみた.pptxShota Shinogi
 
メルカリ・ソウゾウでは どうGoを活用しているのか?
メルカリ・ソウゾウでは どうGoを活用しているのか?メルカリ・ソウゾウでは どうGoを活用しているのか?
メルカリ・ソウゾウでは どうGoを活用しているのか?Takuya Ueda
 
イミュータブルデータモデル(入門編)
イミュータブルデータモデル(入門編)イミュータブルデータモデル(入門編)
イミュータブルデータモデル(入門編)Yoshitaka Kawashima
 
オブジェクト指向エクササイズのススメ
オブジェクト指向エクササイズのススメオブジェクト指向エクササイズのススメ
オブジェクト指向エクササイズのススメYoji Kanno
 
オブジェクト指向できていますか?
オブジェクト指向できていますか?オブジェクト指向できていますか?
オブジェクト指向できていますか?Moriharu Ohzu
 
例外設計における大罪
例外設計における大罪例外設計における大罪
例外設計における大罪Takuto Wada
 
DDD x CQRS 更新系と参照系で異なるORMを併用して上手くいった話
DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話
DDD x CQRS 更新系と参照系で異なるORMを併用して上手くいった話Koichiro Matsuoka
 
組み込みLinuxでのGolangのススメ
組み込みLinuxでのGolangのススメ組み込みLinuxでのGolangのススメ
組み込みLinuxでのGolangのススメTetsuyuki Kobayashi
 

What's hot (20)

やってはいけない空振りDelete
やってはいけない空振りDeleteやってはいけない空振りDelete
やってはいけない空振りDelete
 
イミュータブルデータモデル(世代編)
イミュータブルデータモデル(世代編)イミュータブルデータモデル(世代編)
イミュータブルデータモデル(世代編)
 
雑なMySQLパフォーマンスチューニング
雑なMySQLパフォーマンスチューニング雑なMySQLパフォーマンスチューニング
雑なMySQLパフォーマンスチューニング
 
DockerコンテナでGitを使う
DockerコンテナでGitを使うDockerコンテナでGitを使う
DockerコンテナでGitを使う
 
なぜ、いま リレーショナルモデルなのか(理論から学ぶデータベース実践入門読書会スペシャル)
なぜ、いま リレーショナルモデルなのか(理論から学ぶデータベース実践入門読書会スペシャル)なぜ、いま リレーショナルモデルなのか(理論から学ぶデータベース実践入門読書会スペシャル)
なぜ、いま リレーショナルモデルなのか(理論から学ぶデータベース実践入門読書会スペシャル)
 
BigQueryを始めてみよう - Google Analytics データを活用する
BigQueryを始めてみよう - Google Analytics データを活用するBigQueryを始めてみよう - Google Analytics データを活用する
BigQueryを始めてみよう - Google Analytics データを活用する
 
pg_hint_planを知る(第37回PostgreSQLアンカンファレンス@オンライン 発表資料)
pg_hint_planを知る(第37回PostgreSQLアンカンファレンス@オンライン 発表資料)pg_hint_planを知る(第37回PostgreSQLアンカンファレンス@オンライン 発表資料)
pg_hint_planを知る(第37回PostgreSQLアンカンファレンス@オンライン 発表資料)
 
ドメイン駆動設計のためのオブジェクト指向入門
ドメイン駆動設計のためのオブジェクト指向入門ドメイン駆動設計のためのオブジェクト指向入門
ドメイン駆動設計のためのオブジェクト指向入門
 
関数型・オブジェクト指向 宗教戦争に疲れたなたに送るGo言語入門
関数型・オブジェクト指向宗教戦争に疲れたなたに送るGo言語入門関数型・オブジェクト指向宗教戦争に疲れたなたに送るGo言語入門
関数型・オブジェクト指向 宗教戦争に疲れたなたに送るGo言語入門
 
外部キー制約に伴うロックの小話
外部キー制約に伴うロックの小話外部キー制約に伴うロックの小話
外部キー制約に伴うロックの小話
 
RSA暗号運用でやってはいけない n のこと #ssmjp
RSA暗号運用でやってはいけない n のこと #ssmjpRSA暗号運用でやってはいけない n のこと #ssmjp
RSA暗号運用でやってはいけない n のこと #ssmjp
 
Tackling Complexity
Tackling ComplexityTackling Complexity
Tackling Complexity
 
ネットストーカー御用達OSINTツールBlackBirdを触ってみた.pptx
ネットストーカー御用達OSINTツールBlackBirdを触ってみた.pptxネットストーカー御用達OSINTツールBlackBirdを触ってみた.pptx
ネットストーカー御用達OSINTツールBlackBirdを触ってみた.pptx
 
メルカリ・ソウゾウでは どうGoを活用しているのか?
メルカリ・ソウゾウでは どうGoを活用しているのか?メルカリ・ソウゾウでは どうGoを活用しているのか?
メルカリ・ソウゾウでは どうGoを活用しているのか?
 
イミュータブルデータモデル(入門編)
イミュータブルデータモデル(入門編)イミュータブルデータモデル(入門編)
イミュータブルデータモデル(入門編)
 
オブジェクト指向エクササイズのススメ
オブジェクト指向エクササイズのススメオブジェクト指向エクササイズのススメ
オブジェクト指向エクササイズのススメ
 
オブジェクト指向できていますか?
オブジェクト指向できていますか?オブジェクト指向できていますか?
オブジェクト指向できていますか?
 
例外設計における大罪
例外設計における大罪例外設計における大罪
例外設計における大罪
 
DDD x CQRS 更新系と参照系で異なるORMを併用して上手くいった話
DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話
DDD x CQRS 更新系と参照系で異なるORMを併用して上手くいった話
 
組み込みLinuxでのGolangのススメ
組み込みLinuxでのGolangのススメ組み込みLinuxでのGolangのススメ
組み込みLinuxでのGolangのススメ
 

Similar to SQLアンチパターン~スパゲッティクエリ

SQLアンチパターン読書会 「スパゲッティクエリ」
SQLアンチパターン読書会 「スパゲッティクエリ」SQLアンチパターン読書会 「スパゲッティクエリ」
SQLアンチパターン読書会 「スパゲッティクエリ」makopi 23
 
【SQiP2014】システム操作インターフェイス最適化によるテスト自動化ROI向上
【SQiP2014】システム操作インターフェイス最適化によるテスト自動化ROI向上【SQiP2014】システム操作インターフェイス最適化によるテスト自動化ROI向上
【SQiP2014】システム操作インターフェイス最適化によるテスト自動化ROI向上Tatsuya Ishikawa
 
2011年10月28日
2011年10月28日2011年10月28日
2011年10月28日nukaemon
 
もっと早く知りたかったプログラミング技法9選
もっと早く知りたかったプログラミング技法9選もっと早く知りたかったプログラミング技法9選
もっと早く知りたかったプログラミング技法9選Masayuki Akiyama
 
Ec cubeの基礎からcms連携まで
Ec cubeの基礎からcms連携までEc cubeの基礎からcms連携まで
Ec cubeの基礎からcms連携までMakoto Nishimura
 
One Time Binding & Digest Loop
One Time Binding & Digest LoopOne Time Binding & Digest Loop
One Time Binding & Digest LoopKon Yuichi
 
Try_to_writecode_practicaltest #atest_hack
Try_to_writecode_practicaltest #atest_hackTry_to_writecode_practicaltest #atest_hack
Try_to_writecode_practicaltest #atest_hackkimukou_26 Kimukou
 
関数型言語&形式的手法セミナー(3)
関数型言語&形式的手法セミナー(3)関数型言語&形式的手法セミナー(3)
関数型言語&形式的手法セミナー(3)啓 小笠原
 

Similar to SQLアンチパターン~スパゲッティクエリ (10)

SQLアンチパターン読書会 「スパゲッティクエリ」
SQLアンチパターン読書会 「スパゲッティクエリ」SQLアンチパターン読書会 「スパゲッティクエリ」
SQLアンチパターン読書会 「スパゲッティクエリ」
 
【SQiP2014】システム操作インターフェイス最適化によるテスト自動化ROI向上
【SQiP2014】システム操作インターフェイス最適化によるテスト自動化ROI向上【SQiP2014】システム操作インターフェイス最適化によるテスト自動化ROI向上
【SQiP2014】システム操作インターフェイス最適化によるテスト自動化ROI向上
 
2011年10月28日
2011年10月28日2011年10月28日
2011年10月28日
 
Ruby test double
Ruby test doubleRuby test double
Ruby test double
 
もっと早く知りたかったプログラミング技法9選
もっと早く知りたかったプログラミング技法9選もっと早く知りたかったプログラミング技法9選
もっと早く知りたかったプログラミング技法9選
 
Ec cubeの基礎からcms連携まで
Ec cubeの基礎からcms連携までEc cubeの基礎からcms連携まで
Ec cubeの基礎からcms連携まで
 
One Time Binding & Digest Loop
One Time Binding & Digest LoopOne Time Binding & Digest Loop
One Time Binding & Digest Loop
 
Try_to_writecode_practicaltest #atest_hack
Try_to_writecode_practicaltest #atest_hackTry_to_writecode_practicaltest #atest_hack
Try_to_writecode_practicaltest #atest_hack
 
Hadoop jobbuilder
Hadoop jobbuilderHadoop jobbuilder
Hadoop jobbuilder
 
関数型言語&形式的手法セミナー(3)
関数型言語&形式的手法セミナー(3)関数型言語&形式的手法セミナー(3)
関数型言語&形式的手法セミナー(3)
 

SQLアンチパターン~スパゲッティクエリ