【问题标题】:Dynamic T-SQL - Alter column definition to max length of field动态 T-SQL - 将列定义更改为字段的最大长度
【发布时间】:2014-11-21 15:05:11
【问题描述】:

我正在尝试动态创建代码以将列定义更改为字段的最大长度。

注意数据库的内容不会改变。

这是我目前所拥有的,但我无法单独执行最大长度查询来获取数字。我哪里错了?

问候。

DECLARE @SQL_STMT VARCHAR(MAX) = ''

SELECT @SQL_STMT = @SQL_STMT
    + '''ALTER TABLE '
    + TABLE_NAME
    + ' ALTER COLUMN '
    + COLUMN_NAME
    + ' '
    + DATA_TYPE
    + '('' SELECT MAX(LEN('
    + COLUMN_NAME
    + ')) FROM '
    + TABLE_NAME
    + ''') ''
    '
FROM INFORMATION_SCHEMA.COLUMNS
WHERE DATA_TYPE = 'varchar'

PRINT(@SQL_STMT)

【问题讨论】:

  • 您想将 varchar 列更改为多长 ???这真的不清楚你到底想做什么??
  • 最多为单个字段的最大长度。例如 select max(len(empname)) from emp
  • 你到底为什么要这样做?这可能会削弱使用此数据库的应用程序。您可以开始截断数据错误,因为前端有验证。您不会从中获得任何好处,因为您只是在更改 varchar 列。更不用说您可能会出错,因为您使用的是不考虑尾随空格的 LEN。
  • 在这种情况下,您应该使用DATALENGTH 而不是LEN,因为正如@SeanLange 所提到的,LEN 将排除尾随空格。

标签: sql-server dynamic-sql alter-column


【解决方案1】:

编辑我的第一个版本不够完善,这是我测试过的新版本:

DECLARE @SQL_STMT VARCHAR(MAX) = 'DECLARE @query nvarchar(max);'

SELECT @SQL_STMT = @SQL_STMT
    + 'SET @query =''ALTER TABLE '
    + TABLE_NAME
    + ' ALTER COLUMN '
    + COLUMN_NAME
    + ' '
    + DATA_TYPE
    + '('' +CAST((SELECT MAX(DATALENGTH('
    + COLUMN_NAME
    + ')) FROM '
    + TABLE_NAME
    + ') as nvarchar(max))+'') ''
    exec(@query);'
FROM INFORMATION_SCHEMA.COLUMNS
WHERE DATA_TYPE = 'nvarchar'

PRINT(@SQL_STMT)

【讨论】:

  • 我的第一个版本不起作用(我没有沙盒数据库可以尝试),但我已经用功能解决方案编辑了我的答案。
  • 干杯伙伴!虽然我会像@DrCopyPaste 所说的那样使用数据长度来代替
  • 这确实是一个很好的建议,为了完整起见,我将编辑我的答案。
【解决方案2】:

我不是这个想法的朋友,但这应该可以满足您的需求:

DECLARE @SQL_STMT VARCHAR(MAX) = ''

SELECT 
    IDENTITY(int,1,1) as ID, -- for later update   
     'ALTER TABLE '
    + c.TABLE_NAME
    + ' ALTER COLUMN '
    + c.COLUMN_NAME
    + ' '
    + c.DATA_TYPE
    + '('  P1

    , '  MAX(LEN('
    + c.COLUMN_NAME
    + ')'
    +') FROM '
    + c.TABLE_NAME P2

    , ') ' P3
into #tmp    
FROM INFORMATION_SCHEMA.Tables t 
JOIN INFORMATION_SCHEMA.Columns c 
on    c.TABLE_CATALOG= t.TABLE_CATALOG 
  and c.TABLE_SCHEMA=t.TABLE_SCHEMA
  and c.TABLE_NAME=t.TABLE_NAME
where TABLE_TYPE='BASE TABLE' -- only Tables not views
and DATA_TYPE = 'varchar'

Select @SQL_STMT=''  -- collect ID + Max info e.g. SELECT 1,   MAX(LEN(FirstName)) FROM user_info 
Select @SQL_STMT=@SQL_STMT + 'SELECT '+ CAST(ID as varchar(10)) + ', '+ P2 +CHAR(13) 
from #tmp

Declare @a table (ID Integer,Size Integer) -- table for ID and Len
--print @SQL_STMT

insert into @a Exec (@SQL_STMT)  -- fill table by executing block

                     -- define a minimum size if 0  
Update #tmp set P2 = Case When Size<1 then 1 else Size end  -- update p2
From @a a where a.ID = #tmp.ID

Select @SQL_STMT=''
Select @SQL_STMT=@SQL_STMT + P1 + P2 + P3 +CHAR(13) 
from #Tmp

print @SQL_STMT

Exec(@SQL_STMT)


Drop table #tmp

【讨论】:

    【解决方案3】:

    您不能使用SELECT 语句或整数变量作为ALTER TABLE 语句中列的大小。作为一般规则,您不能混合使用 DDL(数据描述语言,例如,CREATEALTERDROP)和 DML(数据操作语言,例如,SELECTINSERTUPDATE ) 在同一个语句中。

    这意味着您必须使用动态 SQL 才能完成任务。您将无法使用动态 SQL 来构建可以保存和重用的静态 SQL。您需要检索字段的最大长度并将其保存到变量中,然后构造ALTER 语句,最后构造EXEC () ALTER 语句。最简单的方法可能是通过INFORMATION_SCHEMA.COLUMNS 使用游标,但您也可以使用双重动态SQL。即,动态 SQL 本身会生成动态 SQL。然而,这更难调试。

    除此之外,我质疑您尝试做的事情的有效性。简而言之,您永远不需要使用此代码。数据库模式应该是相对固定的,因为改变它会对性能产生重大影响。通过更改列的大小,您是在告诉数据库引擎它需要更改它在磁盘上物理存储数据的方式,而这不是您应该轻易或经常做的事情。只需将值设置得足够大以适应用户需求,您就会走得更远。现代 RDBMS 在存储数据效率方面要好得多,但经常更改列大小仍然是不好的做法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-09-13
      • 1970-01-01
      • 2012-02-08
      • 1970-01-01
      • 2018-02-28
      • 1970-01-01
      • 2019-08-03
      • 1970-01-01
      相关资源
      最近更新 更多