Copyright 2016-2018 G1SystemsInc.
CROSS JOIN はダミーを作るときなど
0~9まで入ったnumテーブルを用意して……
SELECT
(a.id * 100 + b.id * 10 + c.id) AS id
'ダミー文字列'
|| (a.id * 100 + b.id * 10 + c.id) AS col1
FROM
num a, num b, num c;
30
31.
Copyright 2016-2018 G1SystemsInc.
テスト(パラメータテーブル)でも使える
xxx区分(A~D)、xxx種別(01~09)のすべての
組み合わせ(テストパターン)を作る。
-- CREATE TABLE wrk_テストパターン AS
SELECT
a.区分, a.区分名, b.種別, b.種別名
INTO wrk_テストパターン
FROM
xxx区分 a, xxx種別 b;
31
32.
Copyright 2016-2018 G1SystemsInc.
INNER JOIN と OUTER JOIN
32
2 C
生徒ID ランク
3 A
10 B
33 C
40 B
48 C
1石野 扶樹
ID氏名
2岩崎 研二
3岩間 竜也
4上野 浩介
5岡崎 雅之
6岡島 慶二
生徒マスタ 特待生マスタ
INNER JOIN と OUTER JOIN で、結果はどう変わる?
生徒マスタ a INNER JOIN 特待生マスタ b ON a.ID = b.生徒ID
生徒マスタ a LEFT OUTER JOIN 特待生マスタ b ON a.ID = b.生徒ID
生徒マスタ a RIGHT OUTER JOIN 特待生マスタ b ON a.ID = b.生徒ID
1
削除フラグ
0
0
0
0
0
33.
Copyright 2016-2018 G1SystemsInc.33
生徒マスタ 特待生マスタ
生徒マスタ a INNER JOIN 特待生マスタ b ON a.ID = b.生徒ID
INNER JOIN
INNER JOIN は、マッチした部分だけが残る。
2 C
生徒ID ランク
3 A
10 B
33 C
40 B
48 C
1石野 扶樹
ID氏名
2岩崎 研二
3岩間 竜也
4上野 浩介
5岡崎 雅之
6岡島 慶二
1
削除フラグ
0
0
0
0
0
34.
Copyright 2016-2018 G1SystemsInc.34
生徒マスタ 特待生マスタ
生徒マスタ a LEFT OUTER JOIN 特待生マスタ b ON a.ID = b.生徒ID
LEFT OUTER JOIN
2 C
生徒ID ランク
3 A
10 B
33 C
40 B
48 C
1石野 扶樹
ID氏名
2岩崎 研二
3岩間 竜也
4上野 浩介
5岡崎 雅之
6岡島 慶二
1
削除フラグ
0
0
0
0
0
LEFT OUTER JOIN は、左側テーブルがすべて残る。
35.
Copyright 2016-2018 G1SystemsInc.35
生徒マスタ 特待生マスタ
生徒マスタ a RIGHT OUTER JOIN 特待生マスタ b ON a.ID = b.生徒ID
RIGHT OUTER JOIN
2 C
生徒ID ランク
3 A
10 B
33 C
40 B
48 C
1石野 扶樹
ID氏名
2岩崎 研二
3岩間 竜也
4上野 浩介
5岡崎 雅之
6岡島 慶二
1
削除フラグ
0
0
0
0
0
RIGHT OUTER JOIN は、右側テーブルがすべて残る。
36.
Copyright 2016-2018 G1SystemsInc.36
例題
すべての生徒のリストに、特待生マスタの削除フラグが 0の
特待生ランクを出力してください。
2 C
生徒ID ランク
3 A
10 B
33 C
40 B
48 C
1石野 扶樹
ID氏名
2岩崎 研二
3岩間 竜也
4上野 浩介
5岡崎 雅之
6岡島 慶二
生徒マスタ 特待生マスタ
1
削除フラグ
0
0
0
0
0
37.
Copyright 2016-2018 G1SystemsInc.
解答
37
SELECT a.氏名, b.ランク
FROM 生徒マスタ a
LEFT OUTER JOIN 特待生マスタ b
ここに結合条件を追加
これを出力
したくない
生徒マスタ 特待生マスタ
2 C
生徒ID ランク
3 A
10 B
33 C
40 B
48 C
1石野 扶樹
ID氏名
2岩崎 研二
3岩間 竜也
4上野 浩介
5岡崎 雅之
6岡島 慶二
1
削除フラグ
0
0
0
0
0
38.
Copyright 2016-2018 G1SystemsInc.
これはNG!
38
SELECT
a.氏名, b.ランク
FROM 生徒マスタ a
LEFT OUTER JOIN 特待生マスタ b
ON a.ID = b.生徒ID
WHERE
b.削除フラグ = 0;
Copyright 2016-2018 G1SystemsInc.
サブクエリーは意味は正しいけれど……
40
SELECT
a.氏名, b.ランク
FROM 生徒マスタ a
LEFT OUTER JOIN
(SELECT * FROM 特待生マスタ
WHERE 削除フラグ = 0) b
ON a.ID = b.生徒ID;
41.
Copyright 2016-2018 G1SystemsInc.
正しくはこうする!
41
SELECT
a.氏名, b.ランク
FROM 生徒マスタ a
LEFT OUTER JOIN 特待生マスタ b
ON a.ID = b.生徒ID
AND 0 = b.削除フラグ;
42.
Copyright 2016-2018 G1SystemsInc.
イメージにすると……
42
生徒マスタ a 特待生マスタ b
a.ID = b.生徒ID
AND b.削除フラグ = 0
a.ID = b.生徒ID
AND b.削除フラグ <> 0
a.ID <> b.生徒ID
AND b.削除フラグ = 0
a.ID <> b.生徒ID
AND b.削除フラグ <> 0
b.削除フラグ = 0 で
全体を削りたい訳ではない。
43.
Copyright 2016-2018 G1SystemsInc.
SQLServer や Oracle などはサブクエリを直す
SQLServer や Oracle などの商用DBは、
44
SELECT
a.氏名, b.ランク
FROM 生徒マスタ a
LEFT OUTER JOIN
(SELECT * FROM 特待生マスタ
WHERE 削除フラグ = 0) b
ON a.ID = b.生徒ID;
SELECT
a.氏名, b.ランク
FROM 生徒マスタ a
LEFT OUTER JOIN 特待生マスタ b
ON a.ID = b.生徒ID
AND 0 = b.削除フラグ;
パースの段階で SQL を直してから実行します。
直してくれない RDBMS は めっちゃ遅い!
特にハッシュジョインがない MySQL は OUT!
44.
Copyright 2016-2018 G1SystemsInc.
右に置いたテーブルがWHERE句に入るとバグ
45
SELECT
a.氏名, b.ランク
FROM 生徒マスタ a
LEFT OUTER JOIN 特待生マスタ b
ON a.ID = b.生徒ID
WHERE
b.ランク = 'A';
外部結合を打消してしまう。
どちらを意図しているのか、
ソースからは分からない。
45.
Copyright 2016-2018 G1SystemsInc.
ところが、例えば、WordPress 4.9.4 での例
if ( $post_status_join ) {
$join .= " LEFT JOIN {$wpdb->posts} AS p2 ON
({$wpdb->posts}.post_parent = p2.ID)";
foreach ( $statuswheres as $index => $statuswhere ) {
$statuswheres[$index] =
"($statuswhere
OR ({$wpdb->posts}.post_status = 'inherit’
AND ".str_replace( $wpdb->posts, 'p2', $statuswhere ) . "))";
}
}
$where_status = implode( ' OR ', $statuswheres );
if ( ! empty( $where_status ) ) {
$where .= " AND ($where_status)";
} ※ class-wp-query.php
46
46.
Copyright 2016-2018 G1SystemsInc.
WordPress 4.9.4 は、最後に LEFT JOIN に変換
/** Generates SQL clauses to be appended to a main query.
-- 中略 --
public function get_sql( $type, $primary_table, ……
-- 中略 --
/* If any JOINs are LEFT JOINs (as in the case of NOT EXISTS), then all JOINs should be
LEFT. Otherwise posts with no metadata will be excluded from results. */
-- 中略 --
if ( false !== strpos( $sql['join'], 'LEFT JOIN' ) ) {
$sql['join'] = str_replace( 'INNER JOIN', 'LEFT JOIN', $sql['join'] );
}
-- 中略 -- ※ class-wp-meta-query.php
47
Copyright 2016-2018 G1SystemsInc.
論理演算を復習しよう!
AND(論理積)は掛け算、OR(論理和)は足し算
True は 1(0以外)
False は 0
として、簡単な数式に直して考えてみましょう。
もちろん、演算の順序も掛け算(AND)が先です。
T and T and F and T and T and T
= 1 × 1 × 0 × 1 × 1 × 1 = 0 (False)
F or F or F or T or F or F
= 0 + 0 + 0 + 1 + 0 + 0 = 1 (True)
51
51.
Copyright 2016-2018 G1SystemsInc.
因数分解、展開も数式と同じ
(a × b) + (a × c)
= a × (b + c)
(a AND b) OR (a AND c) 男性で日本人、または、男性でアメリカ人
= a AND (b OR c) 男性で、日本人、または、アメリカ人
これらは、SQLに限らずどんな言語でも必要なので、息をす
るのと同じレベルでできるようになってください。
52
Copyright 2016-2018 G1SystemsInc.
実は、動的生成は不要です。
SELECT *
FROM table_a
WHERE
(col1 >= @開始値 OR @開始値 IS NULL)
AND (col1 <= @終了値 OR @終了値 IS NULL);
57
※ 当然、FROM句、HAVING句でも使えます!
57.
Copyright 2016-2018 G1SystemsInc.
パースされると消える!
58
(col1 >= @開始値 OR @開始値 IS NULL)
@開始値がNULL @開始値が1000
(col1 >= NULL OR NULL IS NULL) (col1 >= 1000 OR 1000 IS NULL)
(col1 >= NULL OR True) (col1 >= 1000 OR False)
意味がないので消える (col1 >= 1000)だけが残る
58.
Copyright 2016-2018 G1SystemsInc.
SQLServerは、RECOMPILE指定する
SELECT *
FROM テーブル
WHERE
(col1 = @検索値1 OR @検索値1 IS NULL)
AND (col2 = @検索値2 OR @検索値2 IS NULL)
OPTION(RECOMPILE);
とすれば、毎回、パースからやり直してくれる。
59
59.
Copyright 2016-2018 G1SystemsInc.
更には……(任意の OR条件を追加)
60
画面
開始値
終了値
1
10
または
区分 A
SELECT * FROM table_a
WHERE
((col1 >= @開始値 OR @開始値 IS NULL)
AND (col1 <= @終了値 OR @終了値 IS NULL))
OR (col2 = @区分 AND @区分 IS NOT NULL)
※ 当然、FROM句、HAVING句でも使えます!
60.
Copyright 2016-2018 G1SystemsInc.
更には……
61
画面
検索値
比較演算子
1
<=
SELECT * FROM table_a
WHERE
(col1 = 検索値 OR @比較演算子 <> "=")
AND (col1 >= 検索値 OR @比較演算子 <> ">=")
AND (col1 <= 検索値 OR @比較演算子 <> "<=")
AND (col1 > 検索値 OR @比較演算子 <> ">")
AND (col1 < 検索値 OR @比較演算子 <> "<")
※ 当然、FROM句、HAVING句でも使えます!
▼
61.
Copyright 2016-2018 G1SystemsInc.
LEFT JOIN を切り替えるなら
62
画面
過去特待生だった
らそれも表示
@区分 が 1 なら削除フラグが立っていても出力し、
以外ならしない。
SELECT
a.氏名, b.ランク
FROM 生徒マスタ a
LEFT OUTER JOIN 特待生マスタ b
ON a.ID = b.生徒ID
AND (0 = b.削除フラグ OR @区分 = 1);
する
しない