【问题标题】:MySQL Labs JSON native type: How SUM the result of an array returned by jsn_extract?MySQL Labs JSON 原生类型:如何对 jsn_extract 返回的数组结果求和?
【发布时间】:2015-07-30 18:34:48
【问题描述】:

下面跟随我的场景:

CREATE TABLE `CustomerOrder` (
  `id` bigint(11) unsigned NOT NULL AUTO_INCREMENT,
  `data` json DEFAULT NULL,
  PRIMARY KEY (`id`)
);

我们可以使用这个客户订单 json 作为示例:

{ 
    "creation": "2015-07-30 14:27:51",
    "customer": {
        "id": 2,
        "email": "foo@bar.com"
    },
    "item": [
        {
            "sku": 182,
            "unitPrice": 0.89,
            "qty": 10
        }, {
            "sku": 712,
            "unitPrice": 12.99,
            "qty": 2
        }
    ]
}

在 MySQL 控制台上运行这条 SQL:

SELECT json_extract(data, '$.item[*].unitPrice') AS price FROM CustomerOrder;

我会有这个输出:

[ 0.89, 12.99 ]

现在我如何评估 [0.89 + 12.99] 或 1..N 个元素的项目的总和?

对于我的测试,我使用了这个版本的 MySQL Labs:

http://downloads.mysql.com/snapshots/pb/mysql-5.7.7-labs-json/mysql-5.7.7-labs-json-linux-el6-x86_64.tar.gz

http://mysqlserverteam.com/json-labs-release-native-json-data-type-and-binary-format/

【问题讨论】:

    标签: mysql json nosql mysql-5.7


    【解决方案1】:

    我尝试使用@Rick 的答案,但它对我不起作用。所以我为mysql函数挖掘了mysql文档。这是mysql 5.7.14的工作函数,

    create function sum_array_cells( input_array json )
    returns double
    BEGIN
        DECLARE array_length INTEGER(11);
        DECLARE retval DOUBLE(19,2);
        DECLARE cell_value DOUBLE(19,2);
        DECLARE idx INT(11);
    
        SELECT json_length( input_array ) INTO array_length;
    
        SET retval = 0.0;
    
        SET idx = 0;
    
        WHILE idx < array_length DO
            SELECT json_extract( input_array, concat( '$[', idx, ']' ) )
                INTO cell_value;
    
            SET retval = retval + cell_value;
            SET idx = idx + 1;
        END WHILE;
    
        RETURN retval;
    END
    

    然后使用@Rick 写的函数:

    select sum_array_cells( '[ 0.89, 12.99, 5.23, 2.04 ]' );
    

    【讨论】:

    • 我可以确认这也适用于 MariaDB 10.3.9
    【解决方案2】:

    使用 JSON_TABLE 参见示例:

    SET @datax = '[
      { "product":"apple",  "price": 5}, 
      { "product":"banana",  "price": 7}
    ]';
    
    
    SELECT price.* 
    FROM 
         JSON_TABLE(@datax, '$[*]' COLUMNS (
                    price INTEGER PATH '$.price')
         ) price;`
    

    然后,把价格加起来。

    【讨论】:

      【解决方案3】:

      以下存储函数对我有用:

      delimiter $$
      create function sum_array_cells( input_array json )
      returns double
      language sql deterministic contains sql
      begin
          declare array_length integer;
          declare retval double;
          declare cell_value double;
          declare idx int;
      
          select json_length( input_array ) into array_length;
      
          set retval = 0.0;
      
          set idx = 0;
          while idx < array_length do
              select json_extract( input_array, concat( '$[', idx, ']' ) )
              into cell_value;
      
              set retval = retval + cell_value;
              set idx = idx + 1;
          end while;
      
          return retval;
      
      end$$
      

      然后你会在这样的查询中调用该函数

      select sum_array_cells( '[ 0.89, 12.99, 5.23, 2.04 ]' );
      

      希望这会有所帮助, -瑞克

      【讨论】:

      • 我无法使用您提供的代码创建函数,即使在我让它工作后它也无法工作。这是您的代码的固定版本:
      【解决方案4】:

      MySQL 还不支持表函数(希望很快!),所以我们没有方便的 JSON 函数来从 JSON 数组生成行。目前,Victor Smt 的使用存储过程的建议也是我的首选。

      DagW

      【讨论】:

      • 嗨,Dag,感谢您的回复 - 我找到了另一个解决方案,它不是一个优雅的解决方案,但现在正在工作:stackoverflow.com/questions/21567706/… 我需要将数组(使用 REPLACE)转换为 XML Schema 和 SUM他们的节点。老实说,这是一个非常丑陋的解决方案,但它的工作原理
      【解决方案5】:

      不知道为什么我的代码示例被截断。下面是函数的其余部分:

      
      
          select json_extract( input_array, concat( '$[', idx, ']' ) )
          into cell_value;
      
          set retval = retval + cell_value;
          set idx = idx + 1;
        end while;
      
        return retval;
      
      end$$
      
      

      【讨论】:

      • 可能有点晚了,但我帮你修复了代码示例。您不需要将代码包装在标签中。这些作品现在可能会被删除。
      【解决方案6】:

      试试这个:

      SELECT (json_extract(data, '$.item[0].unitPrice') + json_extract(data, '$.item[1].unitPrice')) AS price  FROM CustomerOrder
      

      【讨论】:

      • 我怎样才能对数据库中的所有元素进行迭代?
      • 当你尝试这个查询时它会给你什么?
      • NULL,但 $.item[0].unitPrice + $.item[1].unitPrice 结果为 13,88。但我需要一个迭代 SUM,因为每个订单可以有 N 个元素。如何用 1000 个商品对客户订单求和?
      • 刚刚编辑了我的答案,如果有人稍后阅读它。还在考虑你的问题
      • 您可以尝试编写一个程序,使用 for 循环为每个客户创建一个包含商品价格的临时表。看看这些程序:stackoverflow.com/questions/5125096/for-loop-example-in-mysql
      【解决方案7】:

      Rick's answer 的优化和更新版本。 在 MySQL 5.7、MySQL 8.0 和 MariaDB 10.3 上测试。

      请注意,它使用小数来确保准确性。随意将其更改为 double 或 int。

      CREATE FUNCTION zy_json_sum ( js_array json )
      RETURNS decimal(18,2) DETERMINISTIC
      BEGIN
          DECLARE total decimal(18,2) DEFAULT 0.0;
          DECLARE idx int DEFAULT 0;
          SELECT json_length( js_array ) INTO idx;
          WHILE idx > 0 DO
              SET idx = idx - 1;
              SELECT json_extract( js_array, concat( '$[', idx, ']' ) ) + total INTO total;
          END WHILE;
          RETURN total;
      END
      

      用法:

      SELECT zy_json_sum( '[1.1, 2.2, "3.3 foo", null, -4.4]' )
      -- Output: 2.20
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-09-06
        • 1970-01-01
        • 2011-01-26
        • 1970-01-01
        • 2019-11-02
        • 1970-01-01
        • 2015-08-29
        相关资源
        最近更新 更多