とりあえず半歩

学んだことを1日1個、簡単なことでも良いから記録していきたい。

与えられたレコードと一致するレコードがあるか(力技で)確認する

課題

DBのあるテーブルと構成の同じレコードが与えられた時、そのレコードの要素全てが一致するレコードがテーブル内に存在するか確認するクエリを書く。

例えばCSVを1行ずつ読み込んでテーブルに既に存在するか確認し、次のような分岐処理をしなさいという課題。

  1. 存在していなければ挿入
  2. 全くの同一内容のレコードが存在するなら何もしない
  3. どこか1つでも要素の値が異なれば更新する

実行

実行環境

使用データ

name minPlayer maxPlayer
catan 2 4
GH13 1 5
indigo 2 4
ra NULL 5

これは、今回用に拵えたテーブル。主キーはname列。

以前インストールしたサンプルデータベースはちょっと見たら列数が多めだったので今回はパス。

そんでもって、与えられたレコードはテーブルの4行目のレコードと全く同じものだとする。つまり、

ra  NULL  5

ってこと。

これを簡単のためにローカル変数を使って次のように定義&セットしておく。

DECLARE @name varchar(50);
DECLARE @minPlayer int;
DECLARE @maxPlayer int;

SET @name = 'ra';
SET @minPlayer = NULL;
SET @maxPlayer = 5;

作業

最初に書いてみたコードは以下。

SELECT
  CASE
    WHEN [minPlayer] = @minPlayer AND [maxPlayer] = @maxPlayer THEN '一致'
    ELSE '不一致'
  END AS [STATUS]
FROM
  [boardgame]
WHERE
  [name] = @name;

これで得られた結果は不一致。なんでーなんでー…と少し悩んだのだけれど、原因はWHEN句の[minPlayer]の比較がNULL = NULLってなってしまうことにあった。NULLかどうかを調べるにはIS NULLを使うのだった。簡単な間違い。。。

書き直したコードが以下。

SELECT
  CASE
    WHEN ( [minPlayer] = @minPlayer OR ( [minPlayer] IS NULL AND @minPlayer IS NULL ) )
      AND ( [maxPlayer] = @maxPlayer OR ( [maxPlayer] IS NULL AND @maxPlayer IS NULL ) ) THEN '一致'
    ELSE '不一致'
  END AS [STATUS]
FROM
  [boardgame]
WHERE
  [name] = @name;

無事一致という結果が得られた。

評価

これを使えば、与えられたレコードが存在していない場合は主キーが一致するレコードがテーブル内にないから抽出件数が0件になってわかるし、主キーが一致するレコードがあるならあるで全要素が一致するのか、部分的に異なっているのかわかる。

ただ、テーブルを構成する全ての列について比較するというタイトルにもあるとおりの力技であるので、大きなテーブルだと手打ちは辛いと思う。

そうなった際は、なぜ全く同一内容のレコードが複数回登録されようとしているのかから確認・考えなおしてみるのも有効だよなと本日思った次第。