Sql Server 2005 使用代码修改列的属性(存储过程)

最近很是郁闷,总公司要求把所有分公司的DB统一管理,以前都是各顾各的,什么添加字段修改数据类型都各做各的。现在要求统一很是麻烦,如果其中一个DB的某字段修改那么其他所有DB里面只要有这个表的都要改。

唉!以前很轻松的只要按需求点点鼠标就改一个DB就好的事情,现在要点60多个数据库去完成操作,而且还有很多DB不在一个SERVER上面,想我高升一向都是很懒的人,这种事情肯定是不愿意做的,可以这种事情低级DBA不做你要谁去做啊,于是一恨心放弃了一个宝贵的双休写了一个统一修改列属性的存储过程,使用代码更改列的数据类型,不知道算不算是远程修改了。以下示例将 AdventureWorks 数据的 Employee 表 Title 列的长度改成55。

ALTER   TABLE  HumanResources.Employee
ALTER   COLUMN  Title  nvarchar ( 55 )

ALTER TABLE  通过更改、添加或删除列和约束,重新分配分区,或者启用或禁用约束和触发器,从而修改表的定义。修改一个表的定义这个语法每个初学数据库的人都是知道的,不过很多情况下我们并不能直接修改,比如以下几种情况

  • 用于索引的列。
  • 用于 CHECK、FOREIGN KEY、UNIQUE 或 PRIMARY KEY 约束的列。
  • 与默认值(由 DEFAULT 关键字定义)相关联的列,或绑定到默认对象的列。
  • 绑定到规则的列。

在实际应用中,如果我们要修改的某一列上面建了索引或者建有约束的话,那个这条语句很可能是要报错的,当然这个和数据类型也有关系,具体的原理是什么我也不是很清楚。可以试一下将 Title 列加上一个索引,然后修改一下试试看。

-- 将 Title 列加上索引,这个只是为了测试一下加了索引后还能不能修改这一列的属性
CREATE   NONCLUSTERED   INDEX  IX_Employee  ON  HumanResources.Employee
 (
   Title
 ) 
WITH ( STATISTICS_NORECOMPUTE  =   OFF , IGNORE_DUP_KEY  =   OFF , ALLOW_ROW_LOCKS  =   ON , ALLOW_PAGE_LOCKS  =   ON ON   [ PRIMARY ]
GO
--加上索引后,还是刚才的修改语句,这里是60因为上一条已经改成55了
ALTER   TABLE  HumanResources.Employee
ALTER   COLUMN  Title  nvarchar (60 )
GO

现在执行这句话不出意外应该抱错了,如果上面有约束也是一样的会报错,不过这和数据类型有关,具体有哪些情况下会出现我也需要有人帮我解惑。

写点基本语法,多温习一下基础知识还是必要的,添加和删除默认约束,因为有时候用代码去修改列的时候有默认约束也是不能修改的,所以要删了重新建。CHECK 约束的建法差不多的不想写了。

--给 Title 列添加一个默认约束 '' 约束名 DF_Employee_Title
ALTER   TABLE  HumanResources.Employee  ADD   CONSTRAINT
DF_Employee_Title 
DEFAULT   ''   FOR  Title
--删除 Title 上的默认约束 DF_Employee_Title
ALTER   TABLE  HumanResources.Employee
DROP   CONSTRAINT  DF_Employee_Title

由于要修改的列可能已经加上了索引或者约束,这就为我现在使用代码修改列的属性带来了一定的麻烦,所以我才花两天时间写这个存储过程,其实关键代码就只有几句。我建到了msdb数据库下面,还加了密。

USE  msdb
GO
-- 建立人:  高升
--
建立日期:2007/06/16
--
修改日期:
--
功能目的:主要就是修改列的属性,不过是可以执行以后修改N多DB的,算不算远程修改列?
--
注意:    就我可以用,因为用的临时表太多了,汗!
CREATE   PROCEDURE  dbo.ModifyColumn
  
@dbName  sysname  =   '' ,         -- 要修改的数据库名,不写就是所以的数据库
   @tableName  sysname  =   '' ,     -- 要修改的表
   @columnName  sysname  =   '' ,     -- 要修改的列
   @type   varchar ( 2 ),             -- M修改A新增
   @dataType   nvarchar ( 128 =   '' ,     -- 数据类型
   @constraint   varchar ( 100 =   '' ,     -- 约束名,新增的时候写
   @is_exec   bit   =   0 ,             -- 是打印是生成的代码还是直接执行
   @custom   varchar ( max =   ''
WITH  ENCRYPTION
AS

IF  ( SUSER_SNAME ()  !=   ' GaoS ' )     -- 我的登陆名,sa都不能执行我的东西
   RAISERROR ( ' The user does not have permission to perform this action. ' , 16 , 1 )

DECLARE   @exec   varchar ( max )     -- 存放执行的语句
DECLARE   @name  sysname         -- 当前要执行的数据库名
DECLARE   @server_id   varchar ( 4 )     -- 那个Server
DECLARE   @exec1   varchar ( 100 )     -- 临时执行的语句
DECLARE   @exec2   varchar ( 100 )     --
DECLARE   @exec3   varchar ( 300 )     --
DECLARE   @constraintName   varchar ( 100 )     -- 默认约束的约束名
DECLARE   @definition   nvarchar ( 50 )         -- 默认值
DECLARE   @sno   smallInt          -- 用于循环

IF  ( @is_exec   <>   1 SET   @is_exec   =   0      -- 该值只能是0或1

IF   exists  ( SELECT   *   FROM  tempdb.sys.objects  WHERE  name  =   ' ##myDB ' )
BEGIN
  
PRINT  ( ' There is already an object named  '' ModifyColumn ''  in the database. ' )
  
RETURN ;
END
--这个表放DB的名字,以及DB所在的SERVER
CREATE   TABLE  ##myDB(sno  smallInt   identity ( 1 , 1 ),name sysname,server_id  tinyInt )

IF  ( @dbName   =   '' )     -- 查询所有要执行的DB,除去系统和临时DB
BEGIN
  
INSERT  ##myDB  select  name, 1   FROM  master.sys.databases
   
WHERE  database_id  >   4   and  name  not   like   ' %temp% '   ORDER   BY  name

  
INSERT  ##myDB  select  name, 2   FROM  SHDB2.master.sys.databases
   
WHERE  database_id  >   4   and  name  not   like   ' %temp% '   ORDER   BY  name

  
INSERT  ##myDB  select  name, 3   FROM  TWDB.master.sys.databases
   
WHERE  database_id  >   4   and  name  not   like   ' %temp% '   ORDER   BY  name

  
INSERT  ##myDB  select  name, 4   FROM  GZDB.master.sys.databases
   
WHERE  database_id  >   4   and  name  not   like   ' %temp% '   ORDER   BY  name
END
ELSE      -- 如果指定了要修改的数据库名
BEGIN
  
DECLARE   @getDBName   varchar ( 500 )
  
SET   @getDBName   =   ' INSERT ##myDB select name,1 FROM master.sys.databases
        WHERE name in (
''' + @dbName + ''' ) ORDER BY name '
  
EXEC  ( @getDBName )
  
SET   @getDBName   =   ' INSERT ##myDB select name,2 FROM SHDB2.master.sys.databases
        WHERE name in (
''' + @dbName + ''' ) ORDER BY name '
  
EXEC  ( @getDBName )
  
SET   @getDBName   =   ' INSERT ##myDB select name,3 FROM TWDB.master.sys.databases
        WHERE name in (
''' + @dbName + ''' ) ORDER BY name '
  
EXEC  ( @getDBName )
  
SET   @getDBName   =   ' INSERT ##myDB select name,4 FROM GZDB.master.sys.databases
        WHERE name in (
''' + @dbName + ''' ) ORDER BY name '
  
EXEC  ( @getDBName )
END

SET   @sno   =   1
WHILE  ( @sno   <=  ( SELECT   COUNT (sno)  FROM  ##MyDB))
  
BEGIN          -- 终于可以开始了,一个DB一个DB的循环吧,不喜欢用游标

  
SELECT   @name   =  name, @server_id   =  server_id  FROM  ##MyDB  WHERE  sno  =   @sno

IF  ( @server_id   =   2 )     -- 如果是本地的服务器2
BEGIN
  
EXEC  SHDB2.msdb.dbo.ModifyColumn --其他Server上的这个存储过程比这个简单多了
                
@dbName   =   @name ,
                
@type   =   @type ,
                
@tableName   =   @tableName ,
                
@columnName   =   @columnName ,
                
@dataType   =   @dataType ,
                
@constraint   =   @constraint ,
                
@is_exec   =   @is_exec ,
                
@custom   =   @custom
  
SET   @exec   =   ''
END

ELSE   IF  ( @server_id   =   3 )     -- 如果是台湾的服务器
BEGIN
  
EXEC  TWDB.msdb.dbo.ModifyColumn
                
@dbName   =   @name ,
                
@type   =   @type ,
                
@tableName   =   @tableName ,
                
@columnName   =   @columnName ,
                
@dataType   =   @dataType ,
                
@constraint   =   @constraint ,
                
@is_exec   =   @is_exec ,
                
@custom   =   @custom
  
SET   @exec   =   ''
END

ELSE   IF  ( @server_id   =   4 )     -- 如果是广州的服务器
BEGIN
  
EXEC  GZDB.msdb.dbo.ModifyColumn
                
@dbName   =   @name ,
                
@type   =   @type ,
                
@tableName   =   @tableName ,
                
@columnName   =   @columnName ,
                
@dataType   =   @dataType ,
                
@constraint   =   @constraint ,
                
@is_exec   =   @is_exec ,
                
@custom   =   @custom
  
SET   @exec   =   ''
END

ELSE   IF  ( @type   =   ' M ' )     -- 修改某个字段,这里才是重点
BEGIN
  
DECLARE   @exec4   varchar ( 500 )     -- 删除索引或键
   DECLARE   @exec5   varchar ( 500 )     -- 添加索引或键
   DECLARE   @exec6   varchar ( 500 )     -- 临时用用为了得到几个参数
   DECLARE   @i_name  sysname         -- 索引名
   DECLARE   @is_key   bit              -- 是否主键
   DECLARE   @i_no   tinyInt               -- 索引的序号
   DECLARE   @c_name   varchar ( 100 )     -- 索引所在的列名
   DECLARE   @i_type   varchar ( 60 )     -- 是否聚集
   DECLARE   @is_unique   varchar ( 6 )     -- 是否唯一

  
SET   @exec4   =   ''
  
SET   @exec5   =   ''
  
SET   @i_name   =   ''

  
IF   EXISTS  ( SELECT   *   FROM  tempdb.sys.objects  WHERE  name  =   ' ##Temp_dafalit ' )
    
DROP   TABLE  ##Temp_dafalit     -- 放默认值的
   IF   EXISTS  ( SELECT   *   FROM  tempdb.sys.objects  WHERE  name  =   ' ##temp_indexName ' )
    
DROP   TABLE  ##temp_indexName     -- 放索引名的

  
SET   @constraintName   =   ''      -- 约束名
   SET   @definition   =   ''          -- 默认值
   SET   @exec3   =   ' use  '   +   @name   +   ' ; '   +   char ( 13 +
        
' SELECT name,definition INTO ##Temp_dafalit FROM sys.default_constraints
          WHERE object_id = (SELECT default_object_id FROM sys.columns WHERE object_id = OBJECT_ID(
'''   +   @tableName   +   ''' ) AND name =  '''   +   @columnName   +   ''' ) '
  
EXEC  ( @exec3 )
  
SELECT   @constraintName   =  name, @definition   =  definition  FROM  ##Temp_dafalit

  
IF  ( @constraintName   !=   '' )     -- 如果该列有默认约束。修改前先删除,改好后还原。
   BEGIN
    
SET   @exec1   =   ' ALTER TABLE  '   +   @tableName   +   '  DROP CONSTRAINT  '   +   @constraintName   +   char ( 13 )
    
SET   @exec2   =   ' ALTER TABLE  '   +   @tableName   +   '  ADD  CONSTRAINT  '   +   @constraintName   +   '  DEFAULT  '   +   @definition   +   '  FOR  '   +   @columnName   +   char ( 13 )
  
END
  
ELSE
  
BEGIN
    
SET   @exec1   =   ''
    
SET   @exec2   =   ''
  
END

  
SET   @exec6   =    ' use  '   +   @name   +   ' ; '   +   char ( 13 +
    
' SELECT IDENTITY(tinyInt,1,1)i_no,c.name,c.is_primary_key INTO ##temp_indexName
       FROM sys.columns a inner join sys.index_columns b 
    ON a.object_id = b.object_id AND a.column_id = b.column_id inner join sys.indexes c
    ON b.object_id = c.object_id AND b.index_id = c.index_id
    WHERE a.object_id = OBJECT_ID(
'''   +   @tableName   +   ''' ) AND a.name =  '''   +   @columnName   +   ''''
  
EXEC  ( @exec6 )

  
SET   @i_no   =   1   -- 此处的循环很无奈,因为有的列上可能有两个索引,也不知道设计有没问题
   WHILE  ( @i_no   <=  ( SELECT   COUNT (i_no)  FROM  ##temp_indexName))
  
BEGIN
    
SELECT   @i_name   =  name , @is_key   =  is_primary_key  FROM  ##temp_indexName  WHERE  i_no  =   @i_no

    
IF  ( @i_name   !=   '' )     -- 如果该列有索引。修改前先删除,改好后还原。
     BEGIN
      
IF  ( @is_key   =   0 )     -- 判断是否主键,主键的索引和非主键索引删除方法不一样
         SET   @exec4   =   @exec4   +   ' DROP INDEX [ '   +   @i_name   +   ' ] ON [ '   +   @tableName   +   ' ] WITH ( ONLINE = OFF ) '   +   char ( 13 )
      
ELSE
        
SET   @exec4   =   @exec4   +   ' ALTER TABLE [ '   +   @tableName   +   ' ] DROP CONSTRAINT [ '   +   @i_name   +   ' ] '   +   char ( 13 )

      
IF   EXISTS  ( SELECT   *   FROM  tempdb.sys.objects  WHERE  name  =   ' ##temp_indexinfo ' )
        
DROP   TABLE  ##temp_indexinfo     -- 存放临时的索引信息

      
SET   @exec6   =    ' use  '   +   @name   +   ' ; '   +   char ( 13 +
        
' SELECT * INTO ##temp_indexinfo FROM
        (SELECT a.name,b.is_descending_key,c.type_desc, c.is_unique,is_primary_key
        FROM sys.columns a inner join sys.index_columns b 
        ON a.object_id = b.object_id AND a.column_id = b.column_id inner join sys.indexes c
        ON b.object_id = c.object_id AND b.index_id = c.index_id
        WHERE a.object_id = OBJECT_ID(
'''   +   @tableName   +   ''' ) AND c.name =  '''   +   @i_name   +   ''' )a '
      
EXEC  ( @exec6 )
      
SET   @c_name   =   ''
      
SELECT   @i_type   =  type_desc,  @is_unique   =  ( CASE  is_unique  WHEN   1   THEN   ' UNIQUE '   ELSE   ''   END ), @is_key   =  is_primary_key  FROM  ##temp_indexinfo
      
SELECT   @c_name   =   @c_name   +   ' ,[ '   +  name  +  ( CASE  is_descending_key  WHEN   1   THEN   ' ] DESC '   ELSE   ' ] ASC '   END FROM  ##temp_indexinfo

      
IF  ( @is_key   =   0 )     -- 主键的索引和非主键索引创建方法不一样
         SET   @exec5   =   @exec5   +   ' CREATE  '   +   @is_unique   +   '   '   +   @i_type   +   '  INDEX [ '   +   @i_name   +   ' ] ON [ '   +   @tableName   +   ' ] ( '   +   SUBSTRING ( @c_name , 2 , len ( @c_name ))  +   ' ) '   +   char ( 13 )
      
ELSE
        
SET   @exec5   =   @exec5   +   ' ALTER TABLE [ '   +   @tableName   +   ' ] ADD CONSTRAINT [ '   +   @i_name   +   ' ] PRIMARY KEY  '   +   @i_type   +   '  ( '   +   SUBSTRING ( @c_name , 2 , len ( @c_name ))  +   ' ) '   +   +   char ( 13 )
    
END
    
ELSE
    
BEGIN
      
SET   @exec4   =   ''
      
SET   @exec5   =   ''
    
END
    
SET   @i_no   =   @i_no   +   1      -- 下一个索引
   END      -- while

  
SET   @exec   =  
    
' IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID( '''   +   @tableName   +   ''' ) AND type =  '' U '' )BEGIN '   +   char ( 13 +
        
' IF ((SELECT is_nullable FROM sys.columns WHERE object_id = OBJECT_ID( '''   +   @tableName   +   ''' ) AND name =  '''   +   @columnName   +   ''' ) = 0) '   +   char ( 13 +
        
' ALTER TABLE  '   +   @tableName   +   '  ALTER COLUMN  '   +   @columnName   +   '   '   + @dataType   +   '  not null  '   +   @constraint   +   char ( 13 +     
        
' ELSE ALTER TABLE  '   +   @tableName   +   '  ALTER COLUMN  '   +   @columnName   +   '   '   + @dataType   +   '   '   +   @constraint   +   char ( 13 +
    
' PRINT  '''   +   @name   +   '  的  '   +   @tableName   +   '  表修改  '   +   @columnName   +   '  列成功 ''  END '   +   char ( 13 +
    
' ELSE PRINT  '''   +   @name   +   '  中没有  '   +   @tableName   +   '  表 '''   +   char ( 13 )

  
SET   @exec   =   ' use  '   +   @name   +   ' ; '   +   char ( 13 +   @exec1   +   @exec4   +   @exec   +   @exec2   +   @exec5

  
IF   EXISTS  ( SELECT   *   FROM  tempdb.sys.objects  WHERE  name  =   ' ##Temp_dafalit ' )
    
DROP   TABLE  ##Temp_dafalit
  
IF   EXISTS  ( SELECT   *   FROM  tempdb.sys.objects  WHERE  name  =   ' ##temp_indexName ' )
    
DROP   TABLE  ##temp_indexName
  
IF   EXISTS  ( SELECT   *   FROM  tempdb.sys.objects  WHERE  name  =   ' ##temp_indexinfo ' )
    
DROP   TABLE  ##temp_indexinfo
  
-- 居然用了3个临时表,还好这个不需要考虑效率问题
END

ELSE   IF  ( @type   =   ' A ' )     -- 增加某个字段
BEGIN
  
SET   @exec   =   ' use  '   +   @name   +   ' ; '   +   char ( 13 +  
    
' IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID( '''   +   @tableName   +   ''' ) AND type =  '' U '' ) '   +   char ( 13 +
        
' BEGIN ALTER TABLE  '   +   @tableName   +   '  ADD  '   +   @columnName   +   '   '   + @dataType   +   '   '   +   @constraint   +   char ( 13 +
        
' PRINT  '''   +   @name   +   '  的  '   +   @tableName   +   '  表增加  '   +   @columnName   +   '  列成功 ''  END '   +   char ( 13 +
    
' ELSE PRINT  '''   +   @name   +   '  中没有  '   +   @tableName   +   '  表 '''   +   char ( 13 )
END

ELSE   IF  ( @type   =   ' D ' )     -- 为列添加默认值
BEGIN
  
SET   @exec   =  
    
' IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID( '''   +   @tableName   +   ''' ) AND type =  '' U '' )BEGIN '   +   char ( 13 +
        
' ALTER TABLE  '   +   @tableName   +   '  ADD CONSTRAINT DF_ '   +   @tableName   +   ' _ '   +   @columnName   + '  DEFAULT ( '   +   @constraint   +   ' ) FOR  '   +   @columnName   +   char ( 13 +  
        
' PRINT  '''   +   @name   +   '  的  '   +   @tableName   +   '  表为  '   +   @columnName   +   '  列添加默认值成功 ''  END '   +   char ( 13 +
    
' ELSE PRINT  '''   +   @name   +   '  中没有  '   +   @tableName   +   '  表 '''   +   char ( 13 )

  
IF   exists  ( SELECT   *   FROM  tempdb.sys.objects  WHERE  name  =   ' ##Temp_dafalit ' )
    
DROP   TABLE  ##Temp_dafalit
  
SET   @constraintName   =   ''  
  
SET   @definition   =   ''
  
SET   @exec3   =   ' use  '   +   @name   +   ' ; '   +   char ( 13 +
        
' SELECT name,definition INTO ##Temp_dafalit FROM sys.default_constraints
          WHERE object_id = (SELECT default_object_id FROM sys.columns WHERE object_id = OBJECT_ID(
'''   +   @tableName   +   ''' ) AND name =  '''   +   @columnName   +   ''' ) '
  
EXEC  ( @exec3 )
  
SELECT   @constraintName   =  name, @definition   =  definition  FROM  ##Temp_dafalit

  
IF  ( @constraintName   !=   '' )
  
BEGIN
    
SET   @exec1   =    ' use  '   +   @name   +   ' ; '   +   char ( 13 +   ' alter table  '   +   @tableName   +   '  drop constraint  '   +   @constraintName
    
SET   @exec   =   @exec1   +   char ( 13 +   @exec   +   char ( 13 )
  
END
  
ELSE
    
SET   @exec   =   ' use  '   +   @name   +   ' ; '   +   char ( 13 +   @exec

  
IF   EXISTS  ( SELECT   *   FROM  tempdb.sys.objects  WHERE  name  =   ' ##Temp_dafalit ' )
    
DROP   TABLE  ##Temp_dafalit
END

ELSE   IF  ( @type   =   ' Z ' )     -- 执行自定义动作
BEGIN
set   @exec   =   ' use  '   +   @name   +   ' ; '   +   char ( 13 +
    
' IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID( '''   +   @tableName   +   ''' ) AND type =  '' U '' )BEGIN '   +   char ( 13 +
        
@custom   +   char ( 13 +   ' print  '' 在  '   +   @name   +   '  中执行成功 ''  END '   +   char ( 13 +
    
' ELSE  print  '' 在  '   +   @name   +   '  中没有这个表 '''   +   char ( 13 )
END

BEGIN  try     -- 开始执行
   IF  ( @is_exec   =   1 EXEC  ( @exec )
  
ELSE   PRINT  ( @exec )
END  try
BEGIN  catch
  
DECLARE   @error   nvarchar ( 500 )
  
SET   @error   =   @name   +   '  的  '   +   @tableName   +   '  中失败,原因: '   +   char ( 13 +  ERROR_MESSAGE()
  
RAISERROR ( @error , 16 , 1 )     -- 抛出错误信息    
END  catch

SET   @sno   =   @sno   +   1
END ;     -- 循环结束

IF   exists  ( SELECT   *   FROM  tempdb.sys.objects  WHERE  name  =   ' ##myDB ' )
  
DROP   TABLE  ##myDB
GO

写的太复杂了,没办法服务器就要4个,以后可能会更多,暂时就60多个数据库,你说我不怎样写以后改某个字段的时候不是要累死吗?本来的设计就不是很好,增加字段修改数据类型是常有的事情。痛苦啊!

其实这个存储过程很简单,就是在修改某一列的时候先判断一下又没索引,有就先自动生成这个索引的删除和添加语句。然后是默认约束,也是一样有就生成删除的和添加的语句。CHECK约束我没写,一个是很复杂,另一个是我们的所有DB总共不到10个这样的约束,没那必要了。这些语句都生成完后,执行修改前先执行删除的,修改完了执行添加的,这样就一切OK了。

测试了没问题,以后看看能不能继续加点东西上去,还有我加密的主要原因是不想公司的那些高级DBA笑话,写的太幼稚了,效率不好,不过总算是能给我减轻工作压力了,高兴!

这篇Blog的地址:http://blog.csdn.net/hb_gx/archive/2007/06/18/1655990.aspx

我高升也只是个初学者,还是非常希望能得到网络上众多高手的指定的,欢迎各种批评!另外说句实话我这篇文章写的真的很烂!

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值