SlideShare a Scribd company logo
SQLアンチパターン読書会
17章:スパゲッティクエリ
2014/4/3(木)
@makopi23
イメージ
複雑に絡み合い、解読や修正が困難なSQLクエリ
3
 17.1 目的: SQLクエリの数を減らす
 SQLプログラマーが最も多く直面する問題
どのようにして目の前の仕事を1つのクエリで実現するか
 解決策はシンプルにしたい
タスクを1つのクエリで解決することで、クエリを「優雅に」
「効率的に」書くことを目的とする。
4
 17.2 アンチパターン
 SQLは非常に表現力に優れた言語・・・
➢ 1つのクエリやステートメントで多くのことを実現できる
➢ だからといって、1つのクエリですべてのタスクを処理
することを強制するものではない
複雑な問題をワンステップで解決しようとする複雑な問題をワンステップで解決しようとする
5
 17.2.1 意図に反した結果 (1/5)
● デカルト積 (Cartesian product)
➢ クエリで指定する2つのテーブルが関連(リレーション
シップ)を制限する条件を持たないときに生まれる。
➢
この条件がないと、2つのテーブルを結合することに
よって、1つのテーブルの各行が、もう1つのテーブルの
すべてのテーブルのすべての行とペアになってしまう。
➢ いわゆる、直積集合 (Cross Join)。
6
 17.2.1 意図に反した結果 (2/5)
同じテーブル2つに対し、
prodcut_idをキーにJOIN・・・
⇒ 直積(デカルト積)が発生
その製品の修正済みのバグ数が11件、
未修整のバグが7件であるはずなのに、
両方とも 77件 (= 11件 × 7件)となっている。
■ Spaghetti-Query/anti/cartesian.sql
7
 17.2.1 意図に反した結果 (3/5)
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 BugsProducts p
INNER JOIN Bugs f ON p.bug_id = f.bug_id AND f.status = 'FIXED'
INNER JOIN Bugs o ON p2.bug_id = o.bug_id AND o.status = 'OPEN'
INNER JOIN BugsProducts p2
●BugsProducts p
●Bugs f
➢ bug_idで結合
➢ statusが'FIXED'
●BugsProducts p2
●Bugs o
➢ bug_idで結合
➢ statusが'OPEN'
■ Spaghetti-Query/anti/cartesian.sql
FIXEDとOPEN
のバグの組み合
わせを制限する
条件なし。。。
8
11行の修正済み(FIXED)バグが、
7行の未修整(OPEN)バグと
すべてペアに
11行の修正済み(FIXED)バグが、
7行の未修整(OPEN)バグと
すべてペアに
9
■ cartesian-no-group.sql の Select句を Select * に変更した結果
 17.2.1 意図に反した結果 (5/5)
10
 17.2.2 さらなる弊害
● 1つのクエリで複数のタスクを行おうとすると・・・
➢
意図しない結果が導かれる
➢ クエリの記述や修正、デバッグが難しくなる
➢ 実行時のコストが上がる
(手の込んだSQLは最適化処理などが難しくなる)
11
 17.3 アンチパターンの見つけ方
● SUM関数やCOUNT関数の結果があり得ないくら
いに大きくなってるのはなぜ?
● このお化けみたいに複雑SQLクエリを書くのに、丸
1日かかったよ!
●
このレポート出力には、もう何も追加できない。この
SQLクエリを書き直すのは手間がかかりすぎる
● このクエリに、もう1つDISTINCTを追加してみよう
●
(実行時間が長すぎる!)
12
17.4 アンチパターンを用いてもよい場合
●
単一のクエリをデータソースに結び付けてアプリケーション
にデータを表示するような、プログラミングフレームワーク
やビジュアルコンポーネントライブラリ、レポートツールを
使っている場合
➢ ただ、レポートの要件が1つのSQLクエリで実現するにはあ
まりにも複雑な場合は、レポートを複数作成した方がよいか
もしれない
● 複数の結果を1つのソート順で表示させるために、1つの
クエリから複雑な結果を得たい場合
➢ SQLクエリでは、ソート順を簡単に指定できる
➢ 複数のクエリ結果をソートするするには、アプリケーション
コードを書くよりもDBでソートした方が効率的な場合が多い
13
 17.5 解決策:分割統治を行う
● 節約の原則(the law of parsimony)
➢
まったく同じ予測をする2つの競合する理論があるとき
は、単純な方が優れている。
● この原則をSQLに当てはめると・・・
➢
まったく同じ結果セットを生む2つのクエリを選択できる
場合は、単純なクエリを選ぶべき。
14
 17.5.1 ワンステップずつ (1/2)
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 p2.product_id, COUNT(o.bug_id) AS count_open
FROM BugsProducts p2
LEFT OUTER JOIN Bugs o ON p2.bug_id = o.bug_id AND o.status = 'OPEN'
WHERE p2.product_id = 1
GROUP BY p2.product_id;
FROM BugsProducts p
INNER JOIN Bugs f ON p.bug_id = f.bug_id AND f.status = 'FIXED'
INNER JOIN Bugs o ON p2.bug_id = o.bug_id AND o.status = 'OPEN'
INNER JOIN BugsProducts p2
SELECT p.product_id,
COUNT(f.bug_id) AS count_fixed,
COUNT(o.bug_id) AS count_open
WHERE p.product_id = 1 GROUP BY p.product_id;
USING (product_id)
デカルト積を避けるために、クエリを分割デカルト積を避けるために、クエリを分割
Spaghetti-Query/anti/cartesian.sql
Spaghetti-Query/soln/split-query.sql
15
 17.5.1 ワンステップずつ (2/2)
● クエリ分割は様々なメリットをもたらす
➢
デカルト積が生じない。
➢ 新たな要件が追加された場合、すでに複雑なクエリをさ
らに複雑にするより、単純なクエリを新たに書く方がは
るかに簡単。
➢ 一般的に、SQLエンジンは複雑なクエリよりも単純なク
エリの方がスムーズかつ確実に実行できる。
➢
コードレビューなどでは、シンプルな複数のクエリを説明
する方が、1つの複雑なクエリを説明するより簡単。
16
 17.5.2 UNIONを用いる
複数のクエリの結果は、UNIONによって1つの結果セットにまとめられる。
2つのサブクエリの結果を区別するための
列として、status列を使用している。
UNIONは、両方のサブクエリの列に互換性が
あるときにのみ使用できる。
17
 17.5.3 CASE式とSUM関数を組み合わせる
条件ごとの集約を1つのクエリでシンプルに行うために、CASE式とSUM
関数を組み合わせる方法がよく使われる。
18
 17.5.4 上司の問題を解決する
最前の解決策は、上司から求められたタスクを分割して処理すること。
■誰かが取り扱っている製品の数:
SELECT COUNT(*) AS how_many_products FROM Products;
■バグを修正した開発者の数:
SELECT COUNT(DISTINCT assigned_to) AS how_many_developers
FROM Bugs WHERE status = 'FIXED';
■開発者1人あたりの平均バグ修正数:
SELECT AVG(bugs_per_developer) AS average_bugs_per_developer
FROM (SELECT dev.account_id, COUNT(*) AS bugs_per_developer
FROM Bugs b INNER JOIN Accounts dev
ON b.assigned_to = dev.account_id
WHERE b.status = 'FIXED' GROUP BY dev.account_id) t;
■修正したバグの中で顧客から報告されたバグの数:
SELECT COUNT(*) AS how_many_customer_bugs
FROM Bugs b INNER JOIN Accounts cust ON b.reported_by = cust.account_id
WHERE b.status = 'FIXED' AND cust.email NOT LIKE '%@example.com';
19
 17.5.5 SQLを用いたSQLの自動的な記述
■ コラム: 複数のUPDATEステートメント生成
【例】 列 last_used の値を、各コンピュータが使用された最新の日付に設定する:
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クエリを分割すると、データの値によっ
てわずかに異なる、似たようなクエリをいくつも生
成することがある。
➢ それは煩わしいので、「コード生成」を行いましょう。
➢ 「コード生成」は、新しいコードを手で書くには非常に労
力がかかるような場面で効果的。
20
まとめ
SQLSQLでは、1行のコードで複雑な問題を解決できでは、1行のコードで複雑な問題を解決でき
ると思える場合があります。ると思える場合があります。
しかし、状況に応じてクエリを分割することも検討しかし、状況に応じてクエリを分割することも検討
するようにしましょう。するようにしましょう。

More Related Content

What's hot

MySQLアーキテクチャ図解講座
MySQLアーキテクチャ図解講座MySQLアーキテクチャ図解講座
MySQLアーキテクチャ図解講座
Mikiya Okuno
 
SQLアンチパターン(インデックスショットガン)
SQLアンチパターン(インデックスショットガン)SQLアンチパターン(インデックスショットガン)
SQLアンチパターン(インデックスショットガン)Tomoaki Uchida
 
プログラマーのためのテスト手法
プログラマーのためのテスト手法プログラマーのためのテスト手法
プログラマーのためのテスト手法
myukidi
 
これからのJDK/JVM 何を選ぶ?どう選ぶ?
これからのJDK/JVM 何を選ぶ?どう選ぶ?これからのJDK/JVM 何を選ぶ?どう選ぶ?
これからのJDK/JVM 何を選ぶ?どう選ぶ?
Takahiro YAMADA
 
Tackling Complexity
Tackling ComplexityTackling Complexity
Tackling Complexity
Yoshitaka Kawashima
 
オブジェクト指向できていますか?
オブジェクト指向できていますか?オブジェクト指向できていますか?
オブジェクト指向できていますか?Moriharu Ohzu
 
SQL大量発行処理をいかにして高速化するか
SQL大量発行処理をいかにして高速化するかSQL大量発行処理をいかにして高速化するか
SQL大量発行処理をいかにして高速化するか
Shogo Wakayama
 
Oss貢献超入門
Oss貢献超入門Oss貢献超入門
Oss貢献超入門
Michihito Shigemura
 
Java ORマッパー選定のポイント #jsug
Java ORマッパー選定のポイント #jsugJava ORマッパー選定のポイント #jsug
Java ORマッパー選定のポイント #jsug
Masatoshi Tada
 
ツール比較しながら語る O/RマッパーとDBマイグレーションの実際のところ
ツール比較しながら語る O/RマッパーとDBマイグレーションの実際のところツール比較しながら語る O/RマッパーとDBマイグレーションの実際のところ
ツール比較しながら語る O/RマッパーとDBマイグレーションの実際のところ
Y Watanabe
 
マルチスレッド問題の特定と再現に頑張った話
マルチスレッド問題の特定と再現に頑張った話マルチスレッド問題の特定と再現に頑張った話
マルチスレッド問題の特定と再現に頑張った話
LINE Corporation
 
並列クエリを実行するPostgreSQLのアーキテクチャ
並列クエリを実行するPostgreSQLのアーキテクチャ並列クエリを実行するPostgreSQLのアーキテクチャ
並列クエリを実行するPostgreSQLのアーキテクチャ
Kohei KaiGai
 
Jvm internal
Jvm internalJvm internal
Jvm internalGo Tanaka
 
InnoDBのすゝめ(仮)
InnoDBのすゝめ(仮)InnoDBのすゝめ(仮)
InnoDBのすゝめ(仮)
Takanori Sejima
 
Introduction to Java 11: Support and JVM Features #jjug
Introduction to Java 11: Support and JVM Features #jjugIntroduction to Java 11: Support and JVM Features #jjug
Introduction to Java 11: Support and JVM Features #jjug
Yuji Kubota
 
mongodb와 mysql의 CRUD 연산의 성능 비교
mongodb와 mysql의 CRUD 연산의 성능 비교mongodb와 mysql의 CRUD 연산의 성능 비교
mongodb와 mysql의 CRUD 연산의 성능 비교Woo Yeong Choi
 
Cache-Oblivious データ構造入門 @DSIRNLP#5
Cache-Oblivious データ構造入門 @DSIRNLP#5Cache-Oblivious データ構造入門 @DSIRNLP#5
Cache-Oblivious データ構造入門 @DSIRNLP#5Takuya Akiba
 
プログラムを高速化する話
プログラムを高速化する話プログラムを高速化する話
プログラムを高速化する話
京大 マイコンクラブ
 
MySQLで論理削除と正しく付き合う方法
MySQLで論理削除と正しく付き合う方法MySQLで論理削除と正しく付き合う方法
MySQLで論理削除と正しく付き合う方法
yoku0825
 
ProxySQL for MySQL
ProxySQL for MySQLProxySQL for MySQL
ProxySQL for MySQL
Mydbops
 

What's hot (20)

MySQLアーキテクチャ図解講座
MySQLアーキテクチャ図解講座MySQLアーキテクチャ図解講座
MySQLアーキテクチャ図解講座
 
SQLアンチパターン(インデックスショットガン)
SQLアンチパターン(インデックスショットガン)SQLアンチパターン(インデックスショットガン)
SQLアンチパターン(インデックスショットガン)
 
プログラマーのためのテスト手法
プログラマーのためのテスト手法プログラマーのためのテスト手法
プログラマーのためのテスト手法
 
これからのJDK/JVM 何を選ぶ?どう選ぶ?
これからのJDK/JVM 何を選ぶ?どう選ぶ?これからのJDK/JVM 何を選ぶ?どう選ぶ?
これからのJDK/JVM 何を選ぶ?どう選ぶ?
 
Tackling Complexity
Tackling ComplexityTackling Complexity
Tackling Complexity
 
オブジェクト指向できていますか?
オブジェクト指向できていますか?オブジェクト指向できていますか?
オブジェクト指向できていますか?
 
SQL大量発行処理をいかにして高速化するか
SQL大量発行処理をいかにして高速化するかSQL大量発行処理をいかにして高速化するか
SQL大量発行処理をいかにして高速化するか
 
Oss貢献超入門
Oss貢献超入門Oss貢献超入門
Oss貢献超入門
 
Java ORマッパー選定のポイント #jsug
Java ORマッパー選定のポイント #jsugJava ORマッパー選定のポイント #jsug
Java ORマッパー選定のポイント #jsug
 
ツール比較しながら語る O/RマッパーとDBマイグレーションの実際のところ
ツール比較しながら語る O/RマッパーとDBマイグレーションの実際のところツール比較しながら語る O/RマッパーとDBマイグレーションの実際のところ
ツール比較しながら語る O/RマッパーとDBマイグレーションの実際のところ
 
マルチスレッド問題の特定と再現に頑張った話
マルチスレッド問題の特定と再現に頑張った話マルチスレッド問題の特定と再現に頑張った話
マルチスレッド問題の特定と再現に頑張った話
 
並列クエリを実行するPostgreSQLのアーキテクチャ
並列クエリを実行するPostgreSQLのアーキテクチャ並列クエリを実行するPostgreSQLのアーキテクチャ
並列クエリを実行するPostgreSQLのアーキテクチャ
 
Jvm internal
Jvm internalJvm internal
Jvm internal
 
InnoDBのすゝめ(仮)
InnoDBのすゝめ(仮)InnoDBのすゝめ(仮)
InnoDBのすゝめ(仮)
 
Introduction to Java 11: Support and JVM Features #jjug
Introduction to Java 11: Support and JVM Features #jjugIntroduction to Java 11: Support and JVM Features #jjug
Introduction to Java 11: Support and JVM Features #jjug
 
mongodb와 mysql의 CRUD 연산의 성능 비교
mongodb와 mysql의 CRUD 연산의 성능 비교mongodb와 mysql의 CRUD 연산의 성능 비교
mongodb와 mysql의 CRUD 연산의 성능 비교
 
Cache-Oblivious データ構造入門 @DSIRNLP#5
Cache-Oblivious データ構造入門 @DSIRNLP#5Cache-Oblivious データ構造入門 @DSIRNLP#5
Cache-Oblivious データ構造入門 @DSIRNLP#5
 
プログラムを高速化する話
プログラムを高速化する話プログラムを高速化する話
プログラムを高速化する話
 
MySQLで論理削除と正しく付き合う方法
MySQLで論理削除と正しく付き合う方法MySQLで論理削除と正しく付き合う方法
MySQLで論理削除と正しく付き合う方法
 
ProxySQL for MySQL
ProxySQL for MySQLProxySQL for MySQL
ProxySQL for MySQL
 

Similar to SQLアンチパターン読書会 「スパゲッティクエリ」

少しずつ手厚くして不具合や仕様漏れを防ぐために
少しずつ手厚くして不具合や仕様漏れを防ぐために少しずつ手厚くして不具合や仕様漏れを防ぐために
少しずつ手厚くして不具合や仕様漏れを防ぐために
Fumiya Sakai
 
タイムボックス制約付きインクリメンタル開発
タイムボックス制約付きインクリメンタル開発タイムボックス制約付きインクリメンタル開発
タイムボックス制約付きインクリメンタル開発
HIDEKAZU MATSUURA
 
Application Architecture for Enterprise Win Store Apps with DDD Pattern
Application Architecture for Enterprise Win Store Apps with DDD PatternApplication Architecture for Enterprise Win Store Apps with DDD Pattern
Application Architecture for Enterprise Win Store Apps with DDD PatternAtsushi Kambara
 
【17-C-2】 クラウド上でのエンタープライズアプリケーション開発
【17-C-2】 クラウド上でのエンタープライズアプリケーション開発【17-C-2】 クラウド上でのエンタープライズアプリケーション開発
【17-C-2】 クラウド上でのエンタープライズアプリケーション開発
lalha
 
ScalaMatsuri 2016
ScalaMatsuri 2016ScalaMatsuri 2016
ScalaMatsuri 2016
Yoshitaka Fujii
 
Coderetreat
CoderetreatCoderetreat
Coderetreat
YAMANE Toshiaki
 
WTM53 phpフレームワーク いまさらcodeigniter
WTM53 phpフレームワーク いまさらcodeigniterWTM53 phpフレームワーク いまさらcodeigniter
WTM53 phpフレームワーク いまさらcodeigniterMasanori Oobayashi
 
Build 番号の自動更新スクリプトについて #cocoa_kansai
Build 番号の自動更新スクリプトについて #cocoa_kansaiBuild 番号の自動更新スクリプトについて #cocoa_kansai
Build 番号の自動更新スクリプトについて #cocoa_kansai
Tomohiro Kumagai
 
Japan Container Day 2018
Japan Container Day 2018Japan Container Day 2018
Japan Container Day 2018
Yoshio Terada
 
Scala + Finagleの魅力
Scala + Finagleの魅力Scala + Finagleの魅力
Scala + Finagleの魅力Kota Mizushima
 
はじめてのAngular その1
はじめてのAngular その1はじめてのAngular その1
はじめてのAngular その1
純一 榮枝
 
初心者向け SQLite の始め方
初心者向け SQLite の始め方初心者向け SQLite の始め方
初心者向け SQLite の始め方
suno88
 
Session3 - LabVIEW NXG Training Course
Session3 - LabVIEW NXG Training CourseSession3 - LabVIEW NXG Training Course
Session3 - LabVIEW NXG Training Course
Yusuke Tochigi
 
【SQiP2014】システム操作インターフェイス最適化によるテスト自動化ROI向上
【SQiP2014】システム操作インターフェイス最適化によるテスト自動化ROI向上【SQiP2014】システム操作インターフェイス最適化によるテスト自動化ROI向上
【SQiP2014】システム操作インターフェイス最適化によるテスト自動化ROI向上
Tatsuya Ishikawa
 
iOSやAndroidアプリ開発のGoodPractice
iOSやAndroidアプリ開発のGoodPracticeiOSやAndroidアプリ開発のGoodPractice
iOSやAndroidアプリ開発のGoodPractice
Ken Morishita
 
Swift を振り返ってみよう #cswift
Swift を振り返ってみよう #cswiftSwift を振り返ってみよう #cswift
Swift を振り返ってみよう #cswift
Tomohiro Kumagai
 
レガシーコード改善ガイド 第7章 いつまでたっても変更作業が終わりません
レガシーコード改善ガイド 第7章 いつまでたっても変更作業が終わりませんレガシーコード改善ガイド 第7章 いつまでたっても変更作業が終わりません
レガシーコード改善ガイド 第7章 いつまでたっても変更作業が終わりませんTakahiro Okada
 
VSCodeで始めるAzure Static Web Apps開発
VSCodeで始めるAzure Static Web Apps開発VSCodeで始めるAzure Static Web Apps開発
VSCodeで始めるAzure Static Web Apps開発
Yuta Matsumura
 
iOSアプリケーションの Unit Test
iOSアプリケーションの Unit TestiOSアプリケーションの Unit Test
iOSアプリケーションの Unit Test
Katsumi Kishikawa
 

Similar to SQLアンチパターン読書会 「スパゲッティクエリ」 (20)

少しずつ手厚くして不具合や仕様漏れを防ぐために
少しずつ手厚くして不具合や仕様漏れを防ぐために少しずつ手厚くして不具合や仕様漏れを防ぐために
少しずつ手厚くして不具合や仕様漏れを防ぐために
 
タイムボックス制約付きインクリメンタル開発
タイムボックス制約付きインクリメンタル開発タイムボックス制約付きインクリメンタル開発
タイムボックス制約付きインクリメンタル開発
 
Application Architecture for Enterprise Win Store Apps with DDD Pattern
Application Architecture for Enterprise Win Store Apps with DDD PatternApplication Architecture for Enterprise Win Store Apps with DDD Pattern
Application Architecture for Enterprise Win Store Apps with DDD Pattern
 
【17-C-2】 クラウド上でのエンタープライズアプリケーション開発
【17-C-2】 クラウド上でのエンタープライズアプリケーション開発【17-C-2】 クラウド上でのエンタープライズアプリケーション開発
【17-C-2】 クラウド上でのエンタープライズアプリケーション開発
 
ScalaMatsuri 2016
ScalaMatsuri 2016ScalaMatsuri 2016
ScalaMatsuri 2016
 
Coderetreat
CoderetreatCoderetreat
Coderetreat
 
WTM53 phpフレームワーク いまさらcodeigniter
WTM53 phpフレームワーク いまさらcodeigniterWTM53 phpフレームワーク いまさらcodeigniter
WTM53 phpフレームワーク いまさらcodeigniter
 
Build 番号の自動更新スクリプトについて #cocoa_kansai
Build 番号の自動更新スクリプトについて #cocoa_kansaiBuild 番号の自動更新スクリプトについて #cocoa_kansai
Build 番号の自動更新スクリプトについて #cocoa_kansai
 
Japan Container Day 2018
Japan Container Day 2018Japan Container Day 2018
Japan Container Day 2018
 
エコSmalltalk
エコSmalltalkエコSmalltalk
エコSmalltalk
 
Scala + Finagleの魅力
Scala + Finagleの魅力Scala + Finagleの魅力
Scala + Finagleの魅力
 
はじめてのAngular その1
はじめてのAngular その1はじめてのAngular その1
はじめてのAngular その1
 
初心者向け SQLite の始め方
初心者向け SQLite の始め方初心者向け SQLite の始め方
初心者向け SQLite の始め方
 
Session3 - LabVIEW NXG Training Course
Session3 - LabVIEW NXG Training CourseSession3 - LabVIEW NXG Training Course
Session3 - LabVIEW NXG Training Course
 
【SQiP2014】システム操作インターフェイス最適化によるテスト自動化ROI向上
【SQiP2014】システム操作インターフェイス最適化によるテスト自動化ROI向上【SQiP2014】システム操作インターフェイス最適化によるテスト自動化ROI向上
【SQiP2014】システム操作インターフェイス最適化によるテスト自動化ROI向上
 
iOSやAndroidアプリ開発のGoodPractice
iOSやAndroidアプリ開発のGoodPracticeiOSやAndroidアプリ開発のGoodPractice
iOSやAndroidアプリ開発のGoodPractice
 
Swift を振り返ってみよう #cswift
Swift を振り返ってみよう #cswiftSwift を振り返ってみよう #cswift
Swift を振り返ってみよう #cswift
 
レガシーコード改善ガイド 第7章 いつまでたっても変更作業が終わりません
レガシーコード改善ガイド 第7章 いつまでたっても変更作業が終わりませんレガシーコード改善ガイド 第7章 いつまでたっても変更作業が終わりません
レガシーコード改善ガイド 第7章 いつまでたっても変更作業が終わりません
 
VSCodeで始めるAzure Static Web Apps開発
VSCodeで始めるAzure Static Web Apps開発VSCodeで始めるAzure Static Web Apps開発
VSCodeで始めるAzure Static Web Apps開発
 
iOSアプリケーションの Unit Test
iOSアプリケーションの Unit TestiOSアプリケーションの Unit Test
iOSアプリケーションの Unit Test
 

SQLアンチパターン読書会 「スパゲッティクエリ」