在 SQL Server 中存储特殊字符(德语、法语、西班牙语)会导致奇怪的字符串

Flo*_*Flo 4 collation character-set utf-8 json sql-server-2019

我试图将德语 \xc3\x9f 存储在 SQL Server 中,但结果是 \xc3\x83\xc5\xb8。\n例如“Gleimstra\xc3\x9fe”存储为“Gleimstra\xc3\x83\” xc5\xb8e"

\n

但其他角色也会发生这种情况:

\n
    \n
  • “K\xc3\xb6nig-Karl-Stra\xc2\xad\xc3\x9fe”存储为“K\xc3\x83\xc2\xb6nig-Karl-Stra\xc3\x83\xc5\xb8e”
  • \n
  • “Quai Andr\xc3\xa9-Citro\xc3\xabn”存储为“Quai Andr\xc3\x83\xc2\xa9 Citro\xc3\x83\xc2\xabn”
  • \n
  • “Carrer dels Adre\xc3\xa7adors 存储为“Carrer dels Adre\xc3\x83\xc2\xa7adors”
  • \n
\n

我查看了数据库排序规则,它显示SQL_Latin1_General_CP1_CI_AS。然后我用谷歌搜索并找到了这个

\n

但我不知道我现有的排序规则是否导致了问题,或者我需要做什么才能存储 \xc3\xb6、\xc3\x9f、\xc3\xa9、\xc3\xab、\ 等特殊字符xc3\xa7、\xc3\xb1、\xc3\xad。

\n

更新1

\n

事实上,我将字符串存储在一nvarchar列中。我通过 .NET 应用程序将数据发送到数据库。这是我在 web.config 中作为连接字符串的内容:
\n<add name="conn" connectionString="data source=(local)\\sql;Initial Catalog=mydb;User Id=myuser;Password=mypassword;" providerName="System.Data.SqlClient"/>

\n

更新2

\n

我从 JSON 文件中读取值,并将其传递给 a Newtonsoft.Json.Linq.JArray,然后将其传递给函数,如下所示:

\n
    InsertStreetId(item.SelectToken("location.street").Value(Of String))\n\n\n  Public Shared Function InsertStreetId(ByVal street As String) As Integer\n        Dim streetId As Integer\n\n        Dim myConnection As SqlConnection = GetConnection()\n        Dim cmd As New SqlCommand("INSERT INTO geo_streets(streetname) VALUES (@streetname)" +\n                             ";SELECT CAST(scope_identity() as int);", myConnection)\n        cmd.Parameters.Add(New SqlParameter("@streetname", street))\n\n        Try\n            myConnection.Open()\n            streetId = CInt(cmd.ExecuteScalar)\n        Catch ex As Exception\n        Finally\n            myConnection.Close()\n        End Try\n        Return streetId\n    End Function\n
Run Code Online (Sandbox Code Playgroud)\n

更新3

\n

好的,这就是我所做的:

\n
Dim myConnection As SqlClient.SqlConnection = GetConnection()\n\nDim cmd As New SqlClient.SqlCommand("UPDATE cities SET name=@name,updatedate=getdate() WHERE geonameid=@geonameid", myConnection)\ncmd.Parameters.Add(New SqlClient.SqlParameter("@name", "K\xc4\x85ty Wroc\xc5\x82awskie"))\ncmd.Parameters.Add(New SqlClient.SqlParameter("@geonameid", 6474))\n\nmyConnection.Open()\ncmd.ExecuteNonQuery\nmyConnection.Close()\n
Run Code Online (Sandbox Code Playgroud)\n

这会将字符串“K\xc4\x85ty Wroc\xc5\x82awskie”正确插入到我的数据库中。所以我猜数据库排序规则工作正常以支持特殊字符?

\n

如果是这样,我现在可能遇到另一个问题,但请告诉我是否最好就此创建一个新问题:\n如上所述,新插入的记录工作正常。但是,在我的数据库中,我仍然有如下值:“S\xc3\x84p\xc4\x82\xc5\x82lno Kraje\xc4\xb9skie”(应该是“S\xc4\x99p\xc3\xb3lno Kraje\xc5\x84skie”) ”)。
\n我现在应该对所有列进行查找/替换吗?如果是这样,我在哪里可以找到映射表来知道要查找什么以及用什么替换,因为我不知道从哪里开始。

\n

Sol*_*zky 7

不知何故,数据以 UTF-8 字节的形式发送。由于您的数据库排序规则为SQL_Latin1_General_CP1_CI_AS,因此使用代码页 1252 (Latin1) 来存储VARCHAR数据。该\xc3\x9f字符被编码为字节C3 9F。在代码页 1252 中,这两个字节相当于以下字符:\xc3\x83\xc5\xb8

\n

您可以通过在 SSMS 或 Visual Studio Code 等中执行以下命令来亲自查看这一点:

\n
SELECT CHAR(0xC3) + CHAR(0x9F);\n
Run Code Online (Sandbox Code Playgroud)\n

解决此问题的一种选择是使用 UTF-8 排序规则(即以 结尾的排序规则_UTF8)作为数据库默认排序规则,因为您使用的是 SQL Server 2019,所以您可以这样做。

\n

如果您不能或根本不想将数据库默认排序规则更改为排序规则_UTF8,那么您将需要找到一种方法来更改连接的编码/区域性/区域设置/排序规则以匹配您的数据库代码页(即Windows 1252)。幸运的是,代码页 1252 确实存储了这些字符(即\xc3\xb6、\xc3\x9f、\xc3\xa9、\xc3\xab、\xc3\xa7、\xc3\xb1、\xc3\xad),但如果您需要要存储更大范围的字符,您可能需要切换到 UTF-8 排序规则(如果坚持使用VARCHAR数据),或者切换到使用NVARCHAR列/数据(在这种情况下,您需要在应用程序中为字符串文字添加前缀,使用大写的N,和/或指定参数的数据类型NVARCHAR而不是VARCHAR数据类型,具体取决于数据的发送方式)。这两个选项都是 Unicode,可以处理所有字符。如果切换到NVARCHAR数据和列(这可能是一个更大的更改),数据库连接软件假定编码为 UTF-16 Little Endian,而无需您指定。

\n
\n

问题的更新 2指出了两个可能的嫌疑人:

\n
    \n
  1. \n
    \n

    我从 JSON 文件中读取值,并将其传递到 Newtonsoft.Json.Linq.JArray 中

    \n
    \n

    和:

    \n
  2. \n
  3. \n
    \n

    然后将其传递给一个函数:

    \n
    cmd.Parameters.Add(New SqlParameter("@streetname", street))\n
    Run Code Online (Sandbox Code Playgroud)\n
    \n
  4. \n
\n

让我们从第二项开始,因为它是一个问题,即使它不是这个特定问题的根源。

\n

您正在使用以下构造函数SqlParameterSqlParameter(String, Object) (我猜它与臭名昭著的SqlParameterCollection.AddWithValue(String, Object)方法相同)。这里的问题是数据类型是从值推断出来的,有时在尝试猜测数据类型时可能会出错。明确指定数据类型总是更好。而且您知道数据类型。因此,请使用以下内容(如果不是 500,请务必调整参数的大小以匹配实际列):

\n
Dim param as New SqlParameter("@streetname", SqlDbType.NVarChar, 500)\nparam.Value = street\ncmd.Parameters.Add(param)\n
Run Code Online (Sandbox Code Playgroud)\n

您应该首先进行更改,看看它是否解决了问题(然后修复在未显式设置数据类型的情况下创建参数的所有代码)。虽然这在任何一种情况下都会有所改进,但我怀疑它并不能真正解决问题。我认为它必须推断类型是 XML 才能转换为 UTF-8 编码,并且我不相信只有名称而值中没有实际 XML 的情况会发生这种情况。

\n

另一个有趣的项目是通过 Json.NET 从文件中读取数据。如果您需要显式设置输出编码,否则它默认为 UTF-8,我不会感到惊讶。看来文件被正确读取,因为\xc3\x9f确实编码为 UTF-8 作为字节C3 9F

\n

因此,如果修复创建方式SqlParameter无法解决问题,那么您需要考虑设置 JSON 的编码。

\n
\n

强烈建议您将进入方法的值的字节保存InsertStreetId到文件中以进行调试,使用类似以下内容:

\n
BitConverter.ToString( BitConverter.GetBytes( street ) )\n
Run Code Online (Sandbox Code Playgroud)\n

我怀疑您会看到如下值:

\n

4700 6C00 6500 6900 6D00 7300 7400 7200 6100 C300 7801 6500

\n

这是 的 UTF-16 LE 表示形式N\'Gleimstra\xc3\x83\xc5\xb8e\'。如果是这种情况,则问题发生在InsertStreetId调用该方法之前,这意味着这与 SQL Server 无关。

\n