ITエンジニアの成長ブログ

ITエンジニアとして行う勉強の発信&日々の生活で体験した楽しいことをゆるく発信

RESTORE のREPLACE句について

SQL Serverでバックアップを復元するときに、意味をしっかりと理解せずに、とりあえずREPLACE句を指定していました。

ややこしいのが、REPLACE句を指定しなくてもエラーにならないこともあったり、反対にREPLACE句を指定しないとエラーになったりと、挙動がよく分からなかったので、自分の整理のためにREPLACE句の使い方やその意味を調べました。

まずは、公式サイトを確認します。
RESTORE の引数 (Transact-SQL) - SQL Server | Microsoft Learn

REPLACE句の説明

REPLACE句は、単語の通りの意味で既に存在するデータベースを上書きするときに使うのが基本。

REPLACE句を指定しない場合は、以下の2つの条件に当てはまるときにエラーになり復元されないと記載があります。

  • RESTORE ステートメントで指定したデータベースが、現在のサーバー上にすでに存在する
  • データベース名が、バックアップセットに記載されているデータベース名と異なる

つまり、上記の2つの条件のどちらかが当てはまらない場合はエラーにならないということですね。例えば、RESTOREステートメントで指定したデータベースが存在しない場合や、データベース名がバックアップセット名と同じ場合はエラーにならないと読み取れます。

上記の2つめの条件が具体的にどこの名前を比較しているのか分からなかったので、追加で調べました。すると、以下にその条件について記載がありました。
RESTORE (Transact-SQL) - SQL Server | Microsoft Learn

上記URLの一部分を引用します。

RESTORE ステートメントで指定したデータベースが現在のサーバーに既に存在し、指定したデータベースのファミリ GUID がバックアップ セットに記録されているデータベースのファミリ GUID と異なる場合、そのデータベースは復元されません。

2つめの条件は、具体的には「指定したデータベースのファミリ GUID がバックアップ セットに記録されているデータベースのファミリ GUID と異なる場合」を指していると思われます。

上記の仮説を確認するために、自分のローカル環境で2つのインスタンスで名前が同じデータベースをそれぞれ作成し、片方のデータベースのバックアップファイルを、もう一つのデータベースへ復元しようとした場合に以下のエラーが発生しました。

復元時のエラー

データベース名は同じですが、ファミリGUIDは異なるというケースになるので、先ほど引用した部分が最初に説明したものより具体的で正しい条件のようです。

因みに、ファミリGUIDとはデータベースのGUIDの一つで復元されても変わらない値のようです。

バックアップセットのファミリGUIDは以下のコマンドで確認できます。
RESTORE HEADERONLY (Transact-SQL) - SQL Server | Microsoft Learn

例えば、以下のように実行できます。

RESTORE HEADERONLY FROM DISK = 'C:\Program Files\Microsoft SQL Server\MSSQL15.MSSQLSERVER\MSSQL\Backup\sample.bak';

以下のカラムがファミリGUIDとなります。

RESTORE HEADERONLYのファミリGUID

データベース自体のファミリGUIDは以下で確認できます。
sys.database_recovery_status (Transact-SQL) - SQL Server | Microsoft Learn

例えば、以下のように実行できます。

SELECT
  d.name,
  drs.family_guid
FROM sys.database_recovery_status drs
INNER JOIN sys.databases d
  ON drs.database_id = d.database_id;

以下のカラムがファミリGUIDとなります。

sys.database_recovery_statusのファミリGUID

まとめると、以下の2つの条件に当てはまる場合はREPLACE句を指定しないとエラーになります。どちらかでも条件に当てはまらない場合はエラーになりません。

  • RESTORE ステートメントで指定したデータベースが、現在のサーバー上にすでに存在する
  • 指定したデータベースのファミリ GUID がバックアップ セットに記録されているデータベースのファミリ GUID と異なる場合

しかし、例外もあり上記のどちらかの条件に当てはまらない場合でもREPLACE句を指定しないとエラーになる場合もあります。それは、完全復旧モデルまたは一括ログ復旧モデルを使用している場合は、ログ末尾のバックアップを行っておらず、STOPAT オプションを使用しない場合にはエラーになります。

とりあえず、以上です。つらつらと書いてきましたがREPLACE句は意図しない上書きをしてしまう可能性があるので頻繁に使うべきではないと公式サイトで言及されています。そのため、以前の私のように理解せずにとりあえずREPLACE句を指定はよくないので、ある程度その意味を理解して指定するべきかなと思います。

今回はこのへんで。最後までお読みいただきありがとうございました。