为什么SQL Server在插入之前应用RTRIM?

时间:2018-03-04 14:06:47

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

我在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)

5 个答案:

答案 0 :(得分:3)

CHARVARCHAR

这可能与创建列时ANSI_PADDING设置的内容有关。如果ANSI_PADDING设置为OFF,那么VARCHAR列会在插入列时自动裁剪。当CHAR被定义为允许NULL值时,VARCHAR可能会有点棘手,但通常总是将列填充为列的最大长度。因此,简而言之,您可能希望将ANSI_PADDINGON设为Pfx

请记住,ANSI列设置在创建列时应用 ,因此您必须删除并重新创建表格或至少重新创建此列。

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

此外,这可能听起来像个人偏好,但由于我们不再处于列名限制为8个字符的日子,您可能希望对列名更具描述性,而不是{{1 },Bse等。拼出单词并具有描述性。我发现这使得开发和调试变得更加容易。我意识到你正在转换一个遗留系统,所以也许很难(或者目前不可能)这样做,但如果可以,那么我会强烈建议它。

如果您需要更多信息,请点击ANSI_PADDING上的文档链接:https://docs.microsoft.com/en-us/sql/t-sql/statements/set-ansi-padding-transact-sql

答案 1 :(得分:1)

在此链接中:

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

答案 2 :(得分:1)

我真的不建议我提议的内容。但是,您可以使用显式unique索引和计算列来完成您想要的任务。

请注意,字符串末尾的空格通常会被忽略。这被认为是一件好事,因为我们没有看到它们。 WYSIWYG(你所看到的是什么)通常是一种合理的方法。例如,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小提琴,用于说明这一点。当然,您需要仅删除列上的唯一约束。

答案 3 :(得分:0)

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

最好使用NVARCHAR数据类型

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

答案 4 :(得分:0)

比较基于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


-----