在整个数据库中更改 GETDATE() 的使用

Lam*_*mak 27 sql-server azure-sql-database sql-server-2017

我需要将本地 SQL Server 2017 数据库迁移到 Azure SQL 数据库,但我面临着一些挑战,因为要克服很多限制。

特别是,由于 Azure SQL 数据库仅在 UTC 时间(无时区)下工作,而我们需要本地时间,因此我们必须更改数据库中GETDATE() 各处的使用,事实证明,这比我预期的要多。

我创建了一个用户定义的函数来获取适合我的时区的本地时间:

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
Run Code Online (Sandbox Code Playgroud)

我遇到的问题是GETDATE()在每个视图、存储过程、计算列、默认值、其他约束等中实际更改此函数。

实施此更改的最佳方法是什么?

我们正处于托管实例的公共预览版中。它仍然有同样的问题GETDATE(),所以它对这个问题没有帮助。迁移到 Azure 是一项要求。这个数据库总是在这个时区使用(并将被使用)。

AMG*_*AMG 17

  1. 使用 SQL Server 工具将数据库对象定义导出到 SQL 文件,其中应包括:表、视图、触发器、SP、函数等

  2. 使用任何允许您查找文本"GETDATE()"并将其替换为的文本编辑器编辑 SQL 文件(首先进行备份)"[dbo].[getlocaldate]()"

  3. 在 Azure SQL 中运行编辑的 SQL 文件以创建数据库对象...

  4. 执行数据迁移。

您可以在此处获得 azure 文档中的参考:Generating Scripts for SQL Azure


Eva*_*oll 15

实施此更改的最佳方法是什么?

我会反过来工作。将数据库中的所有时间戳转换为 UTC,只需使用 UTC 即可。如果您需要不同 tz 中的时间戳,您可以使用AT TIME ZONE(如上所述)创建一个生成的列,该列在指定的 TZ(对于应用程序)中呈现时间戳。但是,我会认真考虑让 UTC 返回到应用程序,并在应用程序中编写该逻辑 - 显示逻辑。

  • 你有什么保证“应用程序和软件”都没有使用 getdate() ?即嵌入在应用程序中的 sql 代码。如果不能保证,重构数据库以使用不同的函数只会导致不一致。 (5认同)

Dav*_*ett 6

与其导出、手动编辑和重新运行,您还可以尝试直接在数据库中执行以下操作:

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
Run Code Online (Sandbox Code Playgroud)

当然扩展它来处理函数、触发器等等。

有几个注意事项:

  • 您可能需要有点明亮和处理之间的不同/额外的空格CREATEPROCEDURE/ VIEW/ <other>。而不是REPLACE为您可能更愿意代替离开CREATE的地方,执行DROP第一,但这个风险让sys.depends朋友外出失衡的地方ALTER可能没有,也如果ALTER失败,你至少可以替代现有的对象仍然在那里与DROP+CREATE你可能不是。

  • 如果你的代码有任何“聪明”的味道,比如用临时 TSQL 修改自己的架构,那么你需要确保搜索和替换CREATE->ALTER不会干扰它。

  • 无论您使用游标还是导出+编辑+运行方法,您都希望在操作后对整个应用程序进行回归测试。

我过去曾使用这种方法进行类似的架构范围更新。这有点黑客,感觉很丑陋,但有时它是最简单/最快的方法。

默认值和其他约束也可以类似地修改,尽管它们只能删除和重新创建而不是更改。就像是:

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
Run Code Online (Sandbox Code Playgroud)

您可能需要应对一些更有趣的事情:如果您按时间进行分区,那么这些部分也可能需要更改。虽然按时间分区比按天分区的情况很少见,但您可能会遇到问题,DATETIME分区函数将 s 解释为前一天或下一天,具体取决于 timezine,使您的分区与您通常的查询不一致。


Sti*_*ing 5

我真的很喜欢大卫的回答,并赞成以程序化的方式做事。

但是,您今天可以尝试通过 SSMS 在 Azure 中进行测试:

右键单击您的数据库 --> 任务 --> 生成脚本..

[背景故事] 我们有一个初级 DBA,他将我们所有的测试环境升级到 SQL 2008 R2,而我们的生产环境是 SQL 2008。这一变化让我至今都感到畏缩。为了从测试迁移到生产,我们必须在 SQL 中生成脚本,使用生成脚本,在高级选项中,我们使用“数据类型到脚本:架构和数据”选项来生成大量文本文件。我们成功地将我们的测试 R2 数据库移动到了我们的旧版 SQL 2008 服务器——在那里将数据库还原到较低版本是行不通的。我们使用 sqlcmd 来输入大文件——因为这些文件对于 SSMS 文本缓冲区来说通常太大了。

我在这里要说的是,此选项可能也适用于您。您只需要执行一个额外的步骤并在生成的文本文件中搜索并用 [dbo].getlocaldate 替换 getdate()。(不过,我会在迁移之前将您的函数放入数据库中)。

(我从不想精通数据库恢复的这种创可贴,但有一段时间它成为了一种事实上的做事方式。而且,它每次都有效。)

如果您选择这条路线,请确保选择“高级”按钮并选择您需要的所有选项(阅读每个选项)以从旧数据库移动到新数据库——就像您提到的默认设置一样。但是请在 Azure 中对其进行一些测试。我敢打赌,您会发现这是一种行之有效的解决方案——只需稍加努力。

在此处输入图片说明