简体   繁体   English

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

[英]Why does SQL Server apply RTRIM before Insert?

I have a table with a Unique Key on columns Pfx, Bse and Sfx. 我在Pfx,Bse和Sfx列上有一个带有唯一键的表。 While inserting data, it seems to me like SQL Server is internally applying an RTRIM and causing an issue with my Sfx column which has a space in the second row. 在插入数据时,在我看来SQL Server在内部应用RTRIM并导致我的Sfx列出现问题,该列在第二行中有空格。 Is it possible to prevent this RTRIM or am I missing something? 是否可以防止此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';

The error message doesn't seem to be trimming the data and retains the space. 错误消息似乎并没有修剪数据并保留了空间。

Violation of UNIQUE KEY constraint 'NNMP0672'. 违反UNIQUE KEY约束'NNMP0672'。 The duplicate key value is (2R83, 6477, AA ) 重复的键值为(2R83,6477,AA)

Are the columns CHAR or VARCHAR ? CHAR还是VARCHAR列?

This likely has to do with what the ANSI_PADDING setting was at the time that the column was created. 这可能与创建列时的ANSI_PADDING设置有关。 If ANSI_PADDING is set to OFF then VARCHAR columns are automatically trimmed when inserted into the column. 如果将ANSI_PADDING设置为OFF则将VARCHAR列插入列时会自动对其进行修剪。 CHAR can be a little more tricky when it is defined to allow NULL values, but in general it always pads columns to be of the maximum length for the column. CHAR定义为允许NULL值时,可能会有些棘手,但总的来说,它总是将列填充为该列的最大长度。 So, in short, you'll probably want VARCHAR columns with ANSI_PADDING set ON . 因此,简而言之,您可能希望将ANSI_PADDING设置为ON VARCHAR列。

Keep in mind that the ANSI setting applies when the column is created , so you will have to drop and recreate the table or at least the column to accomplish this. 请记住,ANSI设置在创建列时适用,因此您必须删除并重新创建表或至少创建列才能完成此操作。

As others have said though, it's generally a pretty bad idea to rely on hidden or whitespace characters to differentiate between keys in a table. 就像其他人所说的那样,通常依靠隐藏字符或空格字符来区分表中的键是一个非常糟糕的主意。 The fact that your import is failing here might mean something else besides the fact that there is a difference in trailing spaces - maybe this is bad data in the source system that should be corrected when you import it so that you never have the issue in the first place. 此处导入失败的事实可能意味着其他原因,除了尾随空格存在差异之外-也许这是源系统中的错误数据,在导入时应予以纠正,这样您就不会再遇到问题了。第一名。 Treat the problem, not the symptom ;) 治疗问题,而不是症状;)

Also, this may sound like a personal preference, but since we're no longer in the days when column names were limited to 8 characters, you might want to be a bit more descriptive with your column names rather than Pfx , Bse , etc. Spell words out and be descriptive. 同样,这听起来像是个人喜好,但由于我们不再需要将列名限制为8个字符,所以您可能希望对列名进行更多描述,而不是PfxBse等。拼出单词并进行描述。 I've found that this makes development and debugging much easier. 我发现这使开发和调试更加容易。 I realize that you're converting a legacy system, so maybe it's difficult (or not possible at this time) to do that, but if you can then I would highly suggest it. 我意识到您正在转换旧系统,因此可能很难(或目前无法执行),但是如果可以的话,我强烈建议您这样做。

Here's a link to the documentation on ANSI_PADDING if you want more information: https://docs.microsoft.com/en-us/sql/t-sql/statements/set-ansi-padding-transact-sql 如果需要更多信息,这是指向ANSI_PADDING文档的链接: https : //docs.microsoft.com/zh-cn/sql/t-sql/statements/set-ansi-padding-transact-sql

In this link: 在此链接中:

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

It says that in order to compare two strings of different lengths, the shorter string is padded with spaces, therefore your 'AA' in the first row becomes 'AA ' for comparison. 它说,为了比较两个不同长度的字符串,较短的字符串用空格填充,因此,第一行中的“ AA”变为“ AA”以进行比较。

Example: 例:

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

I really do not recommend what I am about to propose. 我真的不建议我要提出的建议。 But, you can accomplish what you want by using an explicit unique index and a computed column. 但是,您可以通过使用显式unique索引和计算列来完成所需的操作。

Note that spaces at the end of string are generally ignored. 请注意,通常会忽略字符串末尾的空格。 This is considered a good thing because we don't see them. 这被认为是一件好事,因为我们看不到它们。 WYSIWYG (what-you-see-is-what-you-get) is often a reasonable approach. 所见即所得所见即所得 )通常是一种合理的方法。 Spaces at the end of strings are ignored for LEN() as well as for comparisons, for instance. 例如,对于LEN()以及比较而言,将忽略字符串末尾的空格。

But, you can still calculate the length by appending a character and subtracting. 但是,您仍然可以通过添加字符并减去来计算长度。 So the following will allow you to have spaces at the end count as separate distinct values: 因此,以下内容将使您可以在结尾计数处留出空格作为单独的不同值:

alter table t add s_len as (len(s + 'x') - 1);

create unique index t_s_slen on t(s, s_len);

Here is a SQL Fiddle that illustrates this in action. 是一个SQL Fiddle,它说明了这一点。 Of course, you need to remove the unique constraint on the column alone. 当然,您需要仅删除列上的唯一约束。

What is your table definition? 您的表定义是什么? (ie what data types) (即什么数据类型)

It might suit you better to use NVARCHAR data types 使用NVARCHAR数据类型可能更适合您

See here , as it explains why VARCHAR types use the ANSI standard and ignore whitespace on the end of these data types 参见此处 ,因为它解释了为什么VARCHAR类型使用ANSI标准并忽略这些数据类型末尾的空白

Comparison is based on rtrim but they are different 比较基于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

You could use this to pad the space with _ 您可以使用_来填充空格

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


-----

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM