Files
workspace/code/fms/sql/fms_time_utc_migration.sql
T
2026-09-27 21:59:13 +08:00

124 lines
6.3 KiB
Transact-SQL
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* ============================================================================
时间口径迁移:本地墙钟(服务器时间)→ UTC 墙钟
----------------------------------------------------------------------------
背景:本项目原先全链路使用「服务器本地墙钟值」(库端 getdate() / sysdatetime(),
应用层 LocalDateTime.now(Asia/Shanghai))。现统一为 UTC 口径(见后端 TimeUtils):
· 库中 datetime 列一律存 UTC 墙钟值,新写入默认值走 sysutcdatetime();
· 接口出口发带 Z 的 ISO 串("2026-09-26T02:00:00Z"),前端按浏览器时区展示;
· 提交时前端带 Z / ±HH:mm,后端折回 UTC 墙钟值落库。
本脚本做两件事:
1. 默认值约束:getdate() / sysdatetime() → sysutcdatetime();
2. 存量数据:把每个 datetime 列的旧值从「来源时区墙钟」折成 UTC。
幂等:执行成功后在 dbo.s_time_utc_migrated 留标记,重复执行直接跳过
(校正类脚本重复执行会二次偏移,必须防重)。
执行范围:每个机构库各执行一次(后端按机构切换连接库)。
时区前提:旧数据是「中国标准时间(UTC+8)」的墙钟值 —— 现网服务器与使用方均在中国。
若某机构库的旧值来自别的时区,把 @source_zone 换成对应 Windows 时区名即可。
依赖:AT TIME ZONE 需要 SQL Server 2016+;建议在维护窗口执行(全表更新会持有行锁)。
============================================================================ */
set nocount on;
go
/* ---------------------------------------------------------------------------
0. 防重标记表(首次执行时创建)
--------------------------------------------------------------------------- */
if object_id(N'dbo.s_time_utc_migrated', N'U') is null
begin
create table dbo.s_time_utc_migrated (
b_id varchar(50) not null primary key, -- 迁移标识
b_source_zone sysname not null, -- 旧值的来源时区
b_migrated_at datetime2 not null, -- 执行时间(UTC)
b_column_count int not null -- 校正的列数
);
end
go
declare @source_zone sysname = N'China Standard Time';
declare @already int = (select count(*) from dbo.s_time_utc_migrated where b_id = 'time-utc');
if @already > 0
begin
print N'[跳过] 时间口径迁移已执行过(见 dbo.s_time_utc_migrated 的 time-utc 标记)。';
end
else
begin
declare @table sysname, @column sysname, @type sysname, @sql nvarchar(max);
/* -----------------------------------------------------------------------
1. 默认值约束:getdate() / sysdatetime() → sysutcdatetime()
----------------------------------------------------------------------- */
declare @constraint sysname;
declare defaults_cursor cursor local fast_forward for
select dc.name, t.name, c.name
from sys.default_constraints dc
join sys.tables t on t.object_id = dc.parent_object_id
join sys.columns c on c.object_id = dc.parent_object_id and c.column_id = dc.parent_column_id
where t.is_ms_shipped = 0
and (dc.definition like '%getdate()%' or dc.definition like '%sysdatetime()%')
and dc.definition not like '%sysutcdatetime%';
open defaults_cursor;
fetch next from defaults_cursor into @constraint, @table, @column;
while @@fetch_status = 0
begin
set @sql = N'alter table dbo.' + quotename(@table)
+ N' drop constraint ' + quotename(@constraint) + N';'
+ N'alter table dbo.' + quotename(@table)
+ N' add constraint ' + quotename(@constraint)
+ N' default sysutcdatetime() for ' + quotename(@column) + N';';
execute sp_executesql @sql;
print N'[默认值] ' + @table + N'.' + @column + N' → sysutcdatetime()';
fetch next from defaults_cursor into @constraint, @table, @column;
end
close defaults_cursor;
deallocate defaults_cursor;
/* -----------------------------------------------------------------------
2. 存量数据:每个 datetime 列从「@source_zone 墙钟」折成 UTC
AT TIME ZONE 先给旧值贴上来源时区,再换算到 UTC —— 不写死 -8,
将来某个库的旧值来自其他时区时改 @source_zone 即可。
----------------------------------------------------------------------- */
declare @column_count int = 0;
declare time_columns cursor local fast_forward for
select t.name, c.name, ty.name
from sys.columns c
join sys.tables t on t.object_id = c.object_id
join sys.schemas s on s.schema_id = t.schema_id
join sys.types ty on ty.user_type_id = c.user_type_id
where s.name = 'dbo'
and t.is_ms_shipped = 0
and c.is_computed = 0
and t.name <> 's_time_utc_migrated'
and ty.name in ('datetime', 'datetime2', 'smalldatetime')
order by t.name, c.column_id;
open time_columns;
fetch next from time_columns into @table, @column, @type;
while @@fetch_status = 0
begin
set @sql = N'update dbo.' + quotename(@table)
+ N' set ' + quotename(@column)
+ N' = cast(((' + quotename(@column) + N' at time zone @zone) at time zone ''UTC'') as '
+ @type + N')'
+ N' where ' + quotename(@column) + N' is not null;';
execute sp_executesql @sql, N'@zone sysname', @zone = @source_zone;
set @column_count = @column_count + 1;
fetch next from time_columns into @table, @column, @type;
end
close time_columns;
deallocate time_columns;
/* -----------------------------------------------------------------------
3. 留痕
----------------------------------------------------------------------- */
insert into dbo.s_time_utc_migrated (b_id, b_source_zone, b_migrated_at, b_column_count)
values ('time-utc', @source_zone, sysutcdatetime(), @column_count);
print N'[完成] 已按 ' + @source_zone + N' 校正 ' + cast(@column_count as varchar(10))
+ N' 个时间列;抽查 b_created_at / b_occurdatetime 与界面展示是否一致。';
end
go