有馬総一郎のブログ

(彼氏の事情)

2020年11月22日 01:32:51 JST - 2 minute read - SQL

SQL SEQUENCEを既に登録済みのテーブルの主キーの最大値まで廻す

タイトルどおり、何かの原因でSEQUENCEの現在値と、それを主キーとするテーブルのレコードが不一致な状態になったとき、一致するまでSEQUENCEを廻す。

まずは直感的に思いつきやすいOracleから…ここではシーケンスの現在値を変数 :SEQ_CURRENT_VAL で保持しているものとする。

Oracle

select
  SEQ_ID.nextval
from
  dual
connect by
  level <= (
    select
      max(ID)
    from
      TABLE
  ) - :SEQ_CURRENT_VAL;

へっぽこプログラマーには理解しにくいconnect byだがここでは、単純にlevel=行数となるので、最大値-シーケンスの現在値=値の差廻す。

SQL Server

connect byが使えないので、WITH句共通テーブル式(CTE)を使う。ここでは、トランザクションデータの主キーの最大値を変数 @MAX_ID で保持しているものとする。

with CTE as (
  select
    convert(int, current_value) ID
  from
    sys.sequences
  where
    name = 'SEQ_ID'
  union all
  select
    ID + 1
  from
    CTE
  where
    ID < @MAX_ID
)
select
  next value for SEQ_ID
from
  CTE
where
  ID < @MAX_ID

トランザクションデータの主キーの最大値と、SEQUENCEの現在値が一致していたとしても、CTEの上のテーブルの結果が返ってしまうで、SEQUENCEを廻すSELECT句でも、トランザクションデータの主キーの最大値と比較する必要がある。また

色々駄目な失敗例

with CTE as (
  select
    NEXT VALUE FOR SEQ_ID ID
  union all
  select
    NEXT VALUE FOR SEQ_ID
  from
    CTE
  where
    ID <= @MAX_ID
)
select
*
from
CTE

みたいなのは、

メッセージ 11721、レベル 15、状態 1、行 3
NEXT VALUE FOR 関数は、DISTINCT、UNION、UNION ALL、EXCEPT、INTERSECT のいずれかの演算子を使用するステートメントで直接使用することはできません。

となってエラーになるし、そもそも、SEQUENCEも無駄に廻ってしまう。