1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【VB.NET】動的SQLで「なぜ別テーブルの値が更新される?」を読み解く

1
Posted at

1. はじめに

レガシーシステムのバッチプログラムを解析していると、VB.NETなどでSQLを文字列として組み立てているコードに出会うことがあります。

特に分かりづらいのが、次のようなバインド変数を使ったUPDATE文です。

.AppendLine("UPDATE T_MEMBER SET ") .AppendLine(" LAST_UPDATE_YMD = CASE ") .AppendLine(" WHEN NVL(LAST_UPDATE_YMD, '1999/01/01') > TO_DATE(" & pLastUpdateYmd & ") ") .AppendLine(" THEN LAST_UPDATE_YMD ") .AppendLine(" ELSE " & pLastUpdateYmd & " END ")

さらに、そのバインド変数に対して、別のテーブルから取得した値を設定しているとします。

DBAccess.CreateParameter( cmd, pLastUpdateYmd, dr("UPDATE_YMD"), DbType.Date )

ここで、

pLastUpdateYmd という名前なのだから、T_MEMBER.LAST_UPDATE_YMD の値が入っているのでは?

と考えてしまうことがあります。

しかし、実際にはそうとは限りません。

このようなコードを正しく理解するには、

「バインド変数の名前」ではなく、「そのバインド変数に実際に何の値を設定しているか」

を見る必要があります。

本記事では、一般的なサンプルを使って、

  • バインド変数とは何か
  • 変数名とデータソースの関係
  • 別テーブルの値がUPDATE先に入る仕組み
  • 複雑な動的SQLを読むときのポイント

を整理します。

対象読者

  • VB.NETなどの既存システムを解析している方
  • レガシーシステムの保守・改修を担当している方
  • 動的SQLを読むのが苦手な方
  • バインド変数の仕組みを理解したい方
  • 「この値はどこから来ているの?」というデータの流れを追いたい方
  • Javaなど別言語へのシステム移行で、既存コードの処理を読み解く必要がある方

動的SQLで「なぜ別テーブルの値が更新される?」を読み解く

1. はじめに

レガシーシステムを解析していると、VB.NETなどでSQLを文字列として組み立てているコードに出会うことがあります。

例えば、次のようなUPDATE文です。

.AppendLine("UPDATE T_MEMBER SET ")
.AppendLine(" LAST_UPDATE_YMD = CASE ")
.AppendLine(" WHEN NVL(LAST_UPDATE_YMD, '1999/01/01') > TO_DATE(" & pLastUpdateYmd & ") ")
.AppendLine(" THEN LAST_UPDATE_YMD ")
.AppendLine(" ELSE " & pLastUpdateYmd & " END ")

さらに、pLastUpdateYmd に対して、別のテーブルから取得した値を設定しているとします。

DBAccess.CreateParameter(
    cmd,
    pLastUpdateYmd,
    dr("UPDATE_YMD"),
    DbType.Date
)

ここで、

UPDATEしているのは T_MEMBER なのに、なぜ別テーブルの UPDATE_YMD が入るの?

と疑問に思うことがあります。

実は、このようなコードを読むときは、変数名と実際の値を切り分けて考えることが重要です。


2. 対象読者

この記事は、以下のような方を対象としています。

  • VB.NETなどの既存システムを解析している方
  • レガシーシステムの保守・改修を担当している方
  • 動的SQLが分かりづらいと感じている方
  • バインド変数の仕組みを理解したい方
  • 「この値はどこから来たの?」とデータの流れを追う必要がある方

3. 変数名と「実際の値」は別物

例えば、次のような定義があったとします。

Dim pLastUpdateYmd As String = ":LAST_UPDATE_YMD"

このコードを見ると、

pLastUpdateYmd
    ↓
LAST_UPDATE_YMDの日付

と思ってしまうかもしれません。

しかし、実際に入っているのは日付ではなく、

":LAST_UPDATE_YMD"

というバインド変数の名前です。

バインド変数は、SQLの中で後から値を受け取るための「受け皿」のようなものです。

例えばSQLが、

UPDATE T_MEMBER
SET LAST_UPDATE_YMD = :LAST_UPDATE_YMD
WHERE MEMBER_ID = :MEMBER_ID

だった場合、

:LAST_UPDATE_YMD

は「ここに値を渡してください」という目印に過ぎません。


4. 実際に何の値が入るのか?

では、pLastUpdateYmd に実際の値を設定している処理を見てみます。

DBAccess.CreateParameter(
    cmd,
    pLastUpdateYmd,
    dr("UPDATE_YMD"),
    DbType.Date
)

ここで重要なのは、

dr("UPDATE_YMD")

です。

例えば、前段のSELECTで別テーブルから、

SELECT UPDATE_YMD
FROM T_UPDATE_HISTORY
WHERE MEMBER_ID = 1001

というデータを取得していたとします。

取得した値が、

2026/09/01

なら、

T_UPDATE_HISTORY.UPDATE_YMD
        ↓
dr("UPDATE_YMD")
        ↓
:LAST_UPDATE_YMD
        ↓
T_MEMBER.LAST_UPDATE_YMD

という流れになります。

つまり、

更新先と値の取得元は、同じテーブルである必要はありません。

今回の場合、

【値の取得元】
T_UPDATE_HISTORY.UPDATE_YMD

        ↓

【バインド変数】
:LAST_UPDATE_YMD

        ↓

【更新先】
T_MEMBER.LAST_UPDATE_YMD

となっています。

バインド変数の名前が LAST_UPDATE_YMD だからといって、そこに T_MEMBER.LAST_UPDATE_YMD の値が入っているとは限りません。

実際に何の値が入るかは、パラメータ設定処理を見る必要があります。


5. CASE文では何をしているのか?

今回のSQLでは、単純に値を上書きしているわけではありません。

CASE
    WHEN NVL(LAST_UPDATE_YMD, DATE '1999-01-01')
         > :LAST_UPDATE_YMD
    THEN LAST_UPDATE_YMD
    ELSE :LAST_UPDATE_YMD
END

これは、

現在登録されている日付と、新しく取得した日付を比較して、新しいほうを残す

という処理です。

例えば、

現在の値
2026/08/20

新しく取得した値
2026/09/01

なら、

2026/08/20 > 2026/09/01

は偽なので、

ELSE :LAST_UPDATE_YMD

が選ばれ、

2026/09/01

に更新されます。

逆に、

現在の値
2026/09/10

新しく取得した値
2026/09/01

なら、現在の値のほうが新しいため、

THEN LAST_UPDATE_YMD

が選ばれます。

そのため、古い値で新しい値を上書きすることを防げます。


6. 動的SQLを読むときのポイント

このようなコードを解析するときは、変数名だけを追わないことがポイントです。

次の順番で確認すると、データの流れを追いやすくなります。

① UPDATE先を確認
        ↓
② 更新対象のカラムを確認
        ↓
③ SQL内のバインド変数を確認
        ↓
④ そのバインド変数に何を設定しているか確認
        ↓
⑤ その値がどこから取得されたものか確認

特に重要なのは、

CreateParameter(
    cmd,
    pLastUpdateYmd,
    dr("UPDATE_YMD"),
    DbType.Date
)

のようなパラメータ設定部分です。

pLastUpdateYmd という名前を見るだけではなく、

「この変数に、実際には何の値を渡しているのか?」

を見ることで、データの出どころが分かります。


7. まとめ

動的SQLを解析するときに覚えておきたいのは、

「バインド変数の名前」と「そこに設定される値」は別物

ということです。

例えば、

Dim pLastUpdateYmd As String = ":LAST_UPDATE_YMD"

とあっても、これは日付データではなく、SQLで使用するバインド変数名です。

実際の値は、

DBAccess.CreateParameter(
    cmd,
    pLastUpdateYmd,
    dr("UPDATE_YMD"),
    DbType.Date
)

のような処理によって決まります。

そのため、

「なぜこのテーブルの値が、別のテーブルのカラムに入るのか?」

と疑問に思ったときは、変数名ではなく、

「値がどこから来て、どのバインド変数を経由して、どこへ入るのか」

というデータの流れを追ってみましょう。

レガシーシステムの解析では、変数名だけでは分からない「値のリレー」を見つけることが、処理を理解する大きな手がかりになります。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?