You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过MySQL将货币时序查询结果转换为指定JSON格式?

百万级货币时间序列数据的MySQL JSON格式转换方案

问题描述

我有一张百万级数据量的currency表,结构包含时间戳字段datetime以及上百种货币字段(如Rupee、Yen等)。执行基础查询:

SELECT datetime, Rupee, Yen 
FROM currency 
WHERE datetime BETWEEN value1 AND value2

得到的JSON结果是按时间戳聚合的单条记录格式,但需要转换为按货币分组,每条货币对应一组时间序列数据的目标格式:

原查询JSON结果

[
  {
    "datetime": "2019-02-16T10:40:00.000Z",
    "Rupee": 10,
    "Yen": 60
  },
  {
    "datetime": "2019-02-16T10:50:00.000Z",
    "Rupee": 30,
    "Yen": 70
  },
  {
    "datetime": "2019-02-16T10:55:00.000Z",
    "Rupee": 40,
    "Yen": 80
  },
  {
    "datetime": "2019-02-16T10:58:00.000Z",
    "Rupee": 50,
    "Yen": 90
  }
]

目标JSON格式

[
  {
     "currency": "Rupee",
      "timeseriesdata": [
        ["2019-02-16T10:40:00.000Z",10],
        ["2019-02-16T10:50:00.000Z", 30],
        ["2019-02-16T10:55:00.000Z", 40],
        ["2019-02-16T10:58:00.000Z", 50]
      ]
  },
  {
    "currency": "Yen",
      "timeseriesdata": [
        ["2019-02-16T10:40:00.000Z",60],
        ["2019-02-16T10:50:00.000Z", 70],
        ["2019-02-16T10:55:00.000Z", 80],
        ["2019-02-16T10:58:00.000Z", 90]
      ]
  }
]

需求:不通过Node.js/JavaScript处理,直接用MySQL实现转换,适配百万级数据量和上百种货币的场景。

解决方案

1. 固定货币列场景(少量货币)

如果货币列固定且数量不多,可直接用UNION ALL结合JSON函数构造结果:

SELECT JSON_ARRAYAGG(
    JSON_OBJECT(
        'currency', currency_name,
        'timeseriesdata', timeseries_data
    )
) AS result
FROM (
    -- 处理Rupee
    SELECT 
        'Rupee' AS currency_name,
        JSON_ARRAYAGG(JSON_ARRAY(datetime, Rupee)) AS timeseries_data
    FROM currency
    WHERE datetime BETWEEN value1 AND value2
    UNION ALL
    -- 处理Yen
    SELECT 
        'Yen' AS currency_name,
        JSON_ARRAYAGG(JSON_ARRAY(datetime, Yen)) AS timeseries_data
    FROM currency
    WHERE datetime BETWEEN value1 AND value2
) AS currency_series;

该方法简单直接,但不适用于上百种货币的场景。

2. 动态货币列场景(上百种货币)

通过动态SQL+存储过程自动遍历所有货币列,生成目标JSON:

步骤1:创建存储过程

DELIMITER //

CREATE PROCEDURE GetCurrencyTimeseries(IN start_datetime DATETIME, IN end_datetime DATETIME)
BEGIN
    DECLARE done INT DEFAULT FALSE;
    DECLARE col_name VARCHAR(255);
    -- 遍历currency表中除datetime外的所有列(即货币列)
    DECLARE cur CURSOR FOR 
        SELECT column_name 
        FROM information_schema.columns 
        WHERE table_schema = DATABASE() 
          AND table_name = 'currency' 
          AND column_name != 'datetime';
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;

    SET @json_result = '[]';

    -- 创建临时表存储时间范围内的数据,避免重复扫描原表
    CREATE TEMPORARY TABLE temp_currency 
    SELECT datetime, * FROM currency WHERE datetime BETWEEN start_datetime AND end_datetime;

    OPEN cur;
    read_loop: LOOP
        FETCH cur INTO col_name;
        IF done THEN
            LEAVE read_loop;
        END IF;

        -- 动态构造当前货币的时间序列数组
        SET @sql = CONCAT(
            'SELECT JSON_ARRAYAGG(JSON_ARRAY(datetime, ', col_name, ')) INTO @series_data FROM temp_currency;'
        );
        PREPARE stmt FROM @sql;
        EXECUTE stmt;
        DEALLOCATE PREPARE stmt;

        -- 将当前货币对象追加到结果JSON中
        SET @json_result = JSON_ARRAY_APPEND(
            @json_result,
            '$',
            JSON_OBJECT(
                'currency', col_name,
                'timeseriesdata', @series_data
            )
        );
    END LOOP;
    CLOSE cur;

    -- 输出最终结果并清理临时表
    SELECT @json_result AS result;
    DROP TEMPORARY TABLE IF EXISTS temp_currency;
END //

DELIMITER ;

步骤2:调用存储过程

CALL GetCurrencyTimeseries('2019-02-16 10:40:00', '2019-02-16 10:58:00');

3. 性能优化建议(针对百万级数据)

  • 索引优化:给datetime字段创建索引,减少全表扫描:
    CREATE INDEX idx_currency_datetime ON currency(datetime);
    
  • 版本要求:使用MySQL 8.0+,该版本对JSON函数的性能有显著提升。
  • 内存控制:若数据量极大,可在存储过程中加入分批聚合逻辑,避免单次聚合内存溢出。

内容的提问来源于stack exchange,提问作者micronyks

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 04:20:58