有没有一种简单的方法可以在T-SQL中将utf-8编码的varbinary(max)列转换为varchar(max)。有点像CONVERT(varchar(max), [MyDataColumn])。最好的解决方案是不需要自定义功能。目前,我在客户端转换数据,但这有一个缺点,即正确的过滤和排序不如服务器端有效。
and*_*ero 12
以下解决方案适用于任何编码。
\n有一种棘手的方法可以完全按照OP的要求进行操作。编辑:我发现在 SO 上讨论的相同方法(SQL - UTF-8 to varchar/nvarchar Encoding issues)
\n过程是这样的:
\nSELECT\n CAST(\n \'<?xml version=\'\'1.0\'\' encoding=\'\'utf-8\'\'?><![CDATA[\' --start CDATA\n + REPLACE(\n LB.LongBinary,\n \']]>\', --we need only to escape ]]>, which ends CDATA section\n \']]]]><![CDATA[>\' --we simply split it into two CDATA sections\n ) + \']]>\' AS XML --finish CDATA\n ).value(\'.\', \'nvarchar(max)\')\nRun Code Online (Sandbox Code Playgroud)\n工作原理:varbinary 和 varchar 是相同的位字符串 - 只是解释不同,因此生成的 xml 确实是 utf8 编码的比特流,并且 xml 解释器能够重建正确的 utf8 编码字符。
\n当心函数\'nvarchar(max)\'中的value。如果您使用varchar,它将破坏多字节字符(取决于您的排序规则)。
注意 2 XML 无法处理某些字符,即 0x2。当你的字符串包含这样的字符时,这个技巧就会失败。
\n这很简单。创建另一个数据库,并将 UTF8 排序规则设置为默认排序规则。创建转换VARBINARY为VARCHAR. 返回的结果VARCHAR将具有UTF8数据库的排序规则。
这是另一个简单的技巧。创建一个只有一列的表VARCHAR COLLATE ...UTF8。将数据插入VARBINARY到该表中。它将被正确保存为UTF8 VARCHAR. 遗憾的是内存优化表不能使用UTF8排序规则......
(不要使用这个,这是不必要的,请参阅普通插入技巧)
\n我试图想出一种使用 SQL Server 2019 的 Utf8 排序规则的方法,到目前为止我已经找到了一种可能的方法,它应该比 XML 技巧更快(见下文)。
\ndrop table if exists\n #bin,\n #utf8;\n \ncreate table #utf8 (UTF8 VARCHAR(MAX) COLLATE Czech_100_CI_AI_SC_UTF8);\ncreate table #bin (BIN VARBINARY(MAX));\n \ninsert into #utf8 (UTF8) values (\'\xc5\xbdlu\xc5\xa5ou\xc4\x8dk\xc3\xbd k\xc5\xaf\xc5\x88 \xc5\x99\xc3\xad\xc4\x8dn\xc4\x9b p\xc4\x9bl \xc4\x8f\xc3\xa1belsk\xc3\xa9 \xc3\xb3dy za svitu m\xc4\x9bs\xc3\xadce.\');\ninsert into #bin (BIN) select CAST(UTF8 AS varbinary(max)) from #utf8;\n \nselect * from #utf8; --here you can see the utf8 string is stored correctly and that\nselect BIN, CAST(BIN AS VARCHAR(MAX)) from #bin; --utf8 binary is converted into gibberish\n \nalter table #bin alter column BIN varchar(max) collate Czech_100_CI_AI_SC_UTF8;\nselect * from #bin; --voial\xc3\xa1, correctly converted varchar\n \nalter table #bin alter column BIN nvarchar(max);\nselect * from #bin; --finally, correctly converted nvarchar \nRun Code Online (Sandbox Code Playgroud)\n考试:
\n@TextLengthMultiplier确定转换文本的长度@TextAmount确定一次将转换多少个------------------\n--TEST SETUP\n--DECLARE @LongText NVARCHAR(MAX) = N\'\xc4\x8desk\xc3\xbd jazyk, Ti\xe1\xba\xbfng Vi\xe1\xbb\x87t, \xd1\x80\xd1\x83\xd1\x81\xd1\x81\xd0\xba\xd0\xb8\xd0\xb9 \xd1\x8f\xd0\xb7\xd1\x8b\xd0\xba, \xe6\xbc\xa2\xe8\xaa\x9e, ]]>\';\n--DECLARE @LongText NVARCHAR(MAX) = N\'JUST ASCII, for LOLZ------------------------------------------------------\';\n\nDECLARE\n @TextLengthMultiplier INTEGER = 100000,\n @TextAmount INTEGER = 10;\n\n---------------------\n-- TECHNICALITIES\nDECLARE\n @StartCDATA DATETIME2(7), @EndCDATA DATETIME2(7),\n @StartTable DATETIME2(7), @EndTable DATETIME2(7), \n @StartDB DATETIME2(7), @EndDB DATETIME2(7),\n @StartInsert DATETIME2(7), @EndInsert DATETIME2(7);\n\ndrop table if exists\n #longTexts,\n #longBinaries,\n #CDATAConverts,\n #DBConverts,\n #INsertConverts;\n\nCREATE TABLE #longTexts (LongText VARCHAR (MAX) COLLATE Czech_100_CI_AI_SC_UTF8 NOT NULL);\nCREATE TABLE #longBinaries (LongBinary VARBINARY(MAX) NOT NULL);\nCREATE TABLE #CDATAConverts (LongText VARCHAR (MAX) COLLATE Czech_100_CI_AI_SC_UTF8 NOT NULL);\nCREATE TABLE #DBConverts (LongText VARCHAR (MAX) COLLATE Czech_100_CI_AI_SC_UTF8 NOT NULL);\nCREATE TABLE #InsertConverts (LongText VARCHAR (MAX) COLLATE Czech_100_CI_AI_SC_UTF8 NOT NULL);\n\ninsert into #longTexts --make the long text longer\n (LongText)\nselect\n REPLICATE(@LongText, @TextLengthMultiplier)\nfrom \n TESTES.dbo.Numbers --use while if you don\'t have number table\nWHERE\n Number BETWEEN 1 AND @TextAmount; --make more of them\n\ninsert into #longBinaries (LongBinary) select CAST(LongText AS varbinary(max)) from #longTexts;\n\n--sanity check...\nSELECT TOP(1) * FROM #longTexts;\n\n------------------------------\n--MEASURE CDATA--\nSET @StartCDATA = SYSDATETIME();\nINSERT INTO #CDATAConverts \n (\n LongText\n )\nSELECT\n CAST(\n \'<?xml version=\'\'1.0\'\' encoding=\'\'utf-8\'\'?><![CDATA[\'\n + REPLACE(\n LB.LongBinary,\n \']]>\',\n \']]]]><![CDATA[>\'\n ) + \']]>\' AS XML\n ).value(\'.\', \'Nvarchar(max)\')\nFROM\n #longBinaries AS LB;\nSET @EndCDATA = SYSDATETIME();\n\n--------------------------------------------\n--MEASURE ALTER TABLE--\nSET @StartTable = SYSDATETIME();\nDROP TABLE IF EXISTS #AlterConverts;\nCREATE TABLE #AlterConverts (UTF8 VARBINARY(MAX));\n\nINSERT INTO #AlterConverts \n (\n UTF8\n )\nSELECT\n LB.LongBinary\nFROM\n #longBinaries AS LB;\n\nALTER TABLE #AlterConverts ALTER COLUMN UTF8 VARCHAR(MAX) COLLATE Czech_100_CI_AI_SC_UTF8;\n--ALTER TABLE #AlterConverts ALTER COLUMN UTF8 NVARCHAR(MAX);\n\nSET @EndTable = SYSDATETIME();\n\n--------------------------------------------\n--MEASURE DB--\nSET @StartDB = SYSDATETIME();\n\nINSERT INTO #DBConverts \n (\n LongText\n )\nSELECT\n FUNCTIONS_ONLY.dbo.VarBinaryToUTF8(LB.LongBinary)\nFROM\n #longBinaries AS LB;\n\nSET @EndDB = SYSDATETIME();\n\n--------------------------------------------\n--MEASURE Insert--\nSET @StartInsert = SYSDATETIME();\n\nINSERT INTO #INsertConverts \n (\n LongText\n )\nSELECT\n LB.LongBinary\nFROM\n #longBinaries AS LB;\n\nSET @EndInsert = SYSDATETIME();\n\n--------------------------------------------\n-- RESULTS\nSELECT\n DATEDIFF(MILLISECOND, @StartCDATA, @EndCDATA) AS CDATA_MS,\n DATEDIFF(MILLISECOND, @StartTable, @EndTable) AS ALTER_MS,\n DATEDIFF(MILLISECOND, @StartDB, @EndDB) AS DB_MS,\n DATEDIFF(MILLISECOND, @StartInsert, @EndInsert) AS Insert_MS;\n\nSELECT TOP(1) \'#CDATAConverts \', * FROM #CDATAConverts ;\nSELECT TOP(1) \'#DBConverts \', * FROM #DBConverts ;\nSELECT TOP(1) \'#INsertConverts\', * FROM #INsertConverts;\nSELECT TOP(1) \'#AlterConverts \', * FROM #AlterConverts ;\nRun Code Online (Sandbox Code Playgroud)\n
SQL Server不知道UTF-8(至少您可以有效使用的所有版本)。有开始v2014 SP2有限的支持(以及有关的一些细节支持版本读取时)utf-8从光盘通过编码文件BCP(同样为内容写入到光盘中)。
重要提示:
VARCHAR(x)是不是utf-8。它是1字节编码的扩展ASCII,使用代码页(存在于归类中)作为字符映射。
NVARCHAR(x)是不是utf-16(但非常接近它,它是ucs-2)。这是一个2字节编码的字符串,几乎覆盖了所有已知字符(但存在例外)。
utf-8对于纯拉丁字符,将使用1个字节,但对已编码的外部字符集则使用2个甚至更多个字节。
A VARBINARY(x)将保留utf-8为无意义的字节链。
简单CAST或CONVERT不起作用:VARCHAR将每个单个字节当作一个字符。当然,这不是您期望的结果。NVARCHAR将每个2字节的块视为一个字符。再次不是您需要的东西。
您可能会尝试将其写出到文件中并使用BCP(v2014 SP2或更高版本)将其读回。但是我为您看到的更好的机会是CLR功能。
| 归档时间: |
|
| 查看次数: |
1327 次 |
| 最近记录: |