在 MySQL 和 MariaDB 上按 JSON 数组列对行进行排序

Jon*_*eir 5 mysql json sql-order-by mariadb mysql-json

PostgreSQL 允许按数组对行进行排序。它比较每个数组的第一个值,然后比较第二个值,依此类推(fiddle):

select array[2, 4] as "array"
union
select array[10] as "array"
union
select array[2, 3, 4] as "array"
union
select array[10, 11] as "array"
order by "array"
Run Code Online (Sandbox Code Playgroud)
大批
[2,3,4]
[2, 4]
[10]
[10, 11]

MySQL 和 MariaDB 上最接近的等效项似乎是JSON 数组

MySQL 显然或多或少随机地按长度对数组进行排序( fiddle):

select json_array(2, 4) as `array`
union
select json_array(10) as `array`
union
select json_array(2, 3, 4) as `array`
union
select json_array(10, 11) as `array`
order by `array`
Run Code Online (Sandbox Code Playgroud)
大批
[10]
[2, 4]
[10, 11]
[2,3,4]

MariaDB 有点按值排序,但执行不正确(fiddle)。整数像字符串一样排序(10before 2),具有相同开头的数组则相反([10, 11]before [10]):

select json_array(2, 4) as `array`
union
select json_array(10) as `array`
union
select json_array(2, 3, 4) as `array`
union
select json_array(10, 11) as `array`
order by `array`
Run Code Online (Sandbox Code Playgroud)
大批
[10, 11]
[10]
[2,3,4]
[2, 4]

有没有办法在 MySQL 和/或 MariaDB 上复制 PostgreSQL 的数组排序?

数组可以有任意长度,但我不知道最大长度。

我目前看到的唯一解决方法/黑客是将数组连接0成一个字符串,并用s 将值左侧填充到相同的长度:等002.004010.011

Pro*_*Dec 3

对我来说这看起来像是一个错误。根据文档

如果两个 JSON 数组具有相同的长度并且数组中相应位置的值相等,则它们相等。

如果数组不相等,则它们的顺序由第一个存在差异的位置的元素确定。该位置中值较小的数组首先排序。如果较短数组的所有值都等于较长数组中的相应值,则较短数组先排序。

ORDER BY看起来根本不遵守这样的规则。

这是MySQL 8 和 5.7 的数据库小提琴

我正在使用CROSS JOIN显式比较来获得预期的顺序。

SELECT f.`array`, SUM(f.`array` > g.`array`) cmp
FROM jsons f
CROSS JOIN jsons g
GROUP BY f.`array`
ORDER BY cmp
;
Run Code Online (Sandbox Code Playgroud)

MySQL 5.7 还有一个观察结果,当使用子查询时,>正在执行诸如字符串比较之类的操作,需要再次转换JSON才能获得正确的结果,而 MySQL8 不需要这样做。

SELECT f.`array`, SUM(CAST(f.`array` AS JSON) > CAST(g.`array` AS JSON)) cmp
FROM (
 select json_array(2, 4) as `array`
 union
 select json_array(10) as `array`
 union
 select json_array(2, 3, 4) as `array`
 union
 select json_array(10, 11) as `array`
) f
CROSS JOIN (
 select json_array(2, 4) as `array`
 union
 select json_array(10) as `array`
 union
 select json_array(2, 3, 4) as `array`
 union
 select json_array(10, 11) as `array`
) g
GROUP BY f.`array`
ORDER BY cmp
;
Run Code Online (Sandbox Code Playgroud)

上面的内容在 MariaDB 中不起作用

https://mariadb.com/kb/en/incompatibilities-and-feature-differences- Between-mariadb-106-and-mysql-80/

在MySQL中,JSON是根据json值进行比较的。在 MariaDB 中,JSON 字符串是普通字符串,并作为字符串进行比较。

下面的查询适用于 MariaDB

WITH RECURSIVE jsons AS (
 select json_array(2, 4) as `array`
 union
 select json_array(10) as `array`
 union
 select json_array(2, 3, 4) as `array`
 union
 select json_array(10, 11) as `array`
),
maxlength AS (
 SELECT MAX(JSON_LENGTH(`array`)) maxlength
 FROM jsons
),
numbers AS (
 SELECT 0 AS n
 FROM maxlength
 UNION ALL
 SELECT n + 1
 FROM numbers
 JOIN maxlength ON numbers.n < maxlength.maxlength - 1
),
expanded AS (
 SELECT a.`array`, b.n, JSON_EXTRACT(a.`array`, CONCAT('$[', b.n, ']')) v
 FROM jsons a
 CROSS JOIN numbers b
),
maxpadding AS (
 SELECT MAX(LENGTH(v)) maxpadding
 FROM expanded
)
SELECT a.`array`
FROM expanded a
CROSS JOIN maxpadding b
GROUP BY a.`array`
ORDER BY GROUP_CONCAT(LPAD(a.v, b.maxpadding, '0') ORDER BY a.n ASC)
Run Code Online (Sandbox Code Playgroud)