在 MariaDB 的 JSON_CONTAINS() 中不考虑德语元音变音

German Umlaute not considered in JSON_CONTAINS() with MariaDB

第一次在这里发帖。

我的 PROD 服务器和本地环境出现意外行为问题。

以下是一些背景情况: 在我的应用程序(后端 Laravel 7、前端常规 html/javascript)中,我需要根据存储在其中一列中的 JSON 数据搜索特定 table 中的条目:

Table: flights
columns: id, date, passengers, ... pilot_id, second_pilot_id, flight_data, updated_at, created_at

有些航班通过 pilot_id 或 second_pilot_id 直接链接到飞行员或第二名飞行员。到目前为止还不错,因为我可以很容易地查询它们。然而,也有航班条目,其中没有注册用户进行输入,它们仅由输入的名称表示。这仅在名称不包含特殊字符时有效,特别是德语元音变音符(ö、ä、ü),也不适用于其他特殊字符,如 â 或 ß 或 é、è 等。但仅在 PROD 上,在本地一切正常,即使有特殊字符。

flight_data 在我的迁移文件中的数据类型为“JSON”。

$table->json('flight_data') ... 

现在的问题:

在我的本地环境中,我可以 运行 以下内容并将返回结果:

... ->where(function($q) use ($r) {
$q->whereRaw("IF(payee = 2, JSON_CONTAINS(flight_data, '{\"second_pilotname\":\"$r\"}'), JSON_CONTAINS(flight_data, '{\"pilotname\":\"$r\"}'))");
})->...

如预期的那样,这将使我的示例结果毫无问题

($r填写的是飞行员的具体名字,在我的例子中他叫“Jöhn Düe”)

如果我在我的 PROD 系统上 运行 这个,我将不会得到任何回报。我追踪到 JSON_CONTAINS() 函数,它阻止了结果。我也试过玩“Joehn Duee”,它会被正确找到,所以它基本上归结为德语 Umlaute (ö, ä, ü) 没有以某种方式正确处理。

我也在 phpmyadmin 中尝试了一些 SQL 语句,结果如下:

本地

select id, flight_data, comments, updated_at from logbook where JSON_CONTAINS(flight_data, '{"pilotname": "Juehn Duee"}')

找到 1 个结果

select id, flight_data, comments, updated_at from logbook where JSON_CONTAINS(flight_data, '{"pilotname": "Jühn Düe"}')

找到 1 个结果

产品

select id, flight_data, comments, updated_at from logbook where JSON_CONTAINS(flight_data, '{"pilotname": "Juehn Duee"}')

找到 1 个结果

select id, flight_data, comments, updated_at from logbook where JSON_CONTAINS(flight_data, '{"pilotname": "Jühn Düe"}')

找到 0 个结果

我还检查了存储的原始数据:

产品:

数据
flight_data {"飞行员姓名":"J\u00fchn D\u00fce"}

本地:

数据
flight_data {"飞行员姓名":"J\u00fchn D\u00fce"}

因此从逻辑上讲,数据已转换。这没关系,因为数据随后根据 UTF-8 显示,然后正确显示 ("Jühn Düe")

问题是,我需要在后端比较这些数据。

不同之处在于,在我的本地环境中,我使用的是 MYSQL 8.0(这是一个宅基地服务器,所以 select @@version; => 8.0.23-0ubuntu0.20.04.1)在 PROD(托管服务器)上,我看到“10.3.28-MariaDB-log-cll-lve”

因此区别很明显,MariaDB 与 MYSQL 以及德语元音变音的处理。

我尝试了各种改变数据库条目的转换/字符集的方法,但都没有解决问题。我搜索了很长时间以寻找各种类似的问题,但大多数问题导致数据存储不是以 UTF-8 格式存储的 - 我检查过,这里就是我的情况。

即使查询原始数据也无法正常工作:

以下内容既不适用于 PROD,也不适用于 LOCAL:

select id, flight_data, comments, updated_at from logbook where JSON_CONTAINS(flight_data, '{"pilotname": "J\u00fchn D\u00fce"}')

找到 0 个结果

你能帮我弄清楚我在这里遗漏了什么吗? 显然它必须对数据库做一些事情,我还能检查什么或需要更改什么?

非常感谢大家的帮助!

您应该在开发中使用与在生产中使用的相同的软件。同品牌同版本。否则您可能会遇到这些不兼容的功能。

MariaDB 于 2010 年开始作为 MySQL 项目的一个分支,此后两者逐渐分化。 MySQL 实现了新功能,MariaDB 可能会或可能不会实现类似的功能,要么通过从 MySQL 项目中挑选代码,要么通过实现自己的原始代码。所以随着时间的推移,这两个项目变得越来越不兼容。此时,在最初的分叉之后 10 多年,您应该将 MariaDB 视为一个不同的软件产品。不要指望它的任何部分与 MySQL.

保持兼容

特别是,MariaDB 中 JSON 与 MySQL 的实现并不完全兼容。 MariaDB 为 JSON 数据类型创建自己的原始代码作为 LONGTEXT 的别名。所以内部实现大不相同

您询问是否有需要更改的内容。

由于您在生产中使用的是 MariaDB,而不是 MySQL,因此您应该在开发环境中使用 MariaDB 10.3.28,以确保与您在生产中使用的数据库品牌和版本兼容。


我认为问题是整理问题。一些 unicode 归类实现字符扩展,因此 ue = ü 在德语归类中为真。

这是一个使用 MySQL 5.7 的测试,这是我手边的(我不使用 MariaDB):

mysql> select 'Juehn Duee' collate utf8mb4_unicode_520_ci = 'Jühn Düe' as same;
+------+
| same |
+------+
|    0 |
+------+

mysql> select 'Juehn Duee' collate utf8mb4_german2_ci = 'Jühn Düe' as same;
+------+
| same |
+------+
|    1 |
+------+

如您所见,这与JSON无关,只是与字符串比较和使用哪种排序规则有关。

请参阅“_general_ci 与 _unicode_ci 排序规则”部分中 https://dev.mysql.com/doc/refman/8.0/en/charset-unicode-sets.html 中的解释

感谢大家的投入和回复!

我想出了一个不同的解决方案来解决这个问题。也许它可以帮助某人..

我退后一步检查我是如何存储数据的。为此,我使用了 json_encode(),它创建了 table 内容,如上所示。通过仅使用原始数组来保存它,它可以正常工作

$insert->pilotname = ['pilotname' => $request->pilotname];

不知何故,之前的数据存储已经是问题了。