【问题标题】:Why does SQL Server apply RTRIM before Insert?为什么 SQL Server 在插入之前应用 RTRIM?
【发布时间】:2018-03-04 14:06:46
【问题描述】:

我在 Pfx、Bse 和 Sfx 列上有一个带有唯一键的表。 在插入数据时,在我看来,SQL Server 似乎在内部应用 RTRIM 并导致我的 Sfx 列出现问题,该列在第二行中有一个空格。是否可以防止这种 RTRIM 或我错过了什么?

INSERT INTO Part (Seq, Pfx, Bse, Sfx, Stat, Desc, Cr_date, Cr_User)
SELECT 1 SEQ, '2R83' AS PFX, '6477' BSE, 'AA' SFX, 1 STAT, 'SPLIT MASS FLYWHEEL' DESCR, GETDATE() CR_DT, 'USERID' CR_US 
UNION ALL
SELECT 2, '2R83', '6477', 'AA ', 1, 'SPLIT MASS FLYWHEEL', GETDATE(), 'USERID';

错误信息似乎没有修剪数据并保留空间。

违反 UNIQUE KEY 约束“NNMP0672”。重复键值为 (2R83, 6477, AA)

【问题讨论】:

  • 您真的需要能够将“AA”和“AA”存储为不同的值吗?这对我来说似乎不是一个好主意。即使您可以说服数据库允许它。将来可能会导致非常令人沮丧的维护问题。
  • 好问题。我正在将数据从旧版 oracle DB 迁移到 sql server。还有其他列,例如描述,让我认为应该加载这些数据。希望这是有道理的。
  • @rsreji,SQL Server 在比较字符串值时会忽略尾随空格,因此无论是否存储尾随空格,您都会得到重复的键冲突。
  • @rsreji 如果我遇到这个问题,我会非常怀疑原始记录(带有尾随空格)输入错误。您可以尝试完全按照最初存储的方式加载它,但它可能真正需要的是重复解析数据。
  • 是的,我将尝试看看我是否可以努力处理数据。谢谢。

标签: sql sql-server unique-key character-trimming


【解决方案1】:

列是CHAR 还是VARCHAR

这可能与创建列时的ANSI_PADDING 设置有关。如果ANSI_PADDING 设置为OFF,则VARCHAR 列在插入列时会自动修剪。当CHAR 被定义为允许NULL 值时,它可能会有点棘手,但通常它总是将列填充为列的最大长度。因此,简而言之,您可能需要 VARCHAR 列和 ANSI_PADDING 设置 ON

请记住,ANSI 设置在创建列时应用,因此您必须删除并重新创建表或至少是列才能完成此操作。

正如其他人所说,依靠隐藏或空白字符来区分表中的键通常是一个非常糟糕的主意。您的导入在此处失败的事实可能意味着除了尾随空格存在差异这一事实之外 - 可能这是源系统中的错误数据,在您导入时应该更正它,以便您永远不会遇到问题第一名。治疗问题,而不是症状;)

此外,这听起来像是个人喜好,但由于我们不再处于列名限制为 8 个字符的时代,您可能希望列名更具描述性,而不是 PfxBse 等。拼出单词并进行描述。我发现这使开发和调试变得更加容易。我知道您正在转换旧系统,因此可能很难(或目前不可能)这样做,但如果可以的话,我强烈建议您这样做。

如果您想了解更多信息,请访问ANSI_PADDING 上的文档链接:https://docs.microsoft.com/en-us/sql/t-sql/statements/set-ansi-padding-transact-sql

【讨论】:

  • Varchar 数据类型。感谢您提供有关填充设置的信息,将使用我们的数据建模器进行检查。我确实花时间缩小到这个问题。关于列名,我在提交问题之前缩短了它们。
  • @rsreji 右填充是 SQL 更直观的特征之一。 ANSI SQL 92 要求在比较之前将字符串填充到相同的长度。即使保留了尾随空格,abcabc 仍被视为相等
【解决方案2】:

在此链接中:

https://support.microsoft.com/en-gb/help/316626/inf-how-sql-server-compares-strings-with-trailing-spaces

它说为了比较两个不同长度的字符串,较短的字符串用空格填充,因此第一行的'AA'变成'AA'进行比较。

例子:

create table dbo.Strings (
    ID INT IDENTITY(1,1) PRIMARY KEY CLUSTERED,
    S_VC VARCHAR(100) NULL
)

insert strings (S_VC)
values  ('Robert '),
        ('Robert')

select  ID, S_VC, datalength(S_VC) Data_Len, len(S_VC) [Len]
from    strings

select  *
from    strings s1 inner join strings s2
        on s1.S_VC = s2.S_VC

【讨论】:

    【解决方案3】:

    我真的不推荐我将要提出的建议。但是,您可以通过使用显式 unique 索引和计算列来完成您想要的操作。

    请注意,字符串末尾的空格通常会被忽略。这被认为是一件好事,因为我们看不到它们。 WYSIWYG (what-you-see-is-what-you-get) 通常是一种合理的方法。例如,对于 LEN() 以及比较,字符串末尾的空格会被忽略。

    但是,您仍然可以通过添加字符并减去来计算长度。因此,以下内容将允许您在末尾有空格作为单独的不同值:

    alter table t add s_len as (len(s + 'x') - 1);
    
    create unique index t_s_slen on t(s, s_len);
    

    Here 是一个 SQL Fiddle,它说明了这一点。当然,你需要单独移除列上的唯一约束。

    【讨论】:

      【解决方案4】:

      你的表定义是什么? (即什么数据类型)

      使用NVARCHAR 数据类型可能更适合您

      请参阅 here,因为它解释了为什么 VARCHAR 类型使用 ANSI 标准并忽略这些数据类型末尾的空格

      【讨论】:

      • 数据类型 = varchar。 nvarchar 不会使我的磁盘使用量翻倍吗?该表包含大约 1000 万行。
      • 对于 char / varchar 数据类型,不一定是 double,但接近 double。如果你有整数和小数,那么这些不会受到影响。增加数据库的大小是另一个问题:)
      【解决方案5】:

      比较是基于rtrim,但它们是不同的

      declare @tV table (name varchar(10) primary key);
      insert into @tV values ('bob'), ('alice'), ('ted'), ('al '), (' al');
      select *, len(name) as ln, DATALENGTH(name) as dl
      from @tV;
      
          name       ln          dl
      ---------- ----------- -----------
       al        3           3
      al         2           3
      alice      5           5
      bob        3           3
      ted        3           3
      

      你可以用它来填充空间 _

      set nocount on;
      declare @al1 varchar(10) = 'al';
      declare @al2 varchar(10) = 'al ';
      select @al1, len(@al1), DATALENGTH(@al1), left((rtrim(@al1) + '____'), DATALENGTH(@al1))
           , @al2, len(@al2), DATALENGTH(@al2), left((rtrim(@al2) + '____'), DATALENGTH(@al2));
      select 'equal' where  @al1 = @al2;
      select 'not equal' where  @al1 <> @al2;
      select 'equal' where  @al1 = @al2;
      select 'equal' where  left((rtrim(@al1) + '____'), DATALENGTH(@al1)) = left((rtrim(@al2) + '____'), DATALENGTH(@al2));
      
      ---------- ----------- ----------- -------------- ---------- ----------- ----------- --------------
      al         2           2           al             al         2           3           al_
      
      
      -----
      equal
      
      
      ---------
      
      
      -----
      equal
      
      
      -----
      

      【讨论】:

      • 这是不正确的,值将与额外的空间一起存储,使用 DATA_LENGTH() 来查看实际情况。
      • @MJH 你是对的。让我在删除之前考虑一个解决方案。
      猜你喜欢
      • 1970-01-01
      • 2021-08-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-21
      相关资源
      最近更新 更多