web-dev-qa-db-ja.com

SQL Serverのインプレースアップグレードは、以前と同様に適切ではありませんか?

私はSQL Server 6.5以降でSQL Serverのオン/オフを行っています。それでも、頭の中で鳴り響く古いアドバイスは、インプレースアップグレードを実行することではありませんでした。

現在、2008 R2 DEVおよびTESTシステムをSQL Server 2012にアップグレードしており、同じハードウェアを使用する必要があります。レポートサービスの構成を復元する必要がないという考えは非常に魅力的であり、時間に関して賢明です。関与する分析サービスはなく、異常または非標準的なものは何もありません。データベースエンジンとレポートサービスだけがインストールされています。

インプレースアップグレードで深刻な問題が発生したことはありますか?または、インプレースアップグレードに関する自分の立場を再評価する必要がありますか?

79
DamagedGoods

本当に短い回答-そのままで問題ありません。後で構成を確認し、SQL Server 2012のベストプラクティスを実装できます。

SQL Serverのアップグレード/移行に関するより長い回答

したがって、これは意見であり、必ずしも間違っているまたは正しい答えがあるわけではありませんが、多くの理由から、インプレースではなく移行スタイルのアップグレードを好みます。そうは言っても-いくつかのクライアントはさまざまな理由でインプレースで行うしかありませんでした。SQLServer 2005以降、インプレースアップグレードは以前ほど悪くはありませんでした。

インプレースアップグレードよりも移行を優先する理由

  • より簡単なロールバック-問題が発生した場合は、「アップグレードを中止しました。この問題を解決する間、接続文字列を古いサーバーに変更してください」と言うだけでロールバックできます。 。インプレースでは、それを修正するか、ダウンしています。
  • ハードウェアの更新-ハードウェアは急速に変化します。 4年前は会社に適していたが、現在および今後4年間はインプレースアップグレードでは適切ではなかったハードウェアに簡単に行き詰まる可能性があります。新しいハードウェアの場合は、とにかくどこかの時点で移行を行う必要があります。
  • Feel Better-確かに...これは主観的ですが、新しいOSのインストール、新しいSQLのインストール、あなたの前に(またはあなたが今日知っていることを知る前に)仕事をしている人からのクモの巣で、将来頭痛の種になる可能性があります。
  • 新しいOS-移行により、最新で最高の状態になっていない場合、新しいOSバージョンで開始する機会が与えられます。
  • テストできます-SQLをインストールしてデータベースと使用法でクラウド化する前に、新しいマシンで一連のベースラインを取得したいですか?あなたは今それを行うことができます。
  • ベストプラクティスに潜入する方が簡単な場合があります-SQL Serverサービスアカウントはローカル管理者であった可能性があります。たぶんビルトイン管理者はSAサーバーの役割に属しているかもしれません。何かが一緒にハッキングされて動作するようになっているかもしれません。すべて修正して、最初からやり直すことができます。
  • 無料のテスト環境と追加のスリープ-この新しい環境を作成するときに、実際のカットオーバー日の前に作業できる環境があることは大きなメリットです住む。新しい環境への移行を行うということは、実際のカットオーバー日を大幅に上回って営業時間中にそれを構築し、事前にさまざまな方法でテストできることを意味します。すべてのアプリケーションとシステムで何日間も完全な回帰テストを実行でき、実際に最終的なリストア/アタッチを実行してすべてのアプリケーションをカットオーバーし、新しい環境にアクセスする前に、非常に安心できます。
  • 一度にすべてを実行する必要はありません-私が遭遇する非常に一般的な状況は、いくつかのインスタンスのみに統合しようとしている環境です。おそらくバージョンごとに1つ、おそらく「層」とバージョンごとに1つ。これらのプロジェクトの多くは、テスト、プロジェクト計画、ベンダー認定の適時性に基づいて、さまざまなアプリケーションやデータベースに異なるタイムラインを持っています。移行を行うと、準備が整ったデータベースを移動でき、準備が整った後も、何らかの理由で移動できないデータベースへの要求を処理できます。

Mind you私はあなたに言っていないhaveこれを移行として行う。インプレースは機能しますが、予算内で新しいハードウェアを購入する予定がなく、このアップグレードでそれを実行できない場合は、適切に機能します。アップグレードプロセスでのサポートは6.5日よりもはるかに優れているため、これを行うことで悪い位置に陥ることはありません。

Dev/testをインプレースで実行する予定があるが、本番環境で移行を行いたい場合は、本番環境の前に少なくとも1つの移行を行うことを検討してください。このようにして、事前にチェックリストを作成し、考えていなかった潜在的な問題に対処することができます。

移行のためのアタッチ/デタッチとバックアップ/復元

移行アプローチを選択する場合でも、まだ議論の余地があると思われる決定がもう1つあります。それが、データベースを新しい環境に移行する方法です。データベースを古いサーバーから切り離して新しいサーバーに接続するか、データベースをバックアップしてそこに復元できます。

私はバックアップ/復元を好みます。デタッチ/アタッチについて聞いた最大の利点は、時間を節約できることです。私にとって、いくつかの理由でバックアップ/復元が成功します:

  • 古いものをアクセス可能な状態に保つ-これにより、ソースサーバーにアクセス可能なデータベースを保持できます。 detach/attachも同じようにする必要がありますが、いくつかの手順が必要であり、detach/attachにはこれを複雑にする可能性のある人為的エラーの余地があります。
  • バックアップがあることを保証しています-デタッチからデータベースを取得するだけでなく、バ​​ックアップ手順を忘れる可能性がある代わりに、そのバックアップを取った。
  • ヒューマンエラー-間違ったファイルを削除したり、送信先を忘れたり、ステップをめちゃくちゃにしたりすると、データとログを移動することにより多くのリスクが発生データベース用のファイル。これで、カットする代わりにコピーすることでこれを軽減できます(デタッチする場合は、カットアンドペーストの習慣から抜け出す必要があります)。 SQL Serverはこれらのファイルをロックしなくなったので、ファイルを誤って削除してしまう危険性が高すぎます。
  • それは実際には遅いわけではありませんより遅い-バックアップを取り、それをコピーするのは少し時間がかかります、しかし、私がそれに余分なリスクを払う用意があるのはそれほど多くはありません。実際、完全復旧モデルとログバックアップを使用すると、以下の「移行アプローチを高速化する方法」で説明するように、カットオーバーのダウンタイムをさらに短縮できます。

バックアップ/復元を行うことに決めた場合-古いソースデータベースは引き続きオンラインになります。バックアップを取った後、そのデータベースをオフラインにしたい。セキュリティ、ジョブ、リンクサーバー、証明書、データベースメール設定、およびその他のインスタンス全体の情報をスクリプト化した後、時々さらに一歩進んでSQLインスタンス全体をオフラインにします。これにより、テスト中に誰かが「すべてが美しく見える!」という問題を回避できます。彼らが古いサーバーの古いデータベースと通信しているのは、1、2日後に気づくだけです。これらのデータベースをオフラインにするか、インスタンス全体をオフラインにすると、誤検知やデータベースの混乱を防ぐことができます。

移行アプローチをより速くする方法

完全復旧モデルを利用することで、ダウンタイムがほとんどないビジープロダクション環境で、古い環境から新しい環境へのカットオーバーに必要なダウンタイムを最小限に抑えることができます。基本的に-最新の完全バックアップ、差分バックアップ、およびNORECOVERYを指定して既に取得したログバックアップを復元することにより、移行先の環境をステージングします。最後のカットオーバーで実行する必要があるのは、ログバックアップを復元することだけです。まだ復元されていないものと、WITH RECOVERYを指定して復元する最終的なログバックアップ。このように大規模なデータベースの場合、実際のカットオーバーダウンタイムウィンドウは、完全な差分およびほとんどのログリストアのコストをダウンタイムウィンドウより前に支払うことで大幅に最小化できます。コメントでこれを指摘してくれた Tao に感謝します!

インプレースアップグレードをより安全にする方法

インプレースアプローチを選択するときに、エクスペリエンスと結果を改善するためにできるいくつかのこと。

  • Backup-環境のすべてのユーザーデータベースとシステムデータベースの適切なバックアップを事前に作成し、それらが適切であることを確認します(私は偏執狂的です。最初にそれらをどこかに復元して、それらが本当に正しいことを確認します。時間を浪費している可能性があります。しかし、災害が発生した場合は、感謝の意を表します。
  • 開始する前に十分にテストしてください-良好な環境と良好なデータベースがあることを確認します。エラーログを確認したり、DBCC CHECKDBを定期的に実行したりする必要がありますが、インプレースアップグレードを実行する前に開始するのがよいでしょう。事前に問題を修正します。
  • OSの状態を確認してください-SQLが正常であることを確認するだけでなく、サーバーが正常であることを確認してください。システムまたはアプリケーションのエラーイベントログに危険なエラーがありますか?あなたの空き容量はどうですか?
  • 最悪の事態に備える-しばらく前に、失敗の準備をしていない場合の前提として、一連のブログ投稿がありました- あなたは実際に準備中ですto失敗 ..私はまだそれを信じています。だから、あなたが抱えているかもしれない問題を考え、それに応じて前もってそれに対処してください。 「失敗」の考え方に身を置くと、他の方法では思いもよらないことを思いつくでしょう。

アップグレードまたは移行チェックリストの重要性

アップグレードを実行する(決定済みまたは移行)場合は、チェックリストを作成し、各環境でこのチェックリストを使用することを真剣に検討する必要があります。このチェックリストには、少なくとも次のような多くのものを含める必要があります。

  1. 開始時-テストアップグレードの実行などのいくつかの操作を実行し、最新のデータベース互換性レベルでアプリケーションをテストし、次のようなツールの実行を検討してください SQL Server Upgrade Advisor SQL Serverのアップグレードまたは移行を行う前に、どのようなタスクを完了する必要があるかを事前に確認します。
  2. 事前手順-クリーンアップ、OSタスク、事前のパッチ適用、アップグレードのためのアプリケーションの準備(クリーンシャットダウン、接続文字列の作業)、backupsなど.
  3. アップグレード/移行手順-アップグレードまたは移行を正常に実行するために必要なすべてのこと。データベースへのインストール、変更(またはテストとアプローチによっては変更しない)互換モードの変更など。
  4. 移行/アップグレード後の手順-さまざまなテスト、新しいバージョンまたは新しいサーバー構成オプションのポスト、ベストプラクティスの実装、セキュリティの変更など。
  5. ロールバックステップ-ロールバックステップとマイルストーンがあるはずです。ここまで来てこれが起こったら、どうしますか? 「完全なロールバックを行う」基準とは何ですか?そして、どのようにそのロールバックを実行しますか(接続文字列の変更の変更、設定の変更、古いバージョンへの戻り、適切な場所での再インストール、移行での古いサーバーのポイントなど)。

次に、本番環境のアップグレードを行う担当者に、本番環境以外の環境でチェックリストを実行してもらいます。特に、可能であれば本番環境に似た環境で閉じるようにし(「South of prod」と言っています...)、問題やポイントを書き留めます。チェックリストが不足しているため、チェックリストから転用したり、即興でした。次に、変更をマージし、製品の変更を楽しんでください

移行後またはアップグレード後、移行前に十分にテストすることの重要性を強調できません。アップグレードの最中にロールバックを決定するのは簡単です-特に移行中は。不快なことがある場合は、ロールバックして、移行の最中に効果的かつ確実にトラブルシューティングできない場合はそれを理解してください。この新しい環境でライブになり、ユーザーが接続すると、ロールバックは困難な作業になります。 SQL Serverデータベースを以前のバージョンに復元することはできません。つまり、手作業とデータの移行です。私は常に古い環境を殺すのに数週間待ちますが、ライブユーザーが新しい環境に触れる前にすべての問題を見つけて古い環境を必要としないようにできる限りのことをする必要があります。アップグレード/移行を開始する前であることをお勧めします。

SQL Server Reporting Servicesの移行/アップグレードに関する簡単なメモSSRSインストールの移行は、多くの人が考えている非常に複雑な作業ではありません。これは technet/books online article is実際にかなり便利です 。その記事で最も重要な警告の1つは、「暗号化キーをバックアップする」です。特に、スケジュールされたレポートe-メール受信者の電子メールアドレス、多数の接続の接続情報など。以前のクライアントに、それがどれほど重要であるかを尋ねることができます。私はそのステップをめちゃくちゃにして、レポートスケジュールと接続文字列のアクセス許可の変更にかなりの時間を費やしたので、彼らは知っています。

92
Mike Walsh

私の経験では、以前と同じ意思決定プロセスを行う必要があります。私の知る限り、SQL Serverのインストール自体、MS SQL Server製品自体、および何百万行ものコードを含むソフトウェアをロールアウトするときに発生する可能性のある問題について、「世界を変える」人はいません。何か問題が発生する可能性があり、「ROLLBACK」オプションがないままになっています。

ただし、他の選択肢があります。システムのスナップショットを作成し、別の場所に復元し、アップグレードを実行して、何が起こるかを確認することができます。このテストはあなたに多くの快適さを与えるはずですが、それは製品ボックスで問題が発生しないことを完全に保証するものではありません。ただし、これはSQL 6.5日には利用できなかったオプションです。

私は最悪のシナリオを想定しています。インプレースアップグレードを実行すると、惨めに失敗します。次に、RTOおよびRCO内でこれから回復する必要があります。企業はリスクを理解していますか?それを軽減するための計画を立てていますか?

これでビジネスに問題がある場合は、しないでください。

14
Ali Razeghi

サーバーが仮想環境で実行されている場合は、クローンでスナップショットを実行してから、一括アップグレードを適用し、インスタンスをテストして、アップグレードが成功したことを確認できます。動作する場合は、スナップショットを適用して、クローンを本番サーバーにすることができます。うまくいかない場合は、スナップショットを削除してアップグレード前のイメージに戻って再試行するか、クローンを削除して完全な移行を行うことができます。

2
Troy

ハードウェアへの多大な投資により、現在のSQL Serverのバージョン(2012、3サーバー、22インスタンス、最大300データベース)を維持しながら、OSのみをアップグレードする必要がありました。ミラーリングなどの複雑な設定は不要.

SQL Serverはアップグレードされていないため、この例は質問と完全には一致しません。示されている手順は、実際のインプレース移行よりも実際には単純であるため、これは良い答えだと思います。

概要:外付けドライブが接続され、主に予防策として完全バックアップを取得しました。モデルとmsdbのみが実際に外部ドライブから復元されます。 ldf/mdfは、デタッチ/アタッチのために残されました。一部のローカルアカウントはDB内で参照されました。それらがOSで再作成された後、DB内の参照が再作成されました(SIDが変更される可能性があるため)。

次に、私たちにとって有効な手順を示します。

1)手順12(サーバーの役割)および18〜23で復元されるサーバーレベルの設定をメモします。

2)SQL Server 2012をSP3にパッチします(システムデータベースを復元する場合は一貫性が必要です)。

3)各インスタンスでバージョンが一致することを確認します。 「@@バージョンを選択」

4)このスクリプトを実行して、これらの6つのスクリプトを生成します。多くのインスタンスがある場合、Redgate SQL Multiscriptは非常に時間の節約になります([ツール]-> [オプション] => [行の長さ]を最大(8192)に調整してから、テキスト出力を使用します)。

  • バックアップ
  • 戻す
  • 切り離す
  • 添付
  • ログインを再作成
  • ユーザーをログインに再リンクする

    -- (1) BACKUP / (2) RESTORE
    --    
    --*** SET THESE to external drive location
    --*** and create the Destination Directories
    declare 
        @backupInstanceDir  varchar(300) = 'F:\ExternalDriveBackups\' + replace(@@servername, '\', '_'),
        @dateSuffix         varchar(100) = '2015-12-14'; 
    
    if (object_id('tempdb..DatabaseStatus') is not null)
    drop table #DAtabseSTatus;
    
    select 
        d.name DbName, 
        d.state_desc DbState,
        d.user_access_desc UserMode,
        convert(bit, (d.is_read_only * -1 + 1)) as IsWritable,
        d.is_trustworthy_on as IsTrustWorthy,
        d.is_in_standby IsInStandby,
        d.recovery_model_desc RecoveryModel,
        suser_sname(d.owner_sid) as Owner,
        convert(bit, 
            case when d.database_id <= 4 or d.is_distributor = 1
                then 1
                else 0
            end) as IsSystemDb,
        mf.type_desc as FileType,
        mf.name FileName,
        mf.state FileState,
        mf.state_desc FileStatDesc,
        mf.physical_name PhysicalName,
        mf.type as FileTypeId    
    into #DatabaseStatus
    from
        sys.master_files AS mf
    join sys.databases AS d
    ON  mf.database_id = d.database_id
    where
        1=1
    order by
        d.name,
        mf.physical_name;
    
    if object_id('tempdb..#sqlOut') is not null
        drop table #sqlOutBU
    
    if object_id('tempdb..#sqlOut') is not null
        drop table #sqlOutRE
    
    create table #sqlOutBU
    (
        Command nvarchar(max) not null,
        Row int identity(1,1) not null primary key
    );
    
    create table #sqlOutRE
    (
        Command nvarchar(max) not null,
        Row int identity(1,1) not null primary key
    );
    
    insert into #sqlOutBU select char(10) + '-- BACKUP SCRIPT' + char(10);
    insert into #sqlOutRE select char(10) + '-- RESTORE SCRIPT' + char(10);
    
    
    insert into #sqlOutBU select char(10) + char(10) + '/* ---------------------------------------------------------------------------------------------' + char(10) + 
    'ServerName: ' + @@servername + char(10) + 'ServiceName: ' + @@servicename + char(10) + 'Version: ' + @@version + 
    '--------------------------------------------------------------------------------------------- */';
    
    insert into #sqlOutRE select char(10) + char(10) + '/* ---------------------------------------------------------------------------------------------' + char(10) + 
    'ServerName: ' + @@servername + char(10) + 'ServiceName: ' + @@servicename + char(10) + 'Version: ' + @@version + 
    '--------------------------------------------------------------------------------------------- */';        
    
    PRINT '--Script for Backing up all DBs in a SQL Server Instance to a specific location' 
    
    SET nocount ON 
    
    insert into #sqlOutBU select char(10) + 
    '--' + char(10) + '-- BACKUP ' + @@servername + '--' + char(10) + 
    'use [Master]; set deadlock_priority high;' + char(10);
    
    insert into #sqlOutRE select '
    -- RESTORE
    --
    -- BE SURE TO BACKUP SYSTEM DBS TO AN ALTERNATE LOCATION JUST BEFORE RESTORING!
    --
    use [Master]; set deadlock_priority high;' + char(10);
    
    DECLARE @dbname nvarchar(128) 
    declare dblist_cursor cursor fast_forward for 
    select [name] from master.sys.databases where [name] != 'tempdb'
    order by iif(database_id <= 4, '0', '1') + [name]
    
    open dblist_cursor 
    fetch next from dblist_cursor into @dbname 
    
    while @@fetch_status = 0 
    begin 
    
        declare @bak nvarchar(300) = @backupInstanceDir + '\' + @dbname + '_' + @dateSuffix + '.bak';
    
        insert into #sqlOutBU select char(10) + 'backup database [' + @dbname + '] to disk = ''' + @bak + ''' WITH COPY_ONLY, NOFORMAT, NOINIT, ' + char(10) + 
            'NAME = N''' + @dbName + '-Full'', SKIP, NOREWIND, NOUNLOAD, COMPRESSION, STATS = 25;';
    
        insert into #sqlOutRE select 'restore database [' + @dbName + '] from disk = ''' + @bak + ''' WITH FILE = 1,' + char(10) +
        (
            select '    move ''' + FileName + ''' to ''' + PhysicalName + '''' From #DatabaseStatus
            where FileType = 'Rows' and DbName = @dbName
        ) + ',' + char(10) +
        (
            select '    move ''' + FileName + ''' to ''' + PhysicalName + '''' From #DatabaseStatus
            where FileType = 'Log' and DbName = @dbName
        ) + ',' + char(10) +
        '    NOUNLOAD, REPLACE, STATS = 25;' + char(10);               
    
        fetch next from dblist_cursor into @dbname 
    end 
    
    close dblist_cursor 
    deallocate dblist_cursor 
    
    insert into #sqlOutBU select char(10) + 'go' + char(10);
    insert into #sqlOutRE select char(10) + 'go' + char(10);
    
    select Command from #sqlOutBU order by Row; -- BACKUP SCRIPT
    select Command from #sqlOutRE order by Row; -- RESTORE SCRIPT
    
    go
    
    
    
    --
    -- (3) DETACH  -  Org Author: Artemakis Artemiou
    --      
    
    if object_id('tempdb..#sqlOutDT') is not null
        drop table #sqlOutDT
    
    create table #sqlOutDT
    (
        Command nvarchar(max) not null,
        Row int identity(1,1) not null primary key
    );
    
    insert into #sqlOutDT select char(10) + '-- DETACH all DBs from a SQL Server Instance' + char(10);      
    
    insert into #sqlOutDT select char(10) + char(10) + '/* ---------------------------------------------------------------------------------------------' + char(10) + 
    'ServerName: ' + @@servername + char(10) + 'ServiceName: ' + @@servicename + char(10) + 'Version: ' + @@version + 
    '--------------------------------------------------------------------------------------------- */';
    
    SET nocount ON 
    
    insert into #sqlOutDT select char(10) + '--' + char(10) + '-- DETACH ' + @@servername + char(10) + '--' + char(10) + '
    use MAster; set deadlock_priority high;' + char(10) + char(10);
    
    DECLARE @dbname nvarchar(128) 
    DECLARE dblist_cursor CURSOR fast_forward FOR 
    SELECT [name] 
    FROM   master.sys.databases 
    WHERE  database_id > 4 
    
    OPEN dblist_cursor 
    FETCH next FROM dblist_cursor INTO @dbname 
    
    WHILE @@FETCH_STATUS = 0 
    BEGIN 
        insert into #sqlOutDT select
        'alter database ' + @dbname + ' set single_user with rollback immediate;' + char(10) +
        'EXEC sp_detach_db ''' + @dbname + ''', ''true'';' + char(10);
        FETCH next FROM dblist_cursor INTO @dbname 
    END 
    
    CLOSE dblist_cursor 
    DEALLOCATE dblist_cursor 
    
    insert into #sqlOutDT select char(10) + 'go' + char(10);
    select Command from #sqlOutDT order by Row;
    
    go
    
    
    
    --
    -- (4) ATTACH  -  Org Author: Artemakis Artemiou
    --    
    
    if object_id('tempdb..#sqlOut') is not null
        drop table #sqlOutAT
    
    create table #sqlOutAT
    (
        Command nvarchar(max) not null,
        Row int identity(1,1) not null primary key
    );
    
    insert into #sqlOutAT select char(10) + '-- ATTACH ALL DBs to a SQL Server Instance' + char(10);
    
    insert into #sqlOutAT select char(10) + char(10) + '/* ---------------------------------------------------------------------------------------------' + char(10) + 
    'ServerName: ' + @@servername + char(10) + 'ServiceName: ' + @@servicename + char(10) + 'Version: ' + @@version + 
    '--------------------------------------------------------------------------------------------- */';
    
    SET NOCOUNT ON
    
    insert into #sqlOutAT select char(10) + '--' + char(10) + '-- ATTACH ' + @@servername + char(10) + '--' + char(10) + 
    'use MAster;' + char(10) + char(10);
    
    DECLARE @dbname nvarchar(128);
    
    DECLARE DBList_cursor CURSOR fast_forward FOR 
    select [name] from master.sys.databases where database_id > 4
    order by name;
    
    OPEN DBList_cursor
    
    FETCH NEXT FROM DBList_cursor 
    INTO @dbname
    
    WHILE @@FETCH_STATUS = 0
    BEGIN
    
    declare @attach_TSQL_script varchar(max)
    set @attach_TSQL_script=''
    set @attach_TSQL_script=@attach_TSQL_script+'CREATE DATABASE ' + @dbname +' ON ' 
    
    declare @tsql varchar(max),@filename varchar(max)
    set @tsql='DECLARE DBFiles_cursor CURSOR FOR select [filename] from '+ @dbname + '.sys.sysfiles'
    
    execute (@tsql) 
    
    PRINT '--'+@dbname 
    
    OPEN DBFiles_cursor
    FETCH NEXT FROM DBFiles_cursor INTO @filename
    
    WHILE @@FETCH_STATUS = 0
    BEGIN   
    set @attach_TSQL_script=@attach_TSQL_script+ char(10)+'    (FILENAME = '''+ @filename +'''),' 
    FETCH NEXT FROM DBFiles_cursor INTO @filename
    END
    
    set @attach_TSQL_script=SUBSTRING(@attach_TSQL_script,0,len(@attach_TSQL_script))
    set @attach_TSQL_script=@attach_TSQL_script+ char(10) +'    FOR ATTACH;';
    
    insert into #sqlOutAT select @attach_TSQL_script + char(10);
    
    PRINT @attach_TSQL_script 
    PRINT ''
    
    CLOSE DBFiles_cursor
    DEALLOCATE DBFiles_cursor
    
    FETCH NEXT FROM DBList_cursor 
    INTO @dbname
    
    END 
    
    CLOSE DBList_cursor
    DEALLOCATE DBList_cursor
    
    insert into #sqlOutAT select char(10) + 'go' + char(10);
    select Command from #sqlOutAT order by Row;
    go
    
    
    
    --
    -- (5) GENERATE A 'RE-CREATE LOGINS' SCRIPT
    --
    -- This script was modified from a version that was designed to copy from one server to another:
    --      http://stackoverflow.com/a/5983773/538763
    --
    
    
    USE [master]
    
    if object_id('tempdb..#sqlOut') is not null
    drop table #sqlOut;
    
    create table #sqlOut
    (
    Command nvarchar(max) not null,
    Row int identity(1,1) not null primary key
    );
    
    insert into #sqlOut select char(10) + '-- RECREATE LOGINS' + char(10);
    
    
    insert into #sqlOut select char(10) + char(10) + '/* ---------------------------------------------------------------------------------------------' + char(10) + 
    'ServerName: ' + @@servername + char(10) + 'ServiceName: ' + @@servicename + char(10) + 'Version: ' + @@version + 
    '--------------------------------------------------------------------------------------------- */';
    
    insert into #sqlOut select 'use Master;' + char(10);
    go
    SET ANSI_NULLS ON
    GO
    SET QUOTED_IDENTIFIER ON
    GO
    declare @Debug bit = 0;
    declare @PartnerServer varchar(100) = @@SERVICENAME;  -- use current server before it is shutdown (disabled below)
    
    declare
        @MaxID int,
        @CurrID int,
        @SQL nvarchar(max),
        @LoginName sysname,
        @IsDisabled int,
        @Type char(1),
        @SID varbinary(85),
        @SIDString nvarchar(100),
        @PasswordHash varbinary(256),
        @PasswordHashString nvarchar(300),
        @RoleName sysname,
        @Machine sysname,
        @PermState nvarchar(60),
        @PermName sysname,
        @Class tinyint,
        @MajorID int,
        @ErrNumber int,
        @ErrSeverity int,
        @ErrState int,
        @ErrProcedure sysname,
        @ErrLine int,
        @ErrMsg nvarchar(2048);
    
    declare @Logins Table (LoginID int identity(1, 1) not null primary key,
                        [Name] sysname not null,
                        [SID] varbinary(85) not null,
                        IsDisabled int not null,
                        [Type] char(1) not null,
                        PasswordHash varbinary(256) null)
    declare @Roles Table (RoleID int identity(1, 1) not null primary key,
                    RoleName sysname not null,
                    LoginName sysname not null)
    declare @Perms Table (PermID int identity(1, 1) not null primary key,
                    LoginName sysname not null,
                    PermState nvarchar(60) not null,
                    PermName sysname not null,
                    Class tinyint not null,
                    ClassDesc nvarchar(60) not null,
                    MajorID int not null,
                    SubLoginName sysname null,
                    SubEndPointName sysname null)
    
    Set NoCount On;
    
    If CharIndex('\', @PartnerServer) > 0
    Begin
    Set @Machine = LEFT(@PartnerServer, CharIndex('\', @PartnerServer) - 1);
    End
    Else
    Begin
    Set @Machine = @PartnerServer;
    End
    
    -- Get all Windows logins from principal server
    Set @SQL = 'Select P.name, P.sid, P.is_disabled, P.type, L.password_hash' + CHAR(10) +
        'From ' /*+ QUOTENAME(@PartnerServer) + '.*/ + 'master.sys.server_principals P' + CHAR(10) +
        'Left Join '/* + QUOTENAME(@PartnerServer) + '.*/ + 'master.sys.sql_logins L On L.principal_id = P.principal_id' + CHAR(10) +
        'Where P.type In (''U'', ''G'', ''S'')' + CHAR(10) +
        'And P.name <> ''sa''' + CHAR(10) +
        'And P.name Not Like ''##%''' + CHAR(10) +
        'and P.Name Not like ''NT SERVICE%''' + CHAR(10) +
        'And CharIndex(''' + @Machine + '\'', P.name) = 0;';
    
    Insert Into @Logins (Name, SID, IsDisabled, Type, PasswordHash)
    Exec sp_executesql @SQL;
    
    -- Get all roles from principal server
    Set @SQL = 'Select RoleP.name, LoginP.name' + CHAR(10) +
        'From '/* + QUOTENAME(@PartnerServer) + '.*/ + 'master.sys.server_role_members RM' + CHAR(10) +
        'Inner Join '/* + QUOTENAME(@PartnerServer) + .*/ +'master.sys.server_principals RoleP' +
        CHAR(10) + char(9) + 'On RoleP.principal_id = RM.role_principal_id' + CHAR(10) +
        'Inner Join '/* + QUOTENAME(@PartnerServer) + '.*/ + 'master.sys.server_principals LoginP' +
        CHAR(10) + char(9) + 'On LoginP.principal_id = RM.member_principal_id' + CHAR(10) +
        'Where LoginP.type In (''U'', ''G'', ''S'')' + CHAR(10) +
        'And LoginP.name <> ''sa''' + CHAR(10) +
        'And LoginP.name Not Like ''##%''' + CHAR(10) +
        'And LoginP.name Not Like ''NT SERVICE%''' + CHAR(10) +
        'And RoleP.type = ''R''' + CHAR(10) +
        'And CharIndex(''' + @Machine + '\'', LoginP.name) = 0;';
    
    Insert Into @Roles (RoleName, LoginName)
    Exec sp_executesql @SQL;
    
    -- Get all explicitly granted permissions
    Set @SQL = 'Select P.name Collate database_default,' + CHAR(10) +
        '   SP.state_desc, SP.permission_name, SP.class, SP.class_desc, SP.major_id,' + CHAR(10) +
        '   SubP.name Collate database_default,' + CHAR(10) +
        '   SubEP.name Collate database_default' + CHAR(10) +
        'From '/* + QUOTENAME(@PartnerServer) + '.*/ + ' master.sys.server_principals P' + CHAR(10) +
        'Inner Join '/* + QUOTENAME(@PartnerServer) + '.*/ + ' master.sys.server_permissions SP' + CHAR(10) +
        CHAR(9) + 'On SP.grantee_principal_id = P.principal_id' + CHAR(10) +
        'Left Join '/* + QUOTENAME(@PartnerServer) + '.*/ + ' master.sys.server_principals SubP' + CHAR(10) +
        CHAR(9) + 'On SubP.principal_id = SP.major_id And SP.class = 101' + CHAR(10) +
        'Left Join '/* + QUOTENAME(@PartnerServer) + '.*/ + ' master.sys.endpoints SubEP' + CHAR(10) +
        CHAR(9) + 'On SubEP.endpoint_id = SP.major_id And SP.class = 105' + CHAR(10) +
        'Where P.type In (''U'', ''G'', ''S'')' + CHAR(10) +
        'And P.name <> ''sa''' + CHAR(10) +
        'And P.name Not Like ''##%''' + CHAR(10) +
        'And P.name Not Like ''NT SERVICE%''' + CHAR(10) +
        'And CharIndex(''' + @Machine + '\'', P.name) = 0;'
    
    Insert Into @Perms (LoginName, PermState, PermName, Class, ClassDesc, MajorID, SubLoginName, SubEndPointName)
    Exec sp_executesql @SQL;
    
    --select * from @Logins;
    --select * from @Roles;
    --select * from @perms;
    
    
    Select @MaxID = Max(LoginID), @CurrID = 1
    From @Logins;
    
    While @CurrID <= @MaxID
    Begin
    Select @LoginName = Name,
        @IsDisabled = IsDisabled,
        @Type = [Type],
        @SID = [SID],
        @PasswordHash = PasswordHash
    From @Logins
    Where LoginID = @CurrID;
    
    --    If Not Exists (Select 1 From sys.server_principals
    --              Where name = @LoginName)
    Begin
    
        set @sql = char(10);
        set @sql += 'If Not Exists (Select 1 From sys.server_principals Where name = ''' + @LoginName + ''')' + char(10);
        set @sql += 'begin' + char(10) + '    ';
    
        Set @SQL += 'Create Login ' + quotename(@LoginName)
        If @Type In ('U', 'G')
        Begin
            Set @SQL = @SQL + ' From Windows;'
        End
        Else
        Begin
            Set @PasswordHashString = '0x' +
                Cast('' As XML).value('xs:hexBinary(sql:variable("@PasswordHash"))', 'nvarchar(300)');
    
            Set @SQL = @SQL + ' With Password = ' + @PasswordHashString + ' HASHED;  --, ';
    
            Set @SIDString = '0x' +
                Cast('' As XML).value('xs:hexBinary(sql:variable("@SID"))', 'nvarchar(100)');
            Set @SQL = @SQL + 'SID = ' + @SIDString + ';' + char(10);
        End
    
        set @sql += char(10) +
            '    print ''Created Login ' + @loginName  + ''';' + char(10) +
            'end' + char(10) +
            'else' + char(10) +
            convert(nvarchar(max), '    print ''Login ' + @loginName + ' already existed. '';') + char(10);
    
        If @Debug = 0
        insert into #sqlOut select @SQL;                      
        Else
        Print @SQL;
    
        If @IsDisabled = 1
        Begin
            Set @SQL = 'Alter Login ' + quotename(@LoginName) + ' Disable;'
            If @Debug = 0
                insert into #sqlOut select @SQL;                              
            Else              
                Print @SQL;              
        End
        End
    Set @CurrID = @CurrID + 1;
    End
    
    
    insert into #sqlOut select char(10) + 'use Master;' + char(10);
    
    Select @MaxID = Max(RoleID), @CurrID = 1
    From @Roles;
    
    While @CurrID <= @MaxID
    Begin
    Select @LoginName = LoginName,
        @RoleName = RoleName
    From @Roles
    Where RoleID = @CurrID;
    
    /*  If Not Exists (Select 1 From sys.server_role_members RM
                Inner Join sys.server_principals RoleP
                    On RoleP.principal_id = RM.role_principal_id
                Inner Join sys.server_principals LoginP
                    On LoginP.principal_id = RM.member_principal_id
                Where LoginP.type In ('U', 'G', 'S')
                And RoleP.type = 'R'
                And RoleP.name = @RoleName
                And LoginP.name = @LoginName)*/
    Begin
        If @Debug = 0
        Begin          
            insert into #sqlOut select 'Exec sp_addsrvrolemember @rolename = ''' + @RoleName + ''', @loginame = ''' + @LoginName + ''';';
        End
        Else
        Begin
            Print 'Exec sp_addsrvrolemember @rolename = ''' + @RoleName + ''',';
            Print '     @loginame = ''' + @LoginName + ''';';
        End
    End
    
    Set @CurrID = @CurrID + 1;
    End
    
    
    insert into #sqlOut select char(10) + 'use Master;' + char(10);
    
    
    Select @MaxID = Max(PermID), @CurrID = 1
    From @Perms;
    
    While @CurrID <= @MaxID
    Begin
    Select @PermState = PermState,
        @PermName = PermName,
        @Class = Class,
        @LoginName = LoginName,
        @MajorID = MajorID,
        @SQL = PermState + space(1) + PermName + SPACE(1) +
            Case Class When 101 Then 'On Login::' + QUOTENAME(SubLoginName)
                    When 105 Then 'On ' + ClassDesc + '::' + QUOTENAME(SubEndPointName)
                    Else '' End +
            ' To ' + QUOTENAME(LoginName) + ';'
    From @Perms
    Where PermID = @CurrID;
    
    /*If Not Exists (Select 1 From sys.server_principals P
                Inner Join sys.server_permissions SP On SP.grantee_principal_id = P.principal_id
                Where SP.state_desc = @PermState
                And SP.permission_name = @PermName
                And SP.class = @Class
                And P.name = @LoginName
                And SP.major_id = @MajorID)*/
    Begin
        If @Debug = 0
                insert into #sqlOut select @sql;                      
        Else          
            Print @SQL;          
    End
    
    Set @CurrID = @CurrID + 1;
    End
    
    
    select Command from #sqlOut as SqlOut order by Row;
    go
    
    
    --
    -- (6) Generate a script to Re-link all users to logins based on current state (before shutdown)
    --
    
    use Master;
    
    if object_id('tempdb..#sqlOut') is not null
    drop table #sqlOut;
    
    create table #sqlOut
    (
        Command nvarchar(max) not null,
        Row int identity(1,1) not null primary key
    );
    
    insert into #sqlOut select char(10) + '-- RELINK USERS TO LOGINS' + char(10);
    
    insert into #sqlOut select char(10) + char(10) + '/* ---------------------------------------------------------------------------------------------' + char(10) + 
    'ServerName: ' + @@servername + char(10) + 'ServiceName: ' + @@servicename + char(10) + 'Version: ' + @@version + 
    '--------------------------------------------------------------------------------------------- */';
    
    declare @dbCmd varchar(8000) = '
    use ?;
    
    insert into #sqlOut select char(10) + ''use ?;'' + char(10);  
    
    with links as
    (
    select u.name as UserName,
        l.loginname as LoginName
        from sysusers u 
        join master..syslogins l
        on u.sid = l.sid        
    where u.name != ''dbo''
        and u.isSqlUser = 1 or l.isNtName = 1 or l.isNtGroup = 1
    )
    insert into #sqlOut 
    select ''alter user ['' + UserName + ''] with name = ['' + UserName + ''], login = ['' + LoginName + '']''
    from links
    ';    
    
    exec sp_MSforeachdb @dbCmd;
    
    select Command from #sqlOut order by Row;
    
    go
    

5)スクリプトを実行して、システム(マスター、msdb、モデル)を含むすべてのDBを外部ドライブにバックアップします。

6)スクリプトを実行してすべてのDBを切り離します

7)Cドライブが再フォーマットされます。 LDF/MDFがCにない場合は、それらを保存します。

8)Windows Server 2012がCにインストールされている

9)元のシステムファイルのLDF/MDFがCドライブにない場合は、それらを邪魔にならない場所に移動します。

10)SQL Server 2012が再インストールされ、SP3にパッチされます。システムのユーザー/グループアカウントを再作成する

11)システムDBを新しい場所またはファイル名にバックアップします(元のファイルを上書きしないように注意してください)。

12)役割の再作成スニペットを実行します。何かのようなもの:

USE [master]
CREATE SERVER ROLE [SomeServerRole]
--ALTER SERVER ROLE [dbcreator] ADD MEMBER [SomeServerRole]
--ALTER SERVER ROLE [bulkadmin] ADD MEMBER [SomeServerRole]
-- ALTER SERVER ROLE [SomeServerRole] ADD MEMBER [SomeMemberOrRole]

13)ログインスクリプトを再作成します(ログインが復元された場合は何もしません)

14)SQLエージェントを停止します。

(ここでマスターを復元することができました。

15)上記のスクリプトを使用してmdf/ldfをアタッチします。 a。失敗した場合は、上記のスクリプトを使用して手動でbakから復元します。

16)モデルの復元の試行

17)SQLエージェントが停止していることを確認します。 MSDB(リンク)を復元します。失敗した場合、ジョブ+メンテナンスプラン+メール設定+オペレーターを再作成する必要があります

18)ログインユーザースクリプトを開く...

    a. If there are master users (rare?) then First Re-Create users for master since it was not restored:
        use master;       
        CREATE USER [ABC] FOR LOGIN [machine\ABC]

    b. Run the rest of the script

19)Service Brokerが元の値SELECT名、is_broker_enabled FROM sys.databasesと一致するようにします。

    alter database MSDB set single_user with rollback immediate;
    ALTER DATABASE [MSDB] SET ENABLE_BROKER;
    alter database MSDB set multi_user;

20)SQLエージェントを起動します

21)並列処理のしきい値を元の値に設定します

22)データベース設定を元の値に調整します。

 declare @dbCmd varchar(8000) = '
      use ?;
      if db_name() not in (''master'', ''model'', ''tempdb'', ''msdb'')
      begin
             print ''Adjusting [?]...'';    
            alter database [?] set single_user with rollback immediate;
             aLTER AUTHORIZATION ON DATABASE::[?] to [sa];
            -- alter database [?] set trustworthy on;
            ALTER DATABASE [?] SET AUTO_CLOSE OFF WITH NO_WAIT;     
            alter database [?] set multi_user;
      end     
      else
             print ''Skipping [?]...'';
    ';    

    exec sp_MSforeachdb @dbCmd;

23)ジョブの所有権を確認します。

select s.name as JobName, l.name as login, SUSER_SNAME(s.owner_sid) AS login2
from  msdb..sysjobs s 
left join master.sys.syslogins l on s.owner_sid = l.sid

SQL Serverのバージョンもアップグレードされていた場合、モデルとmsdbデータベースが復元された可能性があるため、ジョブが失われた可能性があります https://support.Microsoft.com/en-us/kb/264474

不足しているもの:

  • マスターデータベースの元のユーザー(まれですか?)
  • サーバーの役割
1
crokusek

どちらのアプローチ自体にも問題はありません。私は両方を実行しましたが、どちらの結果も通常は良好です。

移行アプローチに問題がある場合、それは技術的ではなく、怠惰です。会社がまだxxxxバージョンに完全に移行していない理由は、スイングマイグレーションを選択し、完全に移行するためのハードワークを行ったことがないためです。現在、サーバーのセットは1つではなく2つ以上あります。

0
RowlandG