在MySQL中,使用筛选条件找出JSON数组中所有元素的总和。

huangapple go评论74阅读模式
英文:

Find out the sum of all the elements in json array in mysql with filtering

问题

  1. 是的,amounts 数组和 flag 数组中的元素顺序与 details 中的元素顺序相同。因此,您可以根据这个顺序正确计算总和。

  2. 如果您的MariaDB版本不支持 json_table,您目前的查询方法是一种有效的方式。但如果要在数据库层面更高效地进行求和,您可能需要考虑升级到支持 json_table 的版本,这将使操作更简单和高效。否则,您可能需要继续在应用程序代码中进行求和处理,就像您目前所做的那样。

英文:

I am using mariadb version 10.2.43-MariaDB-1:10.2.43+maria~bionic

Table schema

The table has a columns id attribute and details array with the following two rows of data :

{
  "id": 9,
  "details": [
    {
      "amount": 100,
      "flag": true,
      "fieldA": "abcd"
    },
    {
      "amount": 101,
      "flag": false,
      "fieldB": "bcde"
    },
    {
      "amount": 103,
      "flag": true,
      "fieldA": "abcd"
    }
  ]
},
{
  "id": 10,
  "details": [
    {
      "amount": 110,
      "flag": false,
      "fieldA": "abcd"
    },
    {
      "amount": 102,
      "flag": true,
      "fieldB": "bcde"
    }
  ]
}

I want to calculate the sum of amounts when the flag is true.

  • For id = 9 amounts = [100, 103]
  • For id = 10 amounts = [102]
  • Total = 100 + 103 + 102 = 305

On searching online I found a few answers suggesting json_table but we are using older version of mariadb which does not support json_table.

I have used this way to extract the amounts and flags and then finally handled the sum in code.

SELECT JSON_EXTRACT(features,'$. details[*].amount') as amounts,
       JSON_EXTRACT(features,'$.details[*].flag') as flag 
  FROM table
 WHERE JSON_EXTRACT(features,'$.details[*].flag') != 'NULL';

Output

-------------------------------------------------------------+---------------------------------------------------------------------+
| [500000, 1000000]                                           | [false, false]                                                      |
| [1100000]                                                   | [false]                                                             |
| [1000000]                                                   | [false]                                                             |
| [500000, 1000000]                                           | [false, false]                                                      |
| [100000]                                                    | [false]                                                             |
| [5000000]                                                   | [false]                                                             |                                                       |
| [50000]                                                     | [false]                                                             |
| [500000]                                                    | [false]                                                             |
| [500000]                                                    | [false]                                                             |
| [10000]                                                     | [true]                                                              |
| [49998]                                                     | [true]                                                              |
| [600000, 399980]                                            | [false, true]    

Questions:

  1. I want to know if the order of elements in amounts array and flag array will be the same as that in details. (otherwise the sum I am calculating will be wrong).
  2. Is there a more efficient way to calculate the sum without using code?

答案1

得分: 1

  1. 我想知道金额数组和标志数组的元素顺序是否与详情中的顺序相同(否则我计算的总和将是错误的)。

  2. 是否有一种更有效的方法来计算总和,而不使用代码?

MariaDB不支持json_table,它可以将JSON数组展开为行。但是我们可以借助数字表和其他MariaDB JSON函数来模拟它。

有许多不同的方法可以创建数字表,可以是查询内联的方式,也可以永久存储在一个表中。这里我只是使用了一个固定的数字列表:

mytable t中选择json_value(t.details, concat('$[', n.i, '].amount'))的总金额
从mytable t
内部加入(选择0 as i联合全部选择1联合全部选择2)n
其中n.i < json_length(t.details)
并且json_value(t.details, concat('$[', n.i, '].flag')) = True

联接n生成JSON数组中每个项目的一行;鉴于元素的索引,我们可以生成其金额和标志的适当JSON路径(这就是concat()的作用),并使用json_value()访问这些值。

我手头没有MariaDB 10.2数据库来测试,但这里是**一个MariaDB 10.3示例**。请注意,数据库正确识别了flag属性中的布尔值,这简化了where子句。

英文:

> 1) I want to know if the order of elements in amounts array and flag array will be the same as that in details. (otherwise the sum I am calculating will be wrong).

The documentation says:

> Extracts data from a JSON document. [...]. Returns all matched values; either as a single matched value, or, if the arguments could return multiple values, a result autowrapped as an array in the matching order.

So yes, both calls to json_extract will give you an ordered slice of the json array. But note that, as far as your query goes, you are still left with the task of unesting each slice (the two arrays that you are getting)... which is the core of the task, and brings us to your second question.


> 2. Is there a more efficient way to calculate the sum without using code?

MariaDB does not support json_table, which can expand a json array to rows. But we can emulate it with the help of a table of numbers and of other MariaDB json functions.

There are many different ways that you can create a table of numbers, either inline in the query or permanently stored in a table. Here I am just using a fixed list of a few numbers :

select sum( json_value( t.details, concat(&#39;$[&#39;, n.i, &#39;].amount&#39;) ) ) total_amount
from mytable t
inner join (select 0 as i union all select 1 union all select 2) n 
    on n.i &lt; json_length(t.details)
where json_value( t.details, concat(&#39;$[&#39;, n.i, &#39;].flag&#39;) ) 

The join on n generate one row for each item in the JSON array; given the index of the element, we can generate the proper json path to its amount and flag (that's what the concat()s do), and access the values with json_value().

I don't have a MariaDB 10.2 database at hand to test, but here is a MariaDB 10.3 fiddle. Note the database properly recognizes the boolean value in the flag attribute, which simplifies the where clause.

答案2

得分: 1

以下是您要翻译的内容:

一种选项是在通过 Recursive CTE 循环时使用 JSON_EXTRACT() 函数,该函数在 10.2.2 + 版本中受支持,如下查询所示:

WITH RECURSIVE cte AS
(
 SELECT 0 i
 UNION ALL
 SELECT i + 1 i
   FROM cte
  WHERE i + 1 &lt;= ( SELECT MAX(JSON_LENGTH(features, &#39;$.details&#39;)) FROM t ) 
)
SELECT SUM(CASE 
           WHEN JSON_EXTRACT(features, CONCAT(&#39;$.details[&#39;,i,&#39;].flag&#39;)) = &#39;true&#39; THEN
                CAST(JSON_EXTRACT(features, CONCAT(&#39;$.details[&#39;,i,&#39;].amount&#39;)) AS DOUBLE)
            END) AS sum_amount 
  FROM cte,
       t

&lt;kbd&gt;[演示](https://dbfiddle.uk/XP1rxC7P)&lt;/kbd&gt;
英文:

One option is to use JSON_EXTRACT() function while looping through by Recursive CTE which's supported for 10.2.2 + as in the following query

WITH RECURSIVE cte AS
(
 SELECT 0 i
 UNION ALL
 SELECT i + 1 i
   FROM cte
  WHERE i + 1 &lt;= ( SELECT MAX(JSON_LENGTH(features, &#39;$.details&#39;)) FROM t ) 
)
SELECT SUM(CASE 
           WHEN JSON_EXTRACT(features, CONCAT(&#39;$.details[&#39;,i,&#39;].flag&#39;)) = &#39;true&#39; THEN
                CAST(JSON_EXTRACT(features, CONCAT(&#39;$.details[&#39;,i,&#39;].amount&#39;)) AS DOUBLE)
            END) AS sum_amount 
  FROM cte,
       t

<kbd>Demo</kbd>

huangapple
  • 本文由 发表于 2023年5月14日 04:14:28
  • 转载请务必保留本文链接:https://go.coder-hub.com/76244696.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定