【问题标题】:SQL - "incrementing" a char value causes collation errorSQL - “递增” char 值会导致排序错误
【发布时间】:2015-01-25 02:12:04
【问题描述】:

我正在处理一个表,其中将一堆任意设置存储为VARCHAR(255) 值。我负责处理的特定任务是一个需要递增并返回给调用者的序列号。 (再次注意,序列“编号”存储为VARCHAR,这是我无法控制的)。

因为它是一个序列号,我真的不想在单独的步骤中选择和更新。当我过去用实际的数字字段处理这类事情时,我的方法类似于

UPDATE TABLE SET @SEQ_NUM = VALUE = VALUE + 1

它会增加值并在一个膨胀的过程中为我提供更新的值。我想在这种情况下,我会尝试使用演员表进行相同的基本操作:

DECLARE @SEQ_NUM VARCHAR(255)

UPDATE SOME_TABLE
    SET @SEQ_NUM = VALUE = CAST((CAST(VALUE AS INT) + 1) AS VARCHAR) 
WHERE NAME = 'SOME_NAME'

只要我不尝试将结果分配给变量,实际更新就可以正常工作;一旦我这样做,我就会收到以下错误:

Msg 549, Level 16, State 1, Line 4 排序规则 接收变量的“SQL_Latin1_General_CP1_CI_AS”不等于 “VALUE”列的排序规则“Latin1_General_BIN”。

我明白这意味着什么,但我不明白为什么会发生这种情况,或者进一步说,如何解决这个问题。

除了修复特定错误之外,我欢迎提供有关增加字符序列“数字”的替代方法的建议。

【问题讨论】:

  • 你为什么还要维护自己的序列号?它充满了并发等问题。因为设计这个的人并不认为你会在这里打一场败仗。
  • 您可以通过使用COLLATE 关键字来解决此问题。
  • @SeanLange,相信我,我知道。幸运的是,这个特殊的用例不会经常使用,但它仍然是一个巨大的 PITA。
  • @GordonLinoff--究竟如何?我尝试对计算值强制 COLLATE,如下所示:'SET SEQ_NUM = VALUE = CAST((CAST(VALUE AS INT) + 1) AS VARCHAR) COLLATE SQL_Latin1_General_CP1_CI_AS',但收到相同的错误。我不知道有一种方法可以在变量本身上设置 COLLATE 值(并不是说我不知道​​某事意味着什么)。

标签: sql sql-server sql-server-2008 collation


【解决方案1】:

手动维护序列号绝不是我想要使用的解决方案,但我可以理解这可能存在限制。

如果您将其分解为 2 个步骤,则可以解决该问题。请注意,我已替换您的 WHERE 子句以使此示例代码正常工作:

CREATE TABLE #SOME_TABLE ( [VALUE] VARCHAR(255) )

INSERT  INTO #SOME_TABLE
        ( VALUE )
VALUES  ( '12345' )

DECLARE @SEQ_NUM VARCHAR(255) 

UPDATE  #SOME_TABLE
SET     [VALUE] = CAST(( CAST([VALUE] AS INT) + 1 ) AS VARCHAR(255))
WHERE   1 = 1

SELECT  *
FROM    #SOME_TABLE

SELECT  @SEQ_NUM = [VALUE]
FROM    #SOME_TABLE
WHERE   1 = 1

SELECT  @SEQ_NUM

DROP TABLE #SOME_TABLE

【讨论】:

  • 我还没有开始尝试这种替代方法,但我正在考虑一种与您概述的方法类似的方法,除了在查询中使用 table var 和 OUTPUT 子句。
【解决方案2】:

从其中一位 cmets 看来,您可能已经对此有所了解,但这是我的建议:

UPDATE TABLE
SET VALUE = CAST((CAST(VALUE AS INT) + 1) AS VARCHAR) 
OUTPUT inserted.VALUE
WHERE NAME = 'SOME_NAME'

这将像SELECT 语句一样输出新值。如果您想在 SQL 中执行此操作,也可以将 inserted.VALUE 转换为 int

如果您想将值放入@SEQ_NUM 而不是从语句/存储过程中输出值,则不能使用标量变量,但可以将其泵入表变量中,如下所示:

DECLARE @SEQ_NUM AS TABLE ( VALUE VARCHAR(255) );

UPDATE TABLE
SET VALUE = CAST((CAST(VALUE AS INT) + 1) AS VARCHAR) 
OUTPUT inserted.VALUE INTO @SEQ_NUM ( VALUE )
WHERE NAME = 'SOME_NAME'

SELECT VALUE FROM @SEQ_NUM

【讨论】:

  • 这正是我现在正在做的事情。我在存储过程中进行此更新,需要返回值;是否有一种简化的替代方法可以将输出插入表 var 并选择返回值?如果不是插入到表变量中,我对 OUTPUT 的操作不是很熟悉。
  • 因此,这两个代码块将做完全相同的事情 - 从存储过程中输出新值。如果您不需要操作存储过程中的值,则使用第一个块,当您读取代码中返回的数据时(例如,通过ExecuteScalar),它将具有新值。
【解决方案3】:

您可以继续使用 OP 中的古怪更新,但您必须像这样将 UPDATE 语句中的三重赋值 @Variable = Column = Expression 拆分为 @Variable = ExpressionColumn = @Variable 两个简单赋值

CREATE TABLE #SOME_TABLE (
    NAME VARCHAR(255)
    , VALUE VARCHAR(255) COLLATE Latin1_General_BIN
    )
INSERT #SOME_TABLE SELECT 'SOME_NAME', '42'

DECLARE @SEQ_NUM VARCHAR(255)

/*
-- this quirky update fails on COLLATION mismatch or data-type mismatch
UPDATE #SOME_TABLE
    SET @SEQ_NUM = VALUE = CAST((CAST(VALUE AS INT) + 1) AS VARCHAR) 
WHERE NAME = 'SOME_NAME'
*/

-- this quirky update works in all cases
UPDATE #SOME_TABLE
    SET @SEQ_NUM = CAST((CAST(VALUE AS INT) + 1) AS VARCHAR) 
        , VALUE = @SEQ_NUM
WHERE NAME = 'SOME_NAME'

SELECT *, @SEQ_NUM FROM #SOME_TABLE

这种简单的重写可以防止 db-engine 抱怨 @VariableColumn 之间的数据类型差异(例如 VARCHARNVARCHAR),并且似乎是一种更“便携”的古怪更新方式(如果有的话)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-02-23
    • 2015-07-29
    • 2021-08-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多