【问题标题】:How can I avoid a timestamp insert error when copying data from one table to another?将数据从一个表复制到另一个表时,如何避免时间戳插入错误?
【发布时间】:2015-03-25 05:36:43
【问题描述】:

我正在尝试根据主键剔除表列表 (~30) 中的数据。

我的方法是: 1.创建一个中间表并为每个表加载所需的数据
2.截断原表
3.将中间表中的数据插回原表中。

这是我目前使用的代码:

declare @table nvarchar(max) 
open tab
fetch next from tab into @table
while(@@FETCH_STATUS = 0)
       begin
              print @table
             exec ('select * into ' +@table+'_intermediate from '+@table+' where P_ID in( select P_ID from pc_table )')
             exec ('truncate table '+@table)
             exec ('insert into '+@table+' select * from '+@table+'_intermediate')
             exec ('drop table '+@table+'_intermediate') 
            fetch next from tab into @table
       end
close tab
deallocate tab

我遇到了一个错误:

Cannot insert an explicit value into a timestamp column. 
Use INSERT with a column list to exclude the timestamp column, 
or insert a DEFAULT into the timestamp column.

所以,该错误告诉我无法在时间戳列中插入任何内容。

为了避免选择时间戳,我需要避免选择它(即使用 select *)。

是否有一种简单的方法来选择除类型时间戳之外的所有列,还是我需要进入信息架构并为每个表构建一个动态选择语句?

(或隐含的问题,有没有更好的方法来做我想做的事情?)

谢谢

【问题讨论】:

    标签: sql-server-2008 dml sql-timestamp


    【解决方案1】:

    简短的回答是,您需要在任何有时间戳列的位置放置“null”。

    我制作了这个小脚本来创建列列表,以便将该列表放入 DML 语句中:

             declare @sel_statement nvarchar(max)=''
             declare @col nvarchar(100) =''
             declare @num_rows int =0
             declare @dat_type nvarchar(30)
    
             declare cols cursor for
             select column_name, data_type 
             from information_schema.COLUMNS
             where TABLE_NAME = @table  --uses table fetched from tab cursor
    
             open cols
    
             fetch next from cols into @col, @dat_type
             while(@@FETCH_STATUS = 0)
                    begin
                    set @num_rows +=1
                    if @dat_type = 'timestamp'
                         set @sel_statement += 'null'
                    else  
                          set @sel_statement += @col 
                    fetch next from cols into @col, @dat_type
                    if @@FETCH_STATUS=0
                          set @sel_statement += ','
                    end
             close cols
             deallocate cols 
    

    这不是最漂亮的东西,但它确实有效。

    希望如果遇到此问题,这可以帮助其他人。

    【讨论】:

      【解决方案2】:

      如果是数百万行,而不是数十亿行,那么简单

      DELETE from TABLE where P_ID not in (select P_ID from pc_table)
      

      (可能分批)可能是可以接受的。首先删除所有索引(ID 上的主键除外)和约束,删除行,重新创建索引。更好的是,不要删除,而是禁用索引,然后使用 REBUILD INDEX 重新启用它们。

      还有一点需要考虑。如果您确实使用了中间表,那么在重新插入之后,timestamp 列的所有值都会变得不同。如果您不关心在此列中保留值,您可以简单地在处理之前删除此列并在完成后将其添加回来。

      如果性能很重要,您应该以您选择的任何方法禁用目标表上的约束和索引。

      这给我们带来了另一种方法:

      SELECT * INTO intermediate_table ...
      

      适用于 timestamp 列。

      INSERT INTO final_table SELECT * FROM intermediate_table ...
      

      这不适用于时间戳列。

      因此,您可以使用DROP final_table 代替TRUNCATE final_table,并且也可以第二次使用SELECT * INTO final_table ...

      因此,您还将保留 timestamp 列的值。当然,如果你完全DROP它,你将不得不重新创建原始表的所有约束和索引。

      【讨论】:

        【解决方案3】:

        怎么样

        delete from TABLE where P_ID not in( select P_ID from pc_table )
        

        ?

        【讨论】:

        • 这会起作用,但是在处理数百万行时(至少从我读过的内容来看),截断表会更快,并且不会在每次删除时填充日志文件......但它可能仍然比复制数据然后重新插入要快。
        • 如果它真的是数百万行并且性能很重要,恐怕你会被输入列名所困扰。没有选择 *-except :)
        • 没错。打印这些语句,制作巨大的脚本,将 * 替换为适用的列列表并运行此脚本。我建议对每批使用事务以确保您的数据安全,以防出错。
        猜你喜欢
        • 2018-08-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-08-24
        • 2014-10-15
        • 1970-01-01
        相关资源
        最近更新 更多