【问题标题】:T-SQL: Convert datatime2 to datetime for all columns of type datetime2T-SQL:将 datetime2 转换为 datetime2 类型的所有列的 datetime2
【发布时间】:2009-09-18 20:48:51
【问题描述】:

我有一个充满 datetime2 列的数据库,需要将其移至 SQL 2005 数据库。所以,我需要将所有这些 datetime2(7) 列转换为 datetime。

我该怎么做?

现在我已经设法为所有具有 datetime2 数据类型的列选择表名和列名,如下所示:

SELECT t.name, c.name, i.DATA_TYPE
FROM sys.tables AS t
JOIN sys.columns c ON t.object_id = c.object_id
JOIN information_schema.columns i ON i.TABLE_NAME = t.name AND i.COLUMN_NAME = c.name
WHERE i.data_type = 'datetime2'

我只是不知道该怎么做。

【问题讨论】:

  • 既然所有信息都可以从 information_schema.columns 获得,为什么还要加入三个表?
  • @van,@David。您可能需要检查 sys.columns 中的 is_nullable 列以验证 ALTER COLUMN 中是否需要 NOT NULL

标签: sql datetime alert datetime2


【解决方案1】:

...然后您使用 CURSOR 迭代您的结果并动态运行 DDL,如下所示:

ALTER TABLE myTable ALTER COLUMN myColumn datetime [NOT] NULL

这样你就会得到类似的东西(未经测试):

编辑:还添加了可空性检查:

DECLARE @SQL AS NVARCHAR(1024)
DECLARE @TBL AS NVARCHAR(255)
DECLARE @COL AS NVARCHAR(255)
DECLARE @NUL AS BIT
DECLARE CUR CURSOR FAST_FORWARD FOR
    SELECT  t.name, c.name, c.is_nullable
    FROM    sys.tables AS t
    JOIN    sys.columns c ON t.object_id = c.object_id
    JOIN    information_schema.columns i ON i.TABLE_NAME = t.name AND i.COLUMN_NAME = c.name
    WHERE   i.data_type = 'datetime2'
    ORDER BY t.name, c.name

OPEN CUR
FETCH NEXT FROM CUR INTO @TBL, @COL, @NUL
WHILE @@FETCH_STATUS = 0
BEGIN
    SELECT @SQL = 'ALTER TABLE ' + @TBL + ' ALTER COLUMN ' + @COL + ' datetime' + (CASE WHEN @NUL=1 THEN '' ELSE ' NOT' END) + ' NULL;'
    EXEC sp_executesql @SQL
    FETCH NEXT FROM CUR INTO @TBL, @COL, @NUL
END

CLOSE CUR;
DEALLOCATE CUR;

【讨论】:

  • 对不起,我真的不太懂SQL。可以扩展吗?
  • 在答案中添加了代码。基本上你需要改变列的类型,对吧?您可以使用 ALTER TABLE ... ALTER COLUMN ... 语法来做到这一点。在 cursor 的帮助下,您可以对在某种循环中找到的所有列执行。您可以按照 Lucasz 的说明添加列可空性检查,并将 NOT NULL 添加到语句中。
  • 感谢您的帮助!我认为它可能会奏效。但我还有另一个问题,因为其中许多列都有约束/触发器。我收到一条错误消息,指出“对象 'DF_CreatedDate' 依赖于列 'CreatedDate'。
  • 在这种情况下,您可能会生成另一个动态 SQL 并在 ALTER COLUMN... 之前执行它: ALTER TABLE xxx NOCHECK CONSTRAINT DR_CreatedDate... 或类似的东西。但是对于这样的情况,您可能想查看此约束的代码是什么,如果它类似于 DEFAULT (GETDATE()),那么您可以先将其删除,然后更改数据类型,然后将其添加回来。
【解决方案2】:

我知道这个帖子很旧,但我今天也在做同样的事情,只是想提供我的技术。每当我需要执行大量 DDL 语句时,我都会创建一个生成所需 TSQL 的 TSQL,然后将结果复制到查询窗口并运行它。您不需要像@van 建议那样编写所有游标代码(尽管效果很好)。

因此,对于您的情况,只需运行 sql 语句:

select 'ALTER TABLE ' + table_name + ' ALTER COLUMN ' + column_name + ' datetime [NOT] NULL' 
from INFORMATION_SCHEMA.columns 
where data_type = 'datetime2(7)'.

然后,将结果复制到一个新的查询窗口并运行它。有时您需要在命令之间的自己的行中添加"GO" 语句。如果是这样,请将char(13) + 'GO' 添加到您的输出字符串中。

另外,请确保在 SQL Mgmt Studio 中使用“结果到文本”选项而不是“结果到网格”选项运行查询。

【讨论】:

  • 只是最后的跟进,我正在将我所有的日期时间列转换为 datetime2(7) 所以这是我使用的 SQL:select 'ALTER TABLE [' + t.table_name + '] ALTER COLUMN [' + column_name + '] datetime2(7) ' + (case when is_nullable = 'NO' then 'NOT' else '' end) + ' NULL' from INFORMATION_SCHEMA.columns c inner join information_schema.TABLES t on c.TABLE_NAME = t.table_name where data_type = 'datetime' and t.TABLE_TYPE = 'base table' order by t.table_name
【解决方案3】:

改进了上述答案以适应模式

DECLARE @SQL AS NVARCHAR(1024)
DECLARE @TBL AS NVARCHAR(255)
DECLARE @COL AS NVARCHAR(255)
DECLARE @SCH AS NVARCHAR(255)
DECLARE @NUL AS BIT
DECLARE CUR CURSOR FAST_FORWARD FOR
    SELECT  t.name AS TableName, c.name ColumnName, s.name AS SchemaName, c.is_nullable
    FROM    sys.tables AS t
    JOIN    sys.columns c ON t.object_id = c.object_id
    JOIN    information_schema.columns AS i ON i.TABLE_NAME = t.name AND i.COLUMN_NAME = c.name
    JOIN    sys.schemas AS s on t.schema_id = s.schema_id
    WHERE   i.data_type = 'datetime2'    
    ORDER BY t.name, c.name

OPEN CUR
FETCH NEXT FROM CUR INTO @TBL, @COL, @SCH, @NUL
WHILE @@FETCH_STATUS = 0
BEGIN
    SELECT @SQL = 'ALTER TABLE ['+@SCH+'].[' + @TBL + '] ALTER COLUMN [' + @COL + '] datetime' + (CASE WHEN @NUL=1 THEN '' ELSE ' NOT' END) + ' NULL;'
    EXEC sp_executesql @SQL
    FETCH NEXT FROM CUR INTO @TBL, @COL,@SCH, @NUL
END

CLOSE CUR;
DEALLOCATE CUR;

【讨论】:

  • 希望我能早点看到这个答案,实际上只是写了完全相同的 SQL(除了我使用了本地游标)。值得注意的是,这个答案还解释了带有空格/特殊字符的表名和列名,而接受的则没有
【解决方案4】:

今天需要为架构中的所有用户表执行此操作,并且对任何现有答案都不满意。特别是,我的一些日期时间列有默认值,实际上没有人需要,但阻碍了 ALTER TABLE 命令。所以我写了一个脚本,只删除那些默认值,然后更改列。它保留了可空性,并且可以处理包含空格、连字符等的名称。注意,它不会在之后重新创建默认值。

如果您处于同样的情况,您可以使用这个稳定且经过测试的脚本,它还可以确保不会对用于构成 DDL 语句的 nvarchar(max) 变量进行静默截断:

DECLARE @sql AS nvarchar(max)=N''

--1. "ALTER TABLE [Tablename] DROP CONSTRAINT [DF__Tablename__Colname__Obfuscation]"
SELECT @sql=CAST('' AS nvarchar(MAX))+@sql 
  +N'ALTER TABLE ['+o.[name]+N'] DROP CONSTRAINT ['+co.[name]+']' 
FROM sysconstraints c 
INNER JOIN sysobjects o ON o.[id]=c.[id] 
INNER JOIN syscolumns col ON col.[id]=o.[id] AND col.colid=c.colid
INNER JOIN sysobjects co ON co.[id]=c.constid 
WHERE col.xtype=61 --datetime

EXEC sp_executesql @sql

--2. change type of all datetime columns
SELECT @sql=N''
SELECT @sql=CAST('' AS nvarchar(MAX))+@sql 
  +N'ALTER TABLE [' 
  +convert(nvarchar(max),t.name)
  +N'] ALTER COLUMN [' 
  +convert(nvarchar(max),c.name)
  +N'] datetime2 ' 
  +CASE WHEN c.is_nullable = 1 THEN N'' ELSE N'NOT' END
  +N' NULL;'+convert(nvarchar(max),char(13)+char(10))
FROM sys.tables t 
INNER JOIN sys.columns c ON t.object_id = c.object_id 
INNER JOIN sys.types st ON st.system_type_id = c.system_type_id
WHERE st.name=N'datetime'
AND t.xtype=N'U' --user tables only
ORDER BY t.[name]

EXEC sp_executesql @sql

它使用古老的语法和架构表,因此它从 SQL Server 2008 版(这是第一个支持 datetime2)到 2016 年都可以使用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-05-26
    • 2022-12-02
    • 1970-01-01
    • 2019-04-16
    • 2011-07-31
    • 1970-01-01
    • 1970-01-01
    • 2014-09-12
    相关资源
    最近更新 更多