提问人:habdl 提问时间:10/31/2023 最后编辑:GSerghabdl 更新时间:10/31/2023 访问量:72
如何在 UTF8 排序的 varchar(N) 列中插入长字符串?
How can I insert long string into UTF8-collated varchar(N) column?
问:
我在源中有数据,我需要将其插入到另一个表中。
我需要使用的排序规则是 UTF8(特别是 )。
问题是,即使我将数据削减到最大允许长度(使用函数,或 或其他),某些数据的插入也会失败,并且VARCHAR(MAX)
VARCHAR(N)
Czech_100_CI_AS_SC_UTF8
LEFT
SUBSTRING
消息 2628,级别 16,状态 1,第 25
行 字符串或二进制数据将在表“tablename”列“columnname”中被截断。截断值:'sometext'。
我的问题是,如何适当地缩短文本,使其适合,但仍然尽可能多地保留它?
字符串的实际长度将取决于所使用的字符,对于纯拉丁文本,可以使用全长。如果有几个重音字符,则可用长度会缩短一点。如果文本是完全 Unicode(非拉丁文字,例如日语),则可用长度可能会减半或三分之一。
重现步骤:
DROP TABLE IF EXISTS [#tmpdl]
GO
CREATE TABLE [#tmpdl] ([Txt] VARCHAR(10) COLLATE Czech_100_CI_AS_SC_UTF8 NULL)
GO
INSERT INTO [#tmpdl] ([Txt]) VALUES (LEFT('123456789abcd', 10)) --this works
GO
INSERT INTO [#tmpdl] ([Txt]) VALUES (LEFT('123456789ábcd', 10)) --this fails
GO
INSERT INTO [#tmpdl] ([Txt]) VALUES (LEFT(N'一二三四', 10)) --this also fails
GO
答:
即使我将数据削减到最大允许长度(使用 function 或 或其他什么),某些数据的插入也会失败
LEFT
SUBSTRING
with or you 正在将其削减到指定的字符数,而 in 指定字节数,因此在截断 or 之后,您会留下一个字符串,该字符串可能仍包含超过 10 个字节 - 这是相同的,出于插入的目的,就好像您从未截断过它一样。LEFT
SUBSTRING
10
varchar(10)
LEFT
SUBSTRING
将字符串截断为多个字符而不是字节的一种简单方法是使用 或 ,因为它们会在不适合的代理项对之前剪切字符串并保留原始排序规则:CAST
CONVERT
INSERT INTO [#tmpdl] ([Txt]) VALUES (cast(N'123456789ábcd' COLLATE Czech_100_CI_AS_SC_UTF8 as varchar(10)));
INSERT INTO [#tmpdl] ([Txt]) VALUES (cast(N'一二三四' COLLATE Czech_100_CI_AS_SC_UTF8 as varchar(10)));
请注意,这需要 SQL Server 2012 或更高版本。
评论
对于 UTF8 值表示可以存储在数据类型中的字节数,而不是字符数。对于该值,这些值都是 ANSI 字符,大小为 1 个字节。但是,对于 ,字符的大小为 2 个字节;这会使您尝试的字符串的 datalength 为 11 个字节,而不是 10 个字节。你的最后一个字符串也是一个类似的故事, :10
'123456789abcd'
'123456789ábcd'
á
INSERT
'一二三四'
SELECT V.YourString,
DATALENGTH(V.YourString),
DATALENGTH(LEFT(V.YourString,10))
FROM (VALUES('123456789abcd' COLLATE Czech_100_CI_AS_SC_UTF8),
(CONVERT(varchar(20),N'123456789ábcd' COLLATE Czech_100_CI_AS_SC_UTF8)),
(CONVERT(varchar(20),N'一二三四' COLLATE Czech_100_CI_AS_SC_UTF8)))V(YourString);
YourString (英语) | 数据长度 | 10CharsDataLength |
---|---|---|
123456789ABCD(英语:ABCCD) | 13 | 10 |
123456789ábc | 14 | 11 |
一二三四 | 12 | 12 |
您需要确保定义的列的长度有足够的空间容纳所有字节,而不是字符数:
DROP TABLE IF EXISTS #tmpdl
GO
CREATE TABLE [#tmpdl] ([Txt] VARCHAR(12) COLLATE Czech_100_CI_AS_SC_UTF8 NULL)
GO
INSERT INTO [#tmpdl] ([Txt]) VALUES (LEFT('123456789abcd', 10)) --this works
GO
INSERT INTO [#tmpdl] ([Txt]) VALUES (LEFT('123456789ábcd', 10)) --this fails
GO
INSERT INTO [#tmpdl] ([Txt]) VALUES (LEFT(N'一二三四', 10)) --this also fails
GO
SELECT *
FROM #tmpdl;
GO
DROP TABLE #tmpdl
评论
VARCHAR
NVARCHAR
SET ANSI_WARNINGS OFF;
VARCHAR
NVARCHAR
UCS-2