【问题标题】:SQL Query to Update Existing DateTime field and values in DB to PST from GMTSQL 查询将 DB 中的现有 DateTime 字段和值从 GMT 更新为 PST
【发布时间】:2015-03-25 21:57:00
【问题描述】:

我正在寻找编写查询的最佳方法,该查询采用数据库中现有的 DateTime 字段/值(有超过 50K 行)并将 DateTime 值更新到不同的时区。我想要做的是将现有的日期/时间值从 GMT 时间(当前服务器时间)更改为 PST(我认为是负 7 小时)。我想更改数据库中的值,然后将服务器上的服务器时间更改为 PST 以反映时区,以便所有新记录都显示 PST 时区。

我们将不胜感激,提前感谢您的宝贵时间。

【问题讨论】:

    标签: sql sql-server database datetime


    【解决方案1】:

    您可能根本不应该这样做。将您的数据以 GMT(实际上是 UTC)保存在数据库中,并根据需要在应用程序逻辑中将其转换为太平洋时间。例如,如果您从 .NET 应用程序连接到 SQL Server,请从数据库中查询 UTC 时间并使用 TimeZoneInfo.ConvertTimeFromUtc 进行转换

    最大的原因是太平洋时间使用daylight saving time。因此,虽然一年中的部分时间它遵循 PST(即 UTC-8),但在夏季它遵循 PDT(即 UTC-7)。这意味着调整的时间量是可变的,并且取决于日期本身。

    不仅如此,在回退转换期间出现的值在本地时间是不明确的。例如,2015 年 11 月 1 日凌晨 1:30 在太平洋时间出现两次。如果您将所有数据转换为太平洋时间,您可能会丢失一些信息。

    如果您真的需要直接在 SQL Server 中转换时区,请考虑使用我的SQL Server Time Zone Support 项目。例如:

    SELECT Tzdb.UtcToLocal('2015-07-01 00:00:00', 'America/Los_Angeles')
    

    【讨论】:

      【解决方案2】:

      很遗憾,SQL Server 没有任何内置的时区转换功能。但是,这可以通过 SQLCLR 完成。您可以使用DateTime.ToLocalTime() 方法创建标量用户定义函数。

      是的,从技术上讲,您可以使用具有 ConvertTime(DateTime, TimeZoneInfo, TimeZoneInfo)ConvertTimeFromUtc(DateTime, TimeZoneInfo) 等方法的 TimeZoneInfo 类,但 TimeZoneInfo 类要求程序集具有 PERMISSION_SETUNSAFE,而 DateTime可以以SAFE 运行。坚持使用DateTime 方法的唯一缺点是它们只能在UTC 和服务器的本地时间之间转换,而TimeZoneInfo 可以在任何两个给定时区之间转换。

      由于您的数据已经采用 GMT / UTC,使用DateTime.ToLocalTime 转换为太平洋* 时间会相当容易,只有在您更改服务器的时区之前 更新数据,而不是之后(如问题中所述)。此时您可以执行以下操作:

      ALTER TABLE [SchemaName].[TableName]
        ADD [IsConverted] BIT NULL; -- make sure we don't convert multiple times
      
      WHILE (1 = 1)
      BEGIN
        UPDATE TOP (2000) tab
        SET    tab.DateField = dbo.ConvertTimeToLocalTime(tab.DateField),
               tab.IsConverted = 1
        FROM   [SchemaName].[TableName] tab
        WHERE  tab.IsConverted IS NULL;
      
        IF (@@ROWCOUNT = 0)
        BEGIN
          BREAK;
        END;
      END;
      
      -- Remove the temporary tracking field, but not until all rows are converted
      IF (NOT EXISTS(
                     SELECT *
                     FROM   [SchemaName].[TableName] tab
                     WHERE  tab.IsConverted IS NULL
                    )
          )
      BEGIN
        ALTER TABLE [SchemaName].[TableName]
          DROP COLUMN [IsConverted];
      END;
      

      * 请注意,我使用“太平洋”时间而不是 PST,因为太平洋时间可以是 PST 或 PDT,具体取决于夏令时。因此,尝试通过简单地执行 DATEADD(HOUR, -7, [DateField]) 进行转换对于某些行是正确的,但不是所有行。

      另外,对于那些对这个特殊转换函数感兴趣的人,它可以在SQL# 库的完整版(我是它的作者)中获得(虽然不是免费的)。周围有一些博客文章展示了如何做到这一点,尽管我还没有看到一个遵循最佳实践的博客文章,以便它能够以最佳方式运行。当然,如果这是一次性转换,那么对于价格而言,最佳性能显然不如低效率但免费重要:-)。

      【讨论】:

        【解决方案3】:

        您可以使用 ALTER DATABASE 语句的 SET TIME_ZONE 子句更改数据库时区。例如:

        ALTER DATABASE SET TIME_ZONE='Europe/London';ALTER DATABASE SET TIME_ZONE='-05:00';
        

        您可以参考以下链接了解更多详情。 http://docs.oracle.com/cd/E11882_01/server.112/e10729/ch4datetime.htm#NLSPG263

        【讨论】:

        • 我认为您的解决方案更像是数据库时区解决方案,但它不会将现有的日期/时间数据库值更改为 GMT 的 PST 时区。
        • @thenextmogul 除了你所说的它是一个数据库级别设置,而不是数据本身,它也是一个 Oracle 选项,不是一个 SQL Server 选项。
        猜你喜欢
        • 2011-08-08
        • 1970-01-01
        • 1970-01-01
        • 2012-04-27
        • 1970-01-01
        • 1970-01-01
        • 2019-11-10
        • 2017-02-05
        • 2012-11-01
        相关资源
        最近更新 更多