SELECT
CAST(
'<?xml version=''1.0'' encoding=''utf-8''?><![CDATA[' --start CDATA
+ REPLACE(
LB.LongBinary,
']]>', --we need only to escape ]]>, which ends CDATA section
']]]]><![CDATA[>' --we simply split it into two CDATA sections
) + ']]>' AS XML --finish CDATA
).value('.', 'nvarchar(max)')
(don't use this,it is necessary,see Plain insert trick)不要使用这个,这是不必要的,请参阅 Plain insert trick) 我试图想出一种使用SQL Server 2019的Utf8排序规则的方法,到目前为止,我已经找到了一种可能的方法,它应该比XML技巧更快(见下文)。 1.创建具有varbinary列的临时表。 1.将varbinary值插入到表中 1.使用utf8归类将表alter列更改为varchar
drop table if exists
#bin,
#utf8;
create table #utf8 (UTF8 VARCHAR(MAX) COLLATE Czech_100_CI_AI_SC_UTF8);
create table #bin (BIN VARBINARY(MAX));
insert into #utf8 (UTF8) values ('Žluťoučký kůň říčně pěl ďábelské ódy za svitu měsíce.');
insert into #bin (BIN) select CAST(UTF8 AS varbinary(max)) from #utf8;
select * from #utf8; --here you can see the utf8 string is stored correctly and that
select BIN, CAST(BIN AS VARCHAR(MAX)) from #bin; --utf8 binary is converted into gibberish
alter table #bin alter column BIN varchar(max) collate Czech_100_CI_AI_SC_UTF8;
select * from #bin; --voialá, correctly converted varchar
alter table #bin alter column BIN nvarchar(max);
select * from #bin; --finally, correctly converted nvarchar
速度差
数据库技巧 * 和 * 插入技巧 * 是最快的技巧。
*XML技巧 * 比较慢。
修改表格的技巧 * 是愚蠢的,不要这样做。当你一次修改很多短文本时,它会失败(修改的表格很大)。
测试:
第一个字符串包含一个 *XML技巧 * 的替换
第二个字符串是纯ASCII字符串,没有替换XML字符串
@TextLengthMultiplier确定转换文本的长度
@TextAmount确定一次转换的数量
------------------
--TEST SETUP
--DECLARE @LongText NVARCHAR(MAX) = N'český jazyk, Tiếng Việt, русский язык, 漢語, ]]>';
--DECLARE @LongText NVARCHAR(MAX) = N'JUST ASCII, for LOLZ------------------------------------------------------';
DECLARE
@TextLengthMultiplier INTEGER = 100000,
@TextAmount INTEGER = 10;
---------------------
-- TECHNICALITIES
DECLARE
@StartCDATA DATETIME2(7), @EndCDATA DATETIME2(7),
@StartTable DATETIME2(7), @EndTable DATETIME2(7),
@StartDB DATETIME2(7), @EndDB DATETIME2(7),
@StartInsert DATETIME2(7), @EndInsert DATETIME2(7);
drop table if exists
#longTexts,
#longBinaries,
#CDATAConverts,
#DBConverts,
#INsertConverts;
CREATE TABLE #longTexts (LongText VARCHAR (MAX) COLLATE Czech_100_CI_AI_SC_UTF8 NOT NULL);
CREATE TABLE #longBinaries (LongBinary VARBINARY(MAX) NOT NULL);
CREATE TABLE #CDATAConverts (LongText VARCHAR (MAX) COLLATE Czech_100_CI_AI_SC_UTF8 NOT NULL);
CREATE TABLE #DBConverts (LongText VARCHAR (MAX) COLLATE Czech_100_CI_AI_SC_UTF8 NOT NULL);
CREATE TABLE #InsertConverts (LongText VARCHAR (MAX) COLLATE Czech_100_CI_AI_SC_UTF8 NOT NULL);
insert into #longTexts --make the long text longer
(LongText)
select
REPLICATE(@LongText, @TextLengthMultiplier)
from
TESTES.dbo.Numbers --use while if you don't have number table
WHERE
Number BETWEEN 1 AND @TextAmount; --make more of them
insert into #longBinaries (LongBinary) select CAST(LongText AS varbinary(max)) from #longTexts;
--sanity check...
SELECT TOP(1) * FROM #longTexts;
------------------------------
--MEASURE CDATA--
SET @StartCDATA = SYSDATETIME();
INSERT INTO #CDATAConverts
(
LongText
)
SELECT
CAST(
'<?xml version=''1.0'' encoding=''utf-8''?><![CDATA['
+ REPLACE(
LB.LongBinary,
']]>',
']]]]><![CDATA[>'
) + ']]>' AS XML
).value('.', 'Nvarchar(max)')
FROM
#longBinaries AS LB;
SET @EndCDATA = SYSDATETIME();
--------------------------------------------
--MEASURE ALTER TABLE--
SET @StartTable = SYSDATETIME();
DROP TABLE IF EXISTS #AlterConverts;
CREATE TABLE #AlterConverts (UTF8 VARBINARY(MAX));
INSERT INTO #AlterConverts
(
UTF8
)
SELECT
LB.LongBinary
FROM
#longBinaries AS LB;
ALTER TABLE #AlterConverts ALTER COLUMN UTF8 VARCHAR(MAX) COLLATE Czech_100_CI_AI_SC_UTF8;
--ALTER TABLE #AlterConverts ALTER COLUMN UTF8 NVARCHAR(MAX);
SET @EndTable = SYSDATETIME();
--------------------------------------------
--MEASURE DB--
SET @StartDB = SYSDATETIME();
INSERT INTO #DBConverts
(
LongText
)
SELECT
FUNCTIONS_ONLY.dbo.VarBinaryToUTF8(LB.LongBinary)
FROM
#longBinaries AS LB;
SET @EndDB = SYSDATETIME();
--------------------------------------------
--MEASURE Insert--
SET @StartInsert = SYSDATETIME();
INSERT INTO #INsertConverts
(
LongText
)
SELECT
LB.LongBinary
FROM
#longBinaries AS LB;
SET @EndInsert = SYSDATETIME();
--------------------------------------------
-- RESULTS
SELECT
DATEDIFF(MILLISECOND, @StartCDATA, @EndCDATA) AS CDATA_MS,
DATEDIFF(MILLISECOND, @StartTable, @EndTable) AS ALTER_MS,
DATEDIFF(MILLISECOND, @StartDB, @EndDB) AS DB_MS,
DATEDIFF(MILLISECOND, @StartInsert, @EndInsert) AS Insert_MS;
SELECT TOP(1) '#CDATAConverts ', * FROM #CDATAConverts ;
SELECT TOP(1) '#DBConverts ', * FROM #DBConverts ;
SELECT TOP(1) '#INsertConverts', * FROM #INsertConverts;
SELECT TOP(1) '#AlterConverts ', * FROM #AlterConverts ;
3条答案
按热度按时间clj7thdc1#
SQL-Server不知道UTF-8(至少不知道您可以有效使用的所有版本)。透过
BCP
从光碟读取utf-8
编码的档案时,会出现limited support starting with v2014 SP2(以及一些关于supported versions的详细数据)(将内容写入光碟时也是一样)。重要须知:
VARCHAR(x)
不是utf-8
。它是 *1字节编码的 * 扩展ASCII,使用代码页(位于归类中)作为字符Map。NVARCHAR(x)
不是utf-16
(但非常接近,它是ucs-2
)。这是一个 *2字节编码 * 的字符串,几乎涵盖了所有已知字符(但存在例外)。utf-8
将使用1个字节用于 * 纯拉丁 * 字符,但使用2个或更多字节用于编码的外来字符集。一个
VARBINARY(x)
将把utf-8
作为一个无意义的字节链保存。简单的
CAST
或CONVERT
将不起作用:VARCHAR
会把每个字节当作一个字符。当然这不是你所期望的结果。NVARCHAR
会把每个2字节的块当作一个字符。同样不是你所需要的。您可以尝试将其写入一个文件,然后使用
BCP
(v2014 SP2或更高版本)将其读取回来。ecfdbz9o2#
XML技巧
以下解决方案适用于任何编码。
有一种巧妙的方法可以完全按照OP的要求去做。编辑:我找到了SO(SQL - UTF-8 to varchar/nvarchar Encoding issue)上讨论的相同方法
过程如下:
工作原理:varbinary和varchar是相同的比特串,只是解释不同,因此,得到的XML确实是UTF8编码的比特流,并且XML解释器能够重构正确的UTF8编码的字符。
请注意
value
函数中的'nvarchar(max)'
。如果使用varchar
,它将破坏多字节字符(取决于排序规则)。BEWARE 2XML无法处理某些字符,例如0x2。当您的字符串包含此类字符时,此技巧将失败。
数据库技巧(SQL Server 2019及更高版本)
这很简单。使用UTF8排序规则创建另一个数据库作为默认排序规则。创建将
VARBINARY
转换为VARCHAR
的函数。返回的VARCHAR
将使用数据库的UTF8
排序规则。插入技巧(SQL Server 2019及更高版本)
这是另一个简单的技巧。创建一个包含一列
VARCHAR COLLATE ...UTF8
的表。将VARBINARY
数据插入此表。它将被正确地保存为UTF8
VARCHAR
。遗憾的是,内存优化表不能使用UTF8
排序规则...更改表技巧(SQL Server 2019及更高版本)
(don't use this,it is necessary,see Plain insert trick)不要使用这个,这是不必要的,请参阅 Plain insert trick)
我试图想出一种使用SQL Server 2019的Utf8排序规则的方法,到目前为止,我已经找到了一种可能的方法,它应该比XML技巧更快(见下文)。
1.创建具有varbinary列的临时表。
1.将varbinary值插入到表中
1.使用utf8归类将表alter列更改为varchar
速度差
测试:
@TextLengthMultiplier
确定转换文本的长度@TextAmount
确定一次转换的数量1cosmwyk3#
您可以使用以下命令将字符串发送到varbinary字段
则使用以下命令以字符串形式检索数据