使用 MariaDB 在 JSON_CONTAINS() 中未考虑德语变音符号

Posted

技术标签:

【中文标题】使用 MariaDB 在 JSON_CONTAINS() 中未考虑德语变音符号【英文标题】:German Umlaute not considered in JSON_CONTAINS() with MariaDB 【发布时间】:2021-09-20 23:51:19 【问题描述】:

第一次在这里发帖。

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

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

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”,它会被正确找到,所以它基本上归结为德语变音符号(ö,ä,ü)没有以某种方式正确处理。

我也在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个结果

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

产品:

column data
flight_data "pilotname":"J\u00fchn D\u00fce"

本地:

column data
flight_data "pilotname":"J\u00fchn D\u00fce"

从逻辑上讲,数据被转换了。没关系,因为数据然后根据UTF-8显示,然后正确显示(“Jühn Düe”)

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

不同之处在于,在我的本地环境中,我使用的是 mysql 8.0(它是一个宅基地服务器,所以选择 @@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个结果

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

非常感谢大家的帮助!

【问题讨论】:

【参考方案1】:

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

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

特别是,MariaDB 与 MySQL 中 JSON 的实现并不完全兼容。 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 Versus _unicode_ci Collat​​ions”部分中https://dev.mysql.com/doc/refman/8.0/en/charset-unicode-sets.html 中的说明

【讨论】:

感谢比尔的回答!绝对同意你说的。我从研究中知道,两者之间存在相当多的兼容性问题。但是,我还没有遇到任何具有两个不同系统的问题,显然现在我必须重新考虑我的开发设置。但是,如果我在本地切换到 MariaDB,问题仍然存在 - 如果在字符串中使用特殊字符(如 Umlaute öäü 或 ß、é è 等),JSON_CONTAINS 也不会提供结果......有没有办法解决这个问题MariaDB? 我认为这忽略了基本问题,即数据没有被存储为 UTF-8 - 或者连接可能不使用 UTF-8 ......并且假设是错误的,尤其是使用德语名称,可以轻松地使用两种表示元音连字的方式;甚至不是从意第绪语正字法开始(这可能很难在比较中表示,因为它太不同但会导致发音相似)......在这两种情况下,结果都应该是0 @MartinZeitler,是的,我认为 OP 应该仔细检查所有内容:表/列字符集和默认排序规则、会话字符集和排序规则等。开发环境之间明显不同和生产环境。【参考方案2】:

感谢大家的投入和回复!

我为这个问题找到了不同的解决方案。也许它可以帮助某人..

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

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

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

【讨论】:

以上是关于使用 MariaDB 在 JSON_CONTAINS() 中未考虑德语变音符号的主要内容,如果未能解决你的问题,请参考以下文章

CentOS 7.0 使用 yum 安装 MariaDB 与 MariaDB 的简单配置

安装的mariadb怎么在配置文件里设置密码

Centos7 使用yum安装MariaDB与MariaDB的简单配置与使用

MariaDB 在使用大型 WHERE IN 时崩溃

如何在 from 子句中使用子查询创建视图 - Mariadb

为啥我可以在 Windows 7 上使用 XAMPP 登录 MariaDB