【问题标题】:MSSQL: alter column not null, in generalMSSQL:一般来说,alter column not null
【发布时间】:2013-12-26 17:49:05
【问题描述】:

在 Microsoft SQL Server 2008 R2 中,我想将可为空的列更改为不为空。显然,我可以通过重述数据类型来做到这一点,如

alter table t alter column c int not null

例如,如果列 t.c 是 int 数据类型。但是一般情况下,如果不重新说明现有数据类型呢?我正在寻找一些等价的

alter table t alter column c not null

现有数据类型保留在原位,并且只有可空性被关闭。

背景

我对我的数据库进行了审计,发现很多情况下,列被指定为可为空,但实际上没有出现空值。我想收紧架构以禁止这些列中的空值。手动将 DDL 写入每个“更改列”很容易出错,因为我可能会弄错数据类型。我可以使用模式转储程序自动生成代码,该程序输出每列的现有数据类型,但这也有风险,如果转储程序不知道最新的数据类型并输出其他内容(例如,假设它不知道 datetime2 而是写出 datetime)。

SQL 服务器已经知道列类型是什么,所以肯定有办法告诉它保留它并关闭可空性。

如果除了找到现有的数据类型将其放入 DDL 之外真的没有其他办法,也许您可​​以推荐一个合适的工具来使用?我从 Sybase 时代就知道 dbschema.pl,但可能有更现代的东西,或者一些众所周知的 SQL 片段,可以从模式视图中打印出必要的语句。

【问题讨论】:

  • 想象一下,如果产品必须处理您可能想要“只是”调整的所有不同的、单一的功能 - 即“如果我只想指定数据类型,为什么我必须指定数据类型?更改长度/比例/精度/排序规则/等” - 对您来说,“只是”调整可空性是您想要做的,但就 SQL 而言,它是数据类型的固有部分,如果您想要更改类型,您必须完全重新指定它。
  • @Damien_The_Unbeliever:我总体上同意你的评论。然而,SQL Server 是少数有此要求的 DBMS 之一。在 Postgres、Oracle、DB2、MySQL 和其他几个中,如果类型本身没有改变,则不需要重复数据类型 - 所以这个问题有点可以理解。
  • 您可以使用Information Schema Views 来自动化一些工作,例如编写 TSQL 脚本以供审核。
  • @HABO:确实。困难的部分是找到将信息模式视图反向工程回正确语法'varchar(50)collat​​e database_default not null'等的SQL。除非视图中的某处隐藏了一个列,可让您直接获取此文本?

标签: sql-server types null alter-table alter-column


【解决方案1】:

两种方法:

1) 扩展 this answer 以包括考虑 max_length、precision、scale 和 collat​​ion_name。如果你有多个模式,你也需要适应它。

SELECT
  'ALTER TABLE '
    +QUOTENAME(aud.[table_name])
    +' ALTER COLUMN '
    +QUOTENAME(aud.[column_name])
    +TYPE_NAME([system_type_id])
    +' NOT NULL;'
FROM MyColumnAuditList aud
INNER JOIN sys.columns col ON (
  col.[object_id] = OBJECT_ID(aud.[table_name]) AND
  col.[name] = aud.[column_name]
)

2) 在 SSMS 中,右键单击数据库并选择“脚本数据库为”。使用您选择的文本解析工具从结果中提取列定义。

【讨论】:

    【解决方案2】:

    Anon 建议的“两种方法”答案很有帮助。该网站的评论框不允许足够的文字,所以我将在这里发布我的最终答案。

    linked answer 对用户数据类型有特殊规定,而我的数据库没有,所以我改用 type_name 内置函数。此查询尝试对每一列的类型进行反向工程:

    select t.name,
           c.name,
           case
             when type_name(c.system_type_id) in (
                 'int', 'real', 'float', 'date', 'time', 'datetime', 'datetime2',
                 'tinyint', 'smallint', 'smalldatetime', 'bit', 'bigint', 'timestamp',
                 'image'
               ) then type_name(c.system_type_id)
             else type_name(c.system_type_id) + '('
               + case
                   when precision = 0 then convert(varchar(10), c.max_length)
                   else convert(varchar(10), precision) + ', ' + convert(varchar(10), scale)
                 end
               + ')'
             end as ty
    from sys.tables t
    join sys.columns c
      on t.object_id = c.object_id
    where c.is_nullable = 1
    and   c.is_computed = 0
    and   t.schema_id = 1
    order by t.name,
             c.name
    

    然后,您可以从此查询中获取每一行,并在运行“alter table”之前检查是否存在空值。我正在做类似以下的事情:

    select case when
      exists (select 0 from TABLE)
      and not exists (select 0 from TABLE tablesample (1000 rows) where COLUMN is null)
    then 1 else 0 end
    

    对于每个 TABLE,第一个查询返回的 COLUMN。如果第二个查询返回 1,那么您可能可以更改“alter table”。我使用上面的 tablesample 来阻止这对数据库太重,因为我计划定期运行检查;如果 sp_spaceused 返回的表的大小小于 100 KB,则省略 tablesample 子句。

    或者,如果你觉得勇敢,你可以运行所有的“alter table”语句,如果列确实包含空值,则让它们失败。

    奇怪的是,我没有权限在数据库中右键单击 Management Studio 和“脚本数据库为”,尽管我可以为单个对象执行此操作。

    【讨论】:

      猜你喜欢
      • 2013-11-02
      • 2014-04-17
      • 2017-09-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-11-04
      • 1970-01-01
      • 2021-12-31
      相关资源
      最近更新 更多