Visual Studio 2022(SSDT)のSQL Serverデータベース プロジェクトで、ストアドを CREATE OR ALTER にすると SQL70001 でビルドが止まることがあります。TFS 管理(TFVC)や Azure Repos(Git)の設定を疑いがちですが、原因は別です。この記事では、仕組みを踏まえて最短で解決する書き方と、どうしても必要な場合の回避策を整理します。
起きている現象:CREATE OR ALTER で SQL70001 が出る
VS2022 の「SQL Server データベース プロジェクト」(いわゆる SSDT の Database Project)でストアドプロシージャの定義ファイル(.sql)に次のように書くと、ビルド時にエラーが出るケースがあります。
CREATE OR ALTER PROCEDURE dbo.MyProc
AS
BEGIN
-- 処理
END
代表的なメッセージは次の通りです。
SQL70001: This statement is not recognized in this context.
一見すると「SQL Server 自体は CREATE OR ALTER を理解できるのに、なぜ Visual Studio ではダメなのか?」という違和感が出ます。ここを理解すると、回避策ではなく “正攻法の直し方” が見えてきます。
結論:原因は TFS ではなく「データベース プロジェクトの仕様」
まず結論から言うと、問題の本質は TFS / TFVC や Azure Repos(Git)の設定ではありません。ソース管理は「ファイルを履歴付きで保存する仕組み」であり、SQL の構文解釈やビルド可否を決めるのは、Visual Studio 側のデータベース プロジェクト(SSDT / DacFx)のルールです。
そしてデータベース プロジェクトでは、ストアドプロシージャ・関数・ビューなどの “オブジェクト定義” として認識させる SQL は、原則として CREATE 文で始まる定義 を前提にしています。CREATE OR ALTER はこの前提から外れるため、ビルド時に SQL70001 になりやすい、という整理になります。
重要なのは次の2点です。
- データベース プロジェクトの
.sqlは「そのまま SQL Server に流し込む実行スクリプト」ではなく、「スキーマモデル(DACPAC)を作るための定義ファイル」 - スキーマモデルを作る都合上、定義の入口は
CREATEとして固定されている(=OR ALTERの “実行時判断” を持ち込めない)
仕組みを押さえる:データベース プロジェクトは “コンパイル” してデプロイする
データベース プロジェクトは、C# のプロジェクトに近い発想で動きます。SQL ファイル群を解析して、依存関係を解決し、最終的に DACPAC(データベースの設計図) を生成します。Publish(発行)やパイプラインでのデプロイ時には、この設計図と実際のDBを比較して差分を取り、必要な CREATE / ALTER / DROP などのスクリプトを “ツール側が自動生成” します。
このため、オブジェクト定義ファイルの中で CREATE OR ALTER のような「存在状態に応じて分岐する書き方」をする必要はありません。むしろツール側が “差分” を見て適切に判断するため、定義はシンプルに保つほど安定します。
| 観点 | 通常のSQLスクリプト(手動実行) | データベース プロジェクト(SSDT) |
|---|---|---|
| 目的 | SQL Server に順番に命令を実行させる | DBスキーマのモデルを作り、差分デプロイする |
| ファイルの意味 | 実行手順そのもの | オブジェクトの最終形(定義) |
| 存在判定 | 自分で IF EXISTS / CREATE OR ALTER を書きがち | ツール(DacFx)が比較して自動判断 |
| 依存関係 | 実行順を自分で管理 | 参照関係を解析してビルド段階で検証 |
つまり、データベース プロジェクトの “正しい書き方” は、実行スクリプトの流儀とは違います。ここを混ぜると、今回のように構文エラーやモデル不整合が起きやすくなります。
最短の解決策:OR ALTER をやめて CREATE のみにする
実務的に一番安定して、かつデータベース プロジェクトのメリット(差分デプロイ、依存関係チェック、スキーマ比較)を最大化できるのは、ストアドの定義を CREATE のみに統一する ことです。
テンプレートとしては、次のように SET オプションと GO 区切りを含めた形が分かりやすいです。
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE dbo.MyProc
AS
BEGIN
SET NOCOUNT ON;
-- ここに処理を書く
END
GO
ポイントは次の通りです。
- オブジェクト定義の本体は
CREATEで開始する SET ANSI_NULLSやSET QUOTED_IDENTIFIERは、SSMS が生成する定番の書式なので、チーム内で統一すると差分が読みやすいGOは SQL Server の文法ではなくツール側のバッチ区切り。データベース プロジェクトでも基本的に入れておくと安全
「でも既にDBに存在していたら ALTER が必要では?」という疑問
ここが一番引っかかる部分ですが、データベース プロジェクトの Publish は次の流れで動きます。
- プロジェクト(DACPAC)を “理想形” として用意する
- 実DBのスキーマを読み取り、差分を計算する
- 差分に応じて
CREATE/ALTER/DROPを含むデプロイスクリプトを生成する - 生成されたスクリプトを実行する(設定によりプレビュー、保存も可能)
つまり、定義ファイルに ALTER を書くのは “SSDT の仕事を奪う” 行為になりがちです。定義はあくまで最終形に寄せ、差分適用はツールに任せるのが王道です。
TFS(TFVC)/ Azure Repos(Git)では解決しない理由
「チェックイン方法を変えれば通るのでは?」という発想が出るのは自然ですが、今回の問題はそこでは起きていません。TFVC でも Git でも、リポジトリが扱うのは テキストファイルの差分 です。
- TFVC: ソースのバージョン管理(チェックイン/チェックアウト、棚上げなど)
- Azure Repos(Git): ブランチ運用、PR、履歴管理
どちらも「SQL の構文をどう解析するか」「ビルドでどの文法を許可するか」を変える機能は持ちません。プロジェクトのビルドが失敗するかどうかは、Visual Studio(SSDT / DacFx)が決めます。そのため、リポジトリ種別を変えても CREATE OR ALTER が突然ビルド可能になることは基本的にありません。
どうしても CREATE OR ALTER を使いたい場合の現実的な代替案
とはいえ、運用や移行の都合で「何度実行しても壊れない(冪等な)スクリプト」が欲しくて、CREATE OR ALTER を使いたい場面はあります。データベース プロジェクト中心の運用を崩さずにやるなら、次の “置き場所を変える” 方向が現実的です。
Pre-Deployment / Post-Deployment スクリプトとして管理する
データベース プロジェクトには、Publish の前後で必ず実行されるスクリプトを持てる仕組みがあります。
- Pre-Deployment: 生成された差分スクリプトの前に実行
- Post-Deployment: 生成された差分スクリプトの後に実行
これらは “オブジェクト定義のモデル” とは別枠のため、モデルの制約を受けにくく、運用スクリプトを置く場所として使われます。例えば、どうしても冪等にしたいユーティリティ系のストアドだけを Post-Deployment に寄せる、といったやり方です。
-- Post-Deployment の例(運用都合で冪等にしたい場合)
CREATE OR ALTER PROCEDURE dbo.Util_RebuildIndex
AS
BEGIN
SET NOCOUNT ON;
-- インデックス再構築など
END
GO
注意点として、Pre/Post は “実行されるだけ” であり、スキーマモデルには取り込まれません。つまり、スキーマ比較や依存関係チェックの対象外になりやすく、いつの間にか本体定義とズレる危険があります。
ビルド対象外の「単なる .sql」として管理する
もう一つは、同じリポジトリで管理しつつも、データベース プロジェクトの “ビルド対象” から外した SQL として置く方法です。例えば次のように役割を分けます。
| 置き場所 | 例 | ビルド対象 | 向いている用途 |
|---|---|---|---|
| プロジェクト配下(オブジェクト定義) | Stored Procedures/dbo.MyProc.sql | 対象 | スキーマの正本(基本はここ) |
| プロジェクト配下(Pre/Post) | Scripts/Post-Deployment.sql | 対象(ただしモデル外) | データ移行、初期データ、運用補助 |
| 別フォルダ(運用スクリプト集) | Ops/adhoc_create_or_alter.sql | 対象外 | 手動実行、障害対応、検証用 |
この方法なら、CREATE OR ALTER を含むスクリプトをリポジトリで管理しつつ、データベース プロジェクトのビルドを壊さずに済みます。ただし、プロジェクトの強み(依存関係や差分管理)からは外れるため、“本番の正本” はあくまでプロジェクト側に置く という線引きが重要です。
代替案を選ぶときの比較表
| 選択肢 | メリット | デメリット | おすすめ度 |
|---|---|---|---|
オブジェクト定義を CREATE に統一 | ビルド安定、差分デプロイの恩恵が最大、レビューしやすい | 冪等スクリプトとしての見た目は弱い | 最優先 |
Pre/Post に CREATE OR ALTER | Publish の流れに組み込める、冪等にしやすい | モデル外なのでズレやすい、依存関係チェックが弱い | 限定的に |
| ビルド対象外の運用スクリプトとして管理 | 自由度が高い、緊急対応の道具箱になる | プロジェクトの検証から外れる、実行管理が必要 | 補助的に |
現場で事故が少ないのは、「オブジェクト定義は CREATE、運用スクリプトは別枠」 という分離です。これだけで、SQL の自由度と、SSDT の品質担保の両方を取りやすくなります。
チーム開発でつまずきにくくする運用のコツ(TFVC/Git共通)
今回のエラーは “たまたま CREATE OR ALTER を書いた” ことが直接の原因ですが、根っこには「スクリプトの役割が混ざっている」という構造があります。再発防止の観点で、次の運用をおすすめします。
プロジェクト内のSQLは「最終形」だけを書く
- ストアド、関数、ビュー、トリガーは 基本的に
CREATEのみ IF EXISTSで分岐する “実行ロジック” は、原則としてオブジェクト定義に持ち込まない- データ移行(列の分割、値の補正など)は Pre/Post に寄せ、スキーマの定義とは切り分ける
Publish の挙動を「見える化」して安心して任せる
SSDT の Publish は、設定次第で “どんな SQL を流すか” を事前に確認できます。チームで不安が残るときは、次をルール化すると安心です。
- Publish 前に生成スクリプトを保存し、PR レビューの材料にする
- データ損失が起こりうる操作はブロックする設定(例:可能性がある場合は停止)を使う
- 本番は手動Publishより、パイプライン(Azure DevOps等)で “同じ手順” を再現する
チェックイン前の簡易チェックリスト
| チェック項目 | 見る場所 | 意図 |
|---|---|---|
オブジェクト定義が CREATE で始まっている | 各 .sql ファイル | モデル化できる形に統一 |
| Pre/Post に “本体定義” を置きすぎていない | Pre/Post の内容 | モデルと実体のズレ防止 |
| 依存関係のある変更が同一コミットに入っている | 差分全体 | ビルド・Publishの失敗を減らす |
| ローカルでビルドが通る | VS のビルド結果 | CI を赤くしない |
FAQ:よくある勘違い
SQL Server で実行すると通るのに、なぜプロジェクトでは落ちる?
データベース プロジェクトは “実行エンジン” ではなく “モデル化ツール” です。SQL Server が理解できるかどうかと、プロジェクトがモデルとして取り込めるかどうかは別の話になります。特にストアドや関数のようなオブジェクト定義は、モデルの入口として CREATE が前提になっているため、ここから外れるとエラーになりやすい、という整理です。
ターゲット プラットフォームを新しいSQL Serverにしたら解決する?
ターゲット プラットフォームは “どの機能を許可するか” の影響を持つ一方で、オブジェクト定義の入口(CREATE 前提)という仕様は別問題です。まずは OR ALTER を外してビルドが通ることを確認し、その上でターゲットの設定は “別の互換性問題が出たとき” に見直すのが安全です。
既存環境へのデプロイで、ストアドの更新はどうなる?
Publish(または sqlpackage 等のデプロイ)が差分を計算して、必要なら ALTER PROCEDURE を生成してくれます。定義ファイル側は CREATE のままで問題ありません。逆に、定義側に “手動でALTERを混ぜる” と、ツールが想定する差分計算と競合して予期せぬ結果になりやすくなります。
どうしても冪等にしたい。プロジェクト中心運用は諦めるべき?
諦める必要はありません。冪等性が必要なのは主に “運用スクリプト” や “データ移行” の領域です。オブジェクト定義(スキーマの正本)と、運用・移行(実行手順)を分離すれば、プロジェクト中心でも十分に回せます。冪等にしたいスクリプトは Pre/Post や別フォルダに寄せ、正本は CREATE に統一するのがバランスの良い解です。
まとめ:CREATE OR ALTER を“書かない”のが最短で強い
VS2022+TFS 管理(TFVC)/ Azure Repos を使った開発で CREATE OR ALTER が原因の SQL70001 に遭遇したら、疑うべきはソース管理ではなく、データベース プロジェクトの前提です。
- オブジェクト定義は
CREATEのみに統一 - 作成か更新かの判断は SSDT(DacFx)の差分デプロイに任せる
- 冪等スクリプトが必要なら Pre/Post かビルド対象外に分離
この整理に揃えるだけで、ビルドが安定し、レビューが楽になり、デプロイ事故も減らせます。データベース プロジェクトの良さを活かしつつ、必要な自由度は “置き場所” で確保する、という方針で運用してみてください。

コメント