【问题标题】:Cannot insert an explicit value into a timestamp column无法将显式值插入时间戳列
【发布时间】:2015-03-19 10:01:10
【问题描述】:

试图找到解决错误的方法

SELECT * 
INTO #Products
FROM Products
WHERE Code = @sku

Update #products 
SET Code = Code+'Group',
    ProducttypeID = @SimpleProducttype,
    ParentProductID = @SimpleProductID,
    ID = NEWID()

ALTER TABLE #Products Drop column ts

INSERT Products
SELECT *, null ts FROM #Products

然后我得到:

无法将显式值插入时间戳列。将 INSERT 与列列表一起使用以排除时间戳列,或将 DEFAULT 插入时间戳列。

我不想明确地将每一列都放在语句中,因为表格可能会改变,我只想复制行并改变一些东西。

我尝试了一些解决方法,阅读了很多帖子,但没有任何乐趣。救命!

【问题讨论】:

  • TIMESTAMP 是 SQL Server 中的一种特殊类型(它与日期和时间nothing!) - 它是一个system-internal 计数器,在给定行的每次修改时更新,以便能够检测对行的更改。它是INTERNAL - 你不能设置它,你可以only 读取它。在较新的 SQL Server 版本中,它现在实际上称为 ROWVERSION
  • 问题是当你插入INSERT Products SELECT *, null ts FROM #Products时你假设ts是表products中的最后一列,如果是那么它会正常工作,如果不是那么你有从#Products 的中间删除一列,并在最后将其添加为 null。正如 Gvee 指出的那样,解决方案是明确列出您的列。进一步阅读 - Bad habits to kick : using SELECT * / omitting the column list
  • 错误是:无法将显式值插入时间戳列。将 INSERT 与列列表一起使用以排除时间戳列,或将 DEFAULT 插入时间戳列。我该如何(选项 2) - 将 DEFAULT 插入时间戳列
  • @MartinThompson 插入“默认”值的唯一方法是明确列出INSERT 部分中的字段。 DEFAULT 没有这个就不能使用。

标签: sql-server tsql sql-server-2012


【解决方案1】:

总是、总是总是INSERT 语句中列出您的列。

INSERT INTO your_table (foo, bar)
  SELECT list_columns
       , here_too
  FROM   select_star_is_evil As seriously
;

不这样做的唯一借口是懒惰。

明确代码非常重要!

</rant>

错误信息非常清楚:您正试图在 timestamp 列中插入一个值。您的最后一条语句是尝试插入 NULL 值。

避免这种行为的唯一方法是不在您的INSERT 列表中列出timestamp 列。

【讨论】:

  • 我不同意。我想要一个包含所有内容的行的精确副本并进行一些更改 - 显式放置每一列并且每次更改时都必须向该 SP 添加一个新列是迂腐且浪费时间的。
  • 实际上您的桌子多久更换一次?每次表发生更改时,更改过程只需要大约一分钟。如果您将尝试避免键入列列表所花费的时间加起来,您可能会花费更多时间来尝试避免它,而不是花费 50 次更改过程,即使您的表定期更改也是一年每周一次。
  • 没关系。这意味着如果它确实发生了变化,我不必考虑它对其他地方的影响。
  • 这不是对正确的迂腐,而是对如何解决问题的迂腐。你有三个选择。 1. 明确列出要插入的列。 2. 将您的时间戳列更改为具有默认值的 DATETIME 列。 3 使用sys.columns 生成动态SQL ror 您的插入。因此,与其否认你被给予的建议,不如更乐于接受:-)
  • 如果要复制一行,那就太可笑了。在其他情况下我可以理解它,但是必须跟踪它在哪里以及何时更改架构是浪费时间。
【解决方案2】:

在这里您尝试将 null 值插入到列 ts

另外,这里inserttableselecttable有不同的列列表,所以你必须在select statement中列出列。

试试。

SELECT * 
INTO #Products
FROM Products
WHERE Code = @sku

Update #products 
SET Code = Code+'Group',
    ProducttypeID = @SimpleProducttype,
    ParentProductID = @SimpleProductID,
    ID = NEWID()

ALTER TABLE #Products Drop column ts

INSERT INTO Products(col1,col2)
SELECT col1,col2  FROM #Products

【讨论】:

  • 必须有办法解决这个问题(除了删除时间戳列)
  • 您可以使用:INSERT INTO Products(col1,col2,ts) SELECT col1,col2,null FROM #Products 并且 ts 必须是可以为空的列
【解决方案3】:

这是您的解决方法。

/************************************************************************************************
CREATE SAMPLE TABLE AND ADD SAMPLE DATA
************************************************************************************************/
CREATE TABLE Products
(
    ID UNIQUEIDENTIFIER NOT NULL,
    ProductTypeID INT NOT NULL,
    ParentProductID INT NOT NULL,
    Code VARCHAR(255) NOT NULL,
    ts TIMESTAMP
);
INSERT Products (ID, ProductTypeID, ParentProductID, Code)
VALUES (NEWID(), 1, 1, 'SKU');

/************************************************************************************************
DECLARE INPUT VARIABLES
************************************************************************************************/

DECLARE @SimpleProducttype INT = 2,
        @SimpleProductID INT = 2,
        @Sku VARCHAR(255) = 'SKU',
        @ID UNIQUEIDENTIFIER = NEWID();


/************************************************************************************************
GENERATE INSERT SCRIPT USING SYS.COLUMNS AND XML CONCATENATION
************************************************************************************************/
DECLARE @SQL NVARCHAR(MAX) = 
    'INSERT Products (' + 
        STUFF(( SELECT  ',' + QUOTENAME(c.name)
                FROM    sys.columns AS c
                WHERE   c.[object_id] = OBJECT_ID(N'dbo.Products')
                AND     c.name != 'ts'
                ORDER BY c.column_id
                FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
    + ')
    SELECT  ' +
        STUFF(( SELECT  ',' + CASE WHEN c.Name IN ('ProducttypeID', 'ParentProductID', 'ID') THEN '@' + Name
                                ELSE QUOTENAME(c.name)
                            END
                FROM    sys.columns AS c
                WHERE   c.[object_id] = OBJECT_ID(N'dbo.Products')
                AND     c.name != 'ts'
                ORDER BY c.column_id
                FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)'), 1, 1, '') + '
    FROM    Products
    WHERE   Code = @sku;';
/************************************************************************************************
EXECUTE THE SCRIPT
************************************************************************************************/
EXECUTE sp_executesql 
    @SQL, 
    N'@ParentProductID INT, @ProductTypeID INT, @ID UNIQUEIDENTIFIER, @Sku VARCHAR(255)', 
    @ParentProductID = @SimpleProductID,
    @ProducttypeID = @SimpleProducttype,
    @ID = @ID,
    @Sku = @Sku;

为示例表生成的脚本是:

INSERT Products ([ID],[ProductTypeID],[ParentProductID],[Code])
SELECT  @ID,@ProductTypeID,@ParentProductID,[Code]
FROM    Products
WHERE   Code = @sku;

这是一个 UGLY hack,我不建议您使用它,我发布它是为了展示仅列出列会变得多么简单,并且每次您的架构更改时,使对存储过程的更改。

【讨论】:

    【解决方案4】:

    我建议创建一个返回除时间戳之外的所有列的视图。 您需要确保视图保持最新,但这将允许更新脚本本身在不明确指定列的情况下运行。

    您的脚本将如下所示:

    SELECT * 
    INTO #Products
    FROM vwProducts
    WHERE Code = @sku
    
    Update #products 
    SET Code = Code+'Group',
        ProducttypeID = @SimpleProducttype,
        ParentProductID = @SimpleProductID,
        ID = NEWID()
    
    INSERT vwProducts
    SELECT * FROM #Products
    

    【讨论】:

      【解决方案5】:

      你不能使用select *

      一种方法是从列列表中删除timespan

      INSERT INTO Products ( [Col1],[Col2],[Coln] )
             VALUES ( 'ValueCol1','ValueCol2','ValueCol' )
      

      第二种方法是将timestamp列中的值指定为DEFAULT:

      INSERT INTO Products  ( [Col1], [Timestamp] )
             VALUES ( 'Col1Value', DEFAULT )
      

      【讨论】:

        【解决方案6】:

        如果您不关心时间戳/行版本字段中的值,您可以在复制表上将其定义为 binary(8)。那么你就不需要列出列了。

        【讨论】:

          猜你喜欢
          • 2012-05-02
          • 1970-01-01
          • 2017-05-31
          • 1970-01-01
          • 2015-02-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多