オンプレミスのSQL Server 2017データベースをAzure SQLデータベースに移行する必要があり、通過する制限がかなりあるため、いくつかの課題に直面しています。
特に、Azure SQLデータベースはUTC時間(タイムゾーンなし)でのみ機能し、ローカル時間を必要とするため、GETDATE()
everywhereデータベース内。これは、予想以上の作業であることが証明されています。
私は自分のタイムゾーンで正しく動作する現地時間を取得するユーザー定義関数を作成しました。
_CREATE FUNCTION [dbo].[getlocaldate]()
RETURNS datetime
AS
BEGIN
DECLARE @D datetimeoffset;
SET @D = CONVERT(datetimeoffset, SYSDATETIMEOFFSET()) AT TIME ZONE 'Pacific SA Standard Time';
RETURN(CONVERT(datetime,@D));
END
_
私が問題を抱えている問題は、すべてのビュー、ストアドプロシージャ、計算列、デフォルト値、その他の制約などで、この関数を使用して実際にGETDATE()
を変更することです。
この変更を実装する最良の方法は何ですか?
マネージドインスタンス のパブリックプレビューを表示しています。 GETDATE()
でも同じ問題が発生するため、この問題の解決にはなりません。 Azureへの移行は必須です。このデータベースは、常にこのタイムゾーンで使用されます(使用されます)。
SQL Serverツールを使用して、データベースオブジェクトの定義を、テーブル、ビュー、トリガー、SP、関数などを含むSQLファイルにエクスポートします。
テキスト"GETDATE()"
を見つけて"[dbo].[getlocaldate]()"
に置き換えることができるテキストエディターを使用して、SQLファイルを編集します(最初にバックアップを作成します)。
Azure SQLで編集済みのSQLファイルを実行して、データベースオブジェクトを作成します...
データのマイグレーションを実行します。
ここに、Azureのドキュメントからの参照があります。 SQL Azureのスクリプトの生成
この変更を実装する最良の方法は何ですか?
私は逆に働きます。データベース内のすべてのタイムスタンプをUTCに変換し、UTCを使用してフローを実行します。別のtzのタイムスタンプが必要な場合は、(上記のように)AT TIME ZONE
を使用して生成された列を作成し、指定されたTZ(アプリ用)にタイムスタンプをレンダリングできます。しかし、私はUTCをアプリに戻し、そのロジック(表示ロジック)をアプリに書き込むことを真剣に検討します。
エクスポートして手動で編集して再実行するのではなく、次のような方法でデータベース内で直接ジョブを実行することができます。
DECLARE C CURSOR FOR
SELECT sm.definition, so.type
FROM sys.objects so
JOIN sys.all_sql_modules sm ON sm.object_id = so.object_id
WHERE so.type IN ('P', 'V')
ORDER BY so.name
DECLARE @SQL NVARCHAR(MAX), @ojtype NVARCHAR(MAX)
OPEN C
FETCH NEXT FROM C INTO @SQL, @ojtype
WHILE @@FETCH_STATUS = 0 BEGIN
IF @objtype = 'P' SET @SQL = REPLACE(@SQL, 'CREATE PROCEDURE', 'ALTER PROCEDURE')
IF @objtype = 'V' SET @SQL = REPLACE(@SQL, 'CREATE VIEW' , 'ALTER VIEW' )
SET @SQL = REPLACE(@SQL, 'GETDATE()', '[dbo].[getlocaldate]()')
--PRINT @SQL
EXEC (@SQL)
FETCH NEXT FROM C INTO @SQL, @ojtype
END
CLOSE C
DEALLOCATE C
もちろん、関数やトリガーなどを処理するように拡張することもできます。
注意点がいくつかあります。
もう少し明るくして、CREATE
とPROCEDURE
/VIEW
/<other>
の間の異なる/余分な空白を処理する必要がある場合があります。そのため、REPLACE
ではなくCREATE
をそのままにしてDROP
を最初に実行することをお勧めしますが、これによりsys.depends
や友人がALTER
が失敗する可能性があるキルター、ALTER
が失敗した場合でも、少なくともDROP
+ CREATE
が存在しない場所に既存のオブジェクトがまだ残っています。
独自のスキーマをアドホックTSQLで変更するような「賢い」匂いがコードにある場合は、CREATE
-> ALTER
の検索と置換が干渉しないことを確認する必要があります。それと。
カーソルを使用するか、export + edit + runメソッドを使用するかに関係なく、操作後にアプリケーション全体を回帰テストする必要があります。
この方法を使用して、過去に同様のスキーマ全体の更新を行いました。これは少しハックのようで、見苦しい感じがしますが、最も簡単で迅速な方法の場合もあります。
デフォルトおよび他の制約も同様に変更できますが、変更および変更はできず、ドロップおよび再作成のみが可能です。何かのようなもの:
DECLARE C CURSOR FOR
SELECT AlterDefaultSQL = 'ALTER TABLE [' +st.name+ '] DROP CONSTRAINT [' + si.name + '];'
+ CHAR(10)
+ 'ALTER TABLE [' +st.name+ '] ADD CONSTRAINT [' + si.name + '] DEFAULT '+REPLACE(si.definition, 'GETDATE()', '[dbo].[getlocaldate]()')+' FOR '+sc.name+';'
FROM sys.tables st
JOIN sys.default_constraints si ON si.parent_object_id = st.object_id
JOIN sys.columns sc ON sc.default_object_id = si.object_id
DECLARE @SQL NVARCHAR(MAX)
OPEN C
FETCH NEXT FROM C INTO @SQL
WHILE @@FETCH_STATUS = 0 BEGIN
--PRINT @SQL
EXEC (@SQL)
FETCH NEXT FROM C INTO @SQL
END
CLOSE C
DEALLOCATE C
あなたが対処する必要があるかもしれないいくつかのより楽しい:時間によって分割しているなら、それらの部分も変更する必要があるかもしれません。時間によるパーティション分割が日ごとに行われることはまれですが、パーティション分割関数によってDATETIME
sがtimezineに応じて前日または翌日として解釈され、パーティションが通常のクエリと整列されないままになるという問題が発生する可能性があります。
私はデイビッドの答えが本当に好きで、プログラムによる方法でそれを支持しました。
しかし、SSMSを介してAzureでテスト実行するために、今日これを試すことができます。
データベースを右クリック->タスク->スクリプトの生成..
[バックストーリー]実稼働環境がSQL 2008にある間に、すべてのテスト環境をSQL 2008 R2にアップグレードするジュニアDBAがいました。テストから本番環境に移行するには、生成スクリプトを使用してSQL内でスクリプトを生成する必要があり、詳細オプションでは、[スクリプトするデータのタイプ:スキーマとデータ]オプションを使用して大規模なテキストファイルを生成しました。テストR2データベースを従来のSQL 2008サーバーに正常に移動できました。以前のバージョンへのデータベースの復元は機能しませんでした。大きなファイルを入力するためにsqlcmdを使用しました。ファイルはSSMSテキストバッファーには大きすぎることが多いためです。
ここで私が言っていることは、このオプションはおそらくあなたにも役立つだろうということです。追加の手順を1つ実行し、生成されたテキストファイルでgetdate()を検索して[dbo] .getlocaldateに置き換えるだけです。 (ただし、移行前に関数をデータベースに入れます)
(私はデータベースの復元のこのバンドエイドに熟練したくなかったが、しばらくの間それは物事を実行するための事実上の方法になりました。そして、それは毎回うまくいきました。)
このルートを選択する場合は、必ず[詳細設定]ボタンを選択し、必要なすべてのオプションを選択して(それぞれを読んで)、古いデータベースから新しいデータベース-あなたが言及したデフォルトのように。しかし、Azureでいくつかのテストを実行します。たぶん、これは有効な解決策の1つであることに気付くでしょう-少しの努力で。
私はこれがbestソリューションであると思うので、Evan Carrollsの回答に賛成しています。多くのC#コードを変更する必要があることを同僚に納得させることができなかったため、David Spillettが作成したコードを使用する必要がありました。以下のように、UDF、動的SQL、およびスキーマ(すべてのコードが「dbo。」を使用するわけではない)に関するいくつかの問題を修正しました。
DECLARE C CURSOR LOCAL STATIC FOR
SELECT sm.definition, so.type
FROM sys.objects so
JOIN sys.all_sql_modules sm ON sm.object_id = so.object_id
WHERE so.type IN ('P', 'V')
AND CHARINDEX('getdate()', sm.definition) > 0
ORDER BY so.name
DECLARE @SQL NVARCHAR(MAX), @objtype NVARCHAR(MAX)
OPEN C
WHILE 1=1 BEGIN
FETCH NEXT FROM C INTO @SQL, @objtype
IF @@FETCH_STATUS <> 0 BREAK
IF @objtype = 'P' SET @SQL = REPLACE(@SQL, 'CREATE PROCEDURE', 'ALTER PROCEDURE')
IF @objtype = 'P' SET @SQL = REPLACE(@SQL, 'CREATE PROCEDURE', 'ALTER PROCEDURE') /* when you write "create or alter proc" */
IF @objtype = 'V' SET @SQL = REPLACE(@SQL, 'CREATE VIEW' , 'ALTER VIEW' )
IF CHARINDEX('getdate())''', @sql) > 0 BEGIN /* when dynamic SQL is used */
IF CHARINDEX('utl.getdate())''', @sql) = 0 SET @SQL = REPLACE(@SQL, 'GETDATE()', 'utl.getdate()')
end
ELSE begin
SET @SQL = REPLACE(@SQL, 'GETDATE()', 'CONVERT(DATETIME, CONVERT(datetimeoffset, SYSDATETIME()) AT TIME ZONE ''Central Europe Standard Time'')')
end
EXEC dbo.LongPrint @String = @sql
EXEC (@SQL)
END
CLOSE C
DEALLOCATE C
そして、このようなデフォルトの制約:
DECLARE C CURSOR LOCAL STATIC FOR
SELECT AlterDefaultSQL = 'ALTER TABLE [' +sch.name+ '].[' +st.name+ '] DROP CONSTRAINT [' + si.name + '];'
+ CHAR(10)
+ 'ALTER TABLE [' +sch.name+ '].[' +st.name+ '] ADD CONSTRAINT [' + si.name + '] DEFAULT '+REPLACE(si.definition, 'GETDATE()', 'CONVERT(DATETIME, CONVERT(datetimeoffset, SYSDATETIME()) AT TIME ZONE ''Central Europe Standard Time'')')+' FOR '+sc.name+';'
FROM sys.tables st
JOIN sys.default_constraints si ON si.parent_object_id = st.object_id
JOIN sys.columns sc ON sc.default_object_id = si.object_id
INNER JOIN sys.schemas sch ON sch.schema_id = st.schema_id
WHERE CHARINDEX('getdate()', si.definition) > 0
ORDER BY st.name, sc.name
DECLARE @SQL NVARCHAR(MAX)
OPEN C
WHILE 1=1 BEGIN
FETCH NEXT FROM C INTO @SQL
IF @@FETCH_STATUS <> 0 BREAK
EXEC dbo.LongPrint @String = @sql
EXEC (@SQL)
FETCH NEXT FROM C INTO @SQL
END
CLOSE C
DEALLOCATE C
DF
今日の日付と時刻を返すUDFを使用するという提案は良さそうですが、UDFにはまだ十分なパフォーマンスの問題があると思うので、非常に長くて醜いATタイムゾーンソリューション。
すべてのprocおよびudfを動的に変更して値を変更する
_ DECLARE @Text NVARCHAR(max),
@spname NVARCHAR(max),
@Type CHAR(5),
@Sql NVARCHAR(max)
DECLARE @getobject CURSOR
SET @getobject = CURSOR
FOR SELECT sc.text,
so.NAME,
so.type
FROM sys.syscomments sc
INNER JOIN sysobjects so
ON sc.id = so.id
WHERE sc.[text] LIKE '%getdate()%'
--and type in('P','FN')
OPEN @getobject
FETCH next FROM @getobject INTO @Text, @spname, @Type
WHILE @@FETCH_STATUS = 0
BEGIN
IF ( @Type = 'P'
OR @Type = 'FN' )
SET @Text = Replace(@Text, 'getdate', 'dbo.getlocaldate')
SET @Text = Replace(@Text, 'create', 'alter')
EXECUTE Sp_executesql
@Text
PRINT @Text
--,@spname,@Type
FETCH next FROM @getobject INTO @Text, @spname, @Type
END
CLOSE @getobject
DEALLOCATE @getobject
_
_ CREATE PROCEDURE [dbo].[Testproc1]
AS
SET nocount ON;
BEGIN
DECLARE @CurDate DATETIME = Getdate()
END
_
コメントされた通知 sysobjects Type column condition.MyスクリプトはprocとUDFのみを変更します。
このスクリプトは、GetDate()
を含むすべての_Default Constraint
_を変更します
_ DECLARE @TableName VARCHAR(300),
@constraintName VARCHAR(300),
@colName VARCHAR(300),
@Sql NVARCHAR(max)
DECLARE @getobject CURSOR
SET @getobject = CURSOR
FOR SELECT ds.NAME,
sc.NAME AS colName,
so.NAME AS Tablename
--,ds.definition
FROM sys.default_constraints ds
INNER JOIN sys.columns sc
ON ds.object_id = sc.default_object_id
INNER JOIN sys.objects so
ON so.object_id = ds.parent_object_id
WHERE definition LIKE '%getdate()%'
OPEN @getobject
FETCH next FROM @getobject INTO @constraintName, @colName, @TableName
WHILE @@FETCH_STATUS = 0
BEGIN
SET @Sql = 'ALTER TABLE ' + @TableName
+ ' DROP CONSTRAINT ' + @constraintName + '; '
+ Char(13) + Char(10) + ' ' + Char(13) + Char(10) + ''
SET @Sql = @Sql + ' ALTER TABLE ' + @TableName
+ ' ADD CONSTRAINT ' + @constraintName
+ ' DEFAULT dbo.GetLocaledate() FOR '
+ @colName + ';' + Char(13) + Char(10) + ' ' + Char(13)
+ Char(10) + ''
PRINT @Sql
EXECUTE sys.Sp_executesql
@Sql
--,@spname,@Type
FETCH next FROM @getobject INTO @constraintName, @colName, @TableName
END
CLOSE @getobject
DEALLOCATE @getobject
_