without “WHERE EXISTS” | 踏まれたり蹴られたり...._| ̄|○

踏まれたり蹴られたり...._| ̄|○

「踏んだり蹴ったり」って....あーた、被害者なら「踏まれたり蹴られたり」ってのが正しいんぢゃない??日本語ってばむつかしい...._| ̄|○  (BTW, ついでに、人も指せない「足の親指のトナリの指」を何て呼べばしっくりくるのか、教えてヱライひとっ!!)

SQL に関する個人メモっす。 ワケわかんないかもなので、スルーでよろ。
(このブログ、システム系のキーワード検索に引っかかって来るヒトもいちゃったりするんだな。 あらびっくり。)



テストで見つかったバグがフィックスされて 「データ補正」 を実行することになったんだが、補正用にと貰った SQL を
実行したら、思わぬ結果になって呆然 ―― バックアップは取らないまでも、AutoCommit は当然オフにしてたから
Rollback して非常事態回避。

補正対象のデータ、51 件。
タイムスタンプでの検索でカウントした補正済みデータ、確かに 51 件。
今一度、補正対象のデータをカウント ―― 当然 「0」 件を期待.... してたのに、結果は 「4494」 件。

どゆこと!!?? (滝汗)

冷や汗かきながら、フィールド値を確認....
ミゴトなまでに延々と NULL で埋められて並んでるデータ見た瞬間、一瞬とはいえ本気で凍ったね。 (苦笑)

データ補正どころか完璧に
データ消失...._| ̄|○



アタシが作った SQL なら OK なのに、貰った SQL だと NG。
補正対象外の全データ (全部だぞ、全部。) の全更新対象フィールドが NULL で書き換えられちゃう。

“WHERE EXISTS”条件がないまま更新を実行すると
 更新対象外のデータの全該当フィールドがNULLで更新されることがあります。


ま、そんなおハナシ。 じゃ、行ってみよっか。

/*┌──────────────────────────────────────────┐*/
/*│ IDナンバーで紐付けられるテーブルaとテーブルbにおいて、            │*/
/*│ テーブルaの氏名フィールドに値がなく、テーブルbの同フィールドに値があった場合に │*/
/*│ テーブルbの値でテーブルaの同フィールドを更新する。               │*/
/*└──────────────────────────────────────────┘*/

/*【1-1. 更新対象データ確認】*/
 SELECT b.氏名, a.氏名, a.IDナンバー, a.更新日時
  FROM テーブルa a, テーブルb b
  WHERE a.IDナンバー = b.IDナンバー
   AND (b.氏名 IS NOT NULL AND b.氏名 <> '')
   AND (a.氏名 IS NULL OR a.氏名 = '')
 ORDER BY IDナンバー, 更新日時
 ;
/*【1-2. 更新対象データ数カウント】*/
 SELECT count(*)
  FROM テーブルa a, テーブルb b
  WHERE a.IDナンバー = b.IDナンバー
   AND (b.氏名 IS NOT NULL AND b.氏名 <> '')
   AND (a.氏名 IS NULL OR a.氏名 = '')
 ;

/* ------------------------------------------------------------------------- */

/*【2. 更新実行】 */
 UPDATE テーブルa a SET
 (氏名, 更新日時) = (
  SELECT 氏名, (SELECT current timestamp FROM sysibm.sysdummy1)
   FROM テーブルb b
   WHERE a.IDナンバー = b.IDナンバー
    AND (b.氏名 IS NOT NULL AND b.氏名 <> '')
    AND (a.氏名 IS NULL OR a.氏名 = '')
 )
/*┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓*/
/*┃ 更新対象を下記の条件で限定しないと、対象外の全データの更新フィールドが NULL で更新され┃*/
/*┃【3-1. 実行結果確認-1】の結果は、実行前にカウントした「更新対象データ数」と一致するが ┃*/
/*┃【3-2. 実行結果確認-2】での結果が「0件ヒット」とならないことがあるため、注意が必要。 ┃*/
/*┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛*/
 WHERE EXISTS (
  SELECT *
   FROM テーブルb b
   WHERE a.IDナンバー = b.IDナンバー
    AND (b.氏名 IS NOT NULL AND b.氏名 <> '')
    AND (a.氏名 IS NULL OR a.氏名 = '')
 )
/*┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓*/
/*┃ 更新対象を上記の条件で限定しないと、対象外の全データの更新フィールドが NULL で更新され┃*/
/*┃【3-1. 実行結果確認-1】の結果は、実行前にカウントした「更新対象データ数」と一致するが ┃*/
/*┃【3-2. 実行結果確認-2】での結果が「0件ヒット」とならないことがあるため、注意が必要。 ┃*/
/*┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛*/
 ;

/* ------------------------------------------------------------------------- */

/*【3-1. 実行結果確認-1】
 ⇒⇒⇒⇒ 更新されたデータ数をカウント、「1-2」での対象データ数と一致しているか確認 */
 SELECT count(*)
  FROM テーブルa a
  WHERE 更新日時 = (SELECT max(更新日時) FROM テーブルa)
;
/*【3-2. 実行結果確認-2(「1-2」と同一SQL)】
 ⇒⇒⇒⇒ 「0件ヒット」を確認 */
 SELECT count(*)
  FROM テーブルa a, テーブルb b
  WHERE a.IDナンバー = b.IDナンバー
   AND (b.氏名 IS NOT NULL AND b.氏名 <> '')
   AND (a.氏名 IS NULL OR a.氏名 = '')
 ;
/*【3-3. 実行結果確認-3】
 ⇒⇒⇒⇒ 更新されたデータを上方にリストアップし、紐付けられた全データを確認 */
 SELECT b.氏名, a.氏名, a.IDナンバー,a. 更新日時
  FROM テーブルa a, テーブルb b
  WHERE a.IDナンバー = b.IDナンバー
 ORDER BY 更新日時 DESC, IDナンバー
 ;

/* ------------------------------------------------------------------------- */

/*【Commit OR Rollback】*/
/* COMMIT; */
/* ROLLBACK; */


要は、UPDATE に対する WHERE 句がないというのが問題なんだぞ、と。
「時に」 対象外の全データの対象フィールド値が全部 NULL になっちゃうことがあるんだぞ、と。

(自分で書く時には “WHERE EXISTS” を書いてたから、最初は本気で理由がわからんかったよ...._| ̄|○)

いぢょおっっ!!!!


 ⇒ http://www.atmarkit.co.jp/bbs/phpBB/viewtopic.php?fORum=26&topic=18379