web-dev-qa-db-ja.com

SQL Server-null不可の列を既存のテーブルに追加-SSDT Publishing

ビジネスロジックにより、テーブルに新しい列が常に必要であることを確認することが重要です。したがって、NOT NULLとしてテーブルに追加する必要があります。これを行う方法を説明する 前の質問 とは異なり手動で、これはSSDTパブリッシュで管理する必要があります。

いくつかの実現のため、私はしばらくの間、この単純なサウンドのタスクに対して壁に頭をぶつけてきました。

  1. デフォルト値は適切ではなく、計算列にすることはできません。多分それは外部キー列ですが、他の人にとっては0や-1のような偽の値を使用することはできません。これらの値には有意性がある可能性があるためです(たとえば、数値データ)。
  2. 展開前スクリプトに列を追加すると、同じ列を2回目に自動的に作成しようとしたときにパブリッシュが失敗します(展開前スクリプトがべき等になるように記述されている場合でも)。 (これは私が別の方法で簡単な解決策を考えることができるので、本当に悪化しています)
  3. 展開後のスクリプトで列をNOT NULLに変更すると、SSDTスキーマの更新が発生するたびに元に戻ります(少なくとも、コードベースはソース管理と実際にサーバー上にあるものとの間で不一致になります)
  4. 将来NOT NULLに変更するつもりで、現在列をnull可能として追加しても、ターゲットシステムは次回アップグレード時にすべてのテーブルが同じ状態であるとは限らないため、ソース管理の複数のブランチ/フォークでは機能しません。 (これはとにかくIMOが良いアプローチであるというわけではありません)

他の人から聞いたアプローチは、テーブル定義を直接更新して(スキーマの更新が一貫するようにする)、コンテンツ全体を移動するデプロイ前スクリプトを書くことです新しい列作成ロジックが含まれる一時テーブルにテーブルを変換し、展開後のスクリプトで行を戻します。ただし、これは非常に危険なようですが、NOT NULL列が既存のデータを含むテーブルに追加されていることを検出すると、発行プレビューが失敗します(展開前スクリプトの前に検証が実行されるため)。

孤立したデータを危険にさらすことなく、またはnullを使用できない新しい列を追加したり、本質的に危険な長い移行スクリプトを使用してすべてのパブリッシュでデータを前後に移動したりするにはどうすればよいですか?

ありがとう。

10
Elaskanator

過去にこれを行った方法を紹介します。これは、2番目のポイントで呼び出す展開前スクリプトの特定の制限を解決するように設計されています。

配置前スクリプトに列を追加すると、同じ列を2回目に自動的に作成しようとしたときにパブリッシュが失敗します(配置前スクリプトがべき等になるように記述されている場合でも)。

展開前スクリプトがこれに対して機能しない理由

SSDTプロジェクトをデプロイすると、プロジェクトをつなぎ合わせる方法は次のようになります(少し簡略化されていますが、一般的には)。

  1. ソース(dacpacファイル)とターゲット(データベース)の間で「スキーマ比較」を実行します
  2. その比較の結果に基づいて展開スクリプトを生成します
  3. Dacpacで展開前のスクリプトを処理し(トークンの置き換えなど)、展開スクリプトの先頭にコンテンツを挿入します
  4. 展開後のスクリプトについても同じことを行い、展開スクリプトのendに追加します

ターゲットデータベースではなくdacpacに新しい列が存在する場合、手順2でその列を追加するコードが生成されます。そのため、配置前スクリプトがこの列を追加すると、スクリプトの主要部分は失敗します(手順1のスキーマ比較の結果に基づいて、列が存在しないと想定しているため)。

解決策:SSDT以前のスクリプト

マーティン・スミスはこのオプションについてコメントで述べていますが、これが今のところ私にとって最も効果的な解決策です。

供給パイプラインでモデル前スクリプトを使用します。これはSSDTの一部ではなく、dacfxの発行前に実行されるステップです。そのため、この場合、プレモデルスクリプトは列に目的の値を追加してnullにしないようにすることができ、パブリッシュが発生するときはSSDTが予期する状態にあるため、何もする必要がありません。私はまだpredeployスクリプトの多くの用途を見つけていません。 – Martin Smith6月1日21:45

このソリューションを実装する一般的な手順は次のとおりです。

  1. SSDTプロジェクトにスクリプトを作成して、「SSDT以前」のT-SQLコードを保持します
    • 展開プロセスの仕組みによっては、これらのファイルのコードはおそらくべき等であるべきです
  2. このスクリプトは必ず「ビルドアクション=なし」および「出力ディレクトリにコピー=常にコピー」に設定してください
    • 「常にコピー」オプションは、デプロイメントプロセスがデプロイメントアーティファクトでこのスクリプトを見つけられる必要があるため、特に重要です。
  3. 展開プロセスで、このスクリプトを見つけて実行しますbeforeSSDTスキーマの比較が行われます
  4. そのスクリプトが正常に実行されたら、DacServices/DacFx /を使用して、通常どおりデプロイメントを完了することができます。

最終的に、これにより、SSDT以前のスクリプトで、複雑なビジネスロジックを使用して入力された任意のカスタムコードを使用して列を追加できます。

SSDTプロジェクトに列定義を追加することもできます(ソース管理はデータベースの実際の状態と一致します)。ただし、スキーマ比較を実行しても、その列に関連する変更は確認されません(既に展開しているため)。

Pre-SSDTの他の用途

SSDTが完全に不要な場合にSSDTが「テーブルの再構築」操作*を実行する展開をテストすると、よくわかります。これは、更新されたスキーマで新しいテーブルが作成され、すべてのデータがそのテーブルにコピーされ、古いテーブルが削除され、新しいテーブルの名前が変更されて古いテーブルが置き換えられる場所です。

これは、テーブルが大きい場合、大規模なトランザクションログファイルの増大やその他の問題を引き起こす可能性があります。スキーマの変更がこれを引き起こしていることに気付いた場合は、代わりにpre-SSDT(通常は単純なALTER TABLEステートメント)、テーブルの再構築を避けます。

これは良い考えですか?

私はそう思う。 データベースを提供するための2つの異なるアプローチを批評する:移行と状態 Alex Yatesによると、これは基本的に2つのアプローチを少し組み合わせたものです。 SSDTは状態ベースですが、SSDTが一般的な方法で処理する方法がない複雑なシナリオの一部を処理するために、移行ステップ(SSDTの前)を組み込んでいます。

この回答を書きながら検索を行う場合、何を検索するかがわかったら、これは実際にはSSDTユーザーコミュニティで議論される非常に一般的なアプローチです。私はそれが呼ばれるのを見ました:

  • 事前比較
  • プレモデル
  • プリDAC
  • sSDT以前

等ここに私が上で述べた多くのポイントをカバーする素晴らしい記事があります:

SSDTへの事前比較および導入前スクリプト

そして、(#4 –システムタイプからユーザー定義タイプセクションへの変更)のRed Gateからの1つで、これも事前比較と呼ばれます。

ReadyRollの有無にかかわらず、10個のSSDT展開障害を修正する方法

では、展開前スクリプトの要点は何でしょうか。

Martinは、「デプロイ前スクリプトの多くの使用法」を見つけられなかったと指摘し、私も同じように感じる傾向があります。しかし、それらが役立つシナリオがあります。

同僚が私に指摘した1つの例は、展開後のスクリプトで使用する一時テーブルにデータを格納することでした(たとえば、あるテーブルから別のテーブルに列を移動しているとします)。


*テーブルの再構築は次のようになります。これは恐ろしいことですよね?

GO
PRINT N'Starting rebuilding table [dbo].[MyTable]...';


GO
BEGIN TRANSACTION;

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

SET XACT_ABORT ON;

CREATE TABLE [dbo].[tmp_ms_xx_MyTable] (
    [Id] BIGINT IDENTITY (1, 1) NOT NULL,
    -- etc, other columns
);

IF EXISTS (SELECT TOP 1 1 
           FROM   [dbo].[MyTable])
    BEGIN
        SET IDENTITY_INSERT [dbo].[tmp_ms_xx_MyTable] ON;
        INSERT INTO [dbo].[tmp_ms_xx_MyTable] ([Id], ...)
        SELECT   [Id],
                 -- etc, other columns
        FROM     [dbo].[MyTable]
        ORDER BY [Id] ASC;
        SET IDENTITY_INSERT [dbo].[tmp_ms_xx_MyTable] OFF;
    END

DROP TABLE [dbo].[MyTable];

EXECUTE sp_rename N'[dbo].[tmp_ms_xx_MyTable]', N'MyTable';

COMMIT TRANSACTION;

SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
11
Josh Darnell