优化 - 在循环内从 TEMP TABLE 中选择以获得 JSON 值的平均值的函数
Optimize - Function that SELECTs from TEMP TABLE within loop to get averages of JSON values
我有一个 Mysql 函数,它 运行 作为读取几百万条记录的更大查询的一部分。为了检测异常,我正在计算随时间的平均变化。 table 中的数据存储为 JSON 个对象,以 UNIX 时间戳为键,最多可保存 30 天。
例如,输入 (input_array) 类似于:
[{"1532944806": 16}, {"1533031206": 14}, {"1533117605": 13}, {"1533204305": 12}, {"1533290708": 10}, {"1533463506": 9}, {"1533549907": 9}, {"1533636306": 9}, {"1533722707": 9}, {"1533809108": 9}, {"1533895506": 9}, {"1533981906": 8}, {"1534068306": 7}, {"1534154706": 7}, {"1534241108": 7}, {"1534590304": 7}, {"1534673106": 12}, {"1534759508": 6}, {"1534845905": 7}, {"1534932306": 7}, {"1535018707": 5}, {"1535105106": 3}, {"1535191505": 7}, {"1535277907": 6}, {"1535364305": 7}, {"1535450706": 2}, {"1535537107": 1}]
我只希望平均 减少 变化 - 而不是一天内增加的任何变化。
我正在检查前一天的值是否存在,如果存在,我正在计算变化并将其添加到一个临时 table 中,该 table 被查询为 select 平均值.
到目前为止我有:
CREATE FUNCTION `daily_averages`(input_array JSON) RETURNS int(4)
READS SQL DATA
DETERMINISTIC
BEGIN
DECLARE array_length INTEGER(2);
DECLARE prev_value INTEGER(4);
DECLARE idx INTEGER(4);
DROP TEMPORARY TABLE IF EXISTS collection;
CREATE TEMPORARY TABLE collection (change INTEGER(4) SIGNED DEFAULT 0);
SELECT JSON_LENGTH(input_array) INTO array_length;
SET idx = 0;
WHILE idx < array_length DO
SELECT
IF(idx-1 > -1,
CONVERT(
JSON_EXTRACT(
JSON_EXTRACT(
JSON_EXTRACT( input_array, CONCAT( '$[', idx-1, ']' ) )
, '$.*'
)
, '$[0]'
), SIGNED INTEGER
)
, -1
)
INTO prev_value;
INSERT INTO collection
SELECT (prev_value -
(
CONVERT(
JSON_EXTRACT(
JSON_EXTRACT(
JSON_EXTRACT( input_array, CONCAT( '$[', idx, ']' ) )
, '$.*'
)
, '$[0]'
), SIGNED INTEGER
)
)
)
FROM DUAL
WHERE prev_value > 0;
SET idx = idx + 1;
END WHILE;
RETURN (SELECT AVG(change) FROM collection WHERE change > -1);
END
目前约有 270 万条记录,运行 大约需要 20 分钟。我希望通过避免 DROP/CREATE 开销来优化它或重写它。
似乎没有必要创建一个table来计算平均值,在循环中很简单。不是将每个值插入 table,而是将其添加到总变量。最后,return total/count
.
由于您要对值之间的差异求和,
你也可以使用SET
语句来赋值变量,而不是SELECT ... INTO variable
。
DECLARE array_length INTEGER(2);
DECLARE prev_value INTEGER(4);
DECLARE idx INTEGER(4);
DECLARE total INTEGER(4);
DECLARE counter INTEGER(4);
DECLARE cur_value INTEGER(4);
SET array_length = JSON_LENGTH(input_array);
SET total = 0;
SET counter = 0;
-- Initialize prev_value to the first element
SET prev_value = CONVERT(
JSON_EXTRACT(
JSON_EXTRACT(
JSON_EXTRACT( input_array, '$[0]' )
, '$.*'
)
, '$[0]'
), SIGNED INTEGER
);
SET idx = 1;
WHILE idx < array_length DO
SET cur_value = CONVERT(
JSON_EXTRACT(
JSON_EXTRACT(
JSON_EXTRACT( input_array, CONCAT( '$[', idx, ']' ) )
, '$.*'
)
, '$[0]'
), SIGNED INTEGER
);
IF cur_value < prev_value
THEN
SET total = total + (prev_value - cur_value);
SET counter = counter + 1;
END IF;
SET prev_value = cur_value;
SET idx = idx + 1;
END WHILE;
RETURN total / counter;
深入挖掘一百万 JSON 个字符串。我很惊讶 只用了 20 分钟。
当您插入行时,进行一些计算并将结果存储在某处。然后使用 that 进行监控。
即使您不能在插入行时执行此操作,也只能对 'new' 行执行此操作。再次将之前的信息保存在某处。
至于 DROP/CREATE...可以通过永久 table 来加快速度,然后在每个 proc 调用开始时仅使用 TRUNCATE TABLE
。
INTEGER(4)
中的(4)
没有任何意义。您将始终获得一个 32 位整数。 (此注释可能对过程没有影响。)
我有一个 Mysql 函数,它 运行 作为读取几百万条记录的更大查询的一部分。为了检测异常,我正在计算随时间的平均变化。 table 中的数据存储为 JSON 个对象,以 UNIX 时间戳为键,最多可保存 30 天。
例如,输入 (input_array) 类似于:
[{"1532944806": 16}, {"1533031206": 14}, {"1533117605": 13}, {"1533204305": 12}, {"1533290708": 10}, {"1533463506": 9}, {"1533549907": 9}, {"1533636306": 9}, {"1533722707": 9}, {"1533809108": 9}, {"1533895506": 9}, {"1533981906": 8}, {"1534068306": 7}, {"1534154706": 7}, {"1534241108": 7}, {"1534590304": 7}, {"1534673106": 12}, {"1534759508": 6}, {"1534845905": 7}, {"1534932306": 7}, {"1535018707": 5}, {"1535105106": 3}, {"1535191505": 7}, {"1535277907": 6}, {"1535364305": 7}, {"1535450706": 2}, {"1535537107": 1}]
我只希望平均 减少 变化 - 而不是一天内增加的任何变化。
我正在检查前一天的值是否存在,如果存在,我正在计算变化并将其添加到一个临时 table 中,该 table 被查询为 select 平均值.
到目前为止我有:
CREATE FUNCTION `daily_averages`(input_array JSON) RETURNS int(4)
READS SQL DATA
DETERMINISTIC
BEGIN
DECLARE array_length INTEGER(2);
DECLARE prev_value INTEGER(4);
DECLARE idx INTEGER(4);
DROP TEMPORARY TABLE IF EXISTS collection;
CREATE TEMPORARY TABLE collection (change INTEGER(4) SIGNED DEFAULT 0);
SELECT JSON_LENGTH(input_array) INTO array_length;
SET idx = 0;
WHILE idx < array_length DO
SELECT
IF(idx-1 > -1,
CONVERT(
JSON_EXTRACT(
JSON_EXTRACT(
JSON_EXTRACT( input_array, CONCAT( '$[', idx-1, ']' ) )
, '$.*'
)
, '$[0]'
), SIGNED INTEGER
)
, -1
)
INTO prev_value;
INSERT INTO collection
SELECT (prev_value -
(
CONVERT(
JSON_EXTRACT(
JSON_EXTRACT(
JSON_EXTRACT( input_array, CONCAT( '$[', idx, ']' ) )
, '$.*'
)
, '$[0]'
), SIGNED INTEGER
)
)
)
FROM DUAL
WHERE prev_value > 0;
SET idx = idx + 1;
END WHILE;
RETURN (SELECT AVG(change) FROM collection WHERE change > -1);
END
目前约有 270 万条记录,运行 大约需要 20 分钟。我希望通过避免 DROP/CREATE 开销来优化它或重写它。
似乎没有必要创建一个table来计算平均值,在循环中很简单。不是将每个值插入 table,而是将其添加到总变量。最后,return total/count
.
由于您要对值之间的差异求和,
你也可以使用SET
语句来赋值变量,而不是SELECT ... INTO variable
。
DECLARE array_length INTEGER(2);
DECLARE prev_value INTEGER(4);
DECLARE idx INTEGER(4);
DECLARE total INTEGER(4);
DECLARE counter INTEGER(4);
DECLARE cur_value INTEGER(4);
SET array_length = JSON_LENGTH(input_array);
SET total = 0;
SET counter = 0;
-- Initialize prev_value to the first element
SET prev_value = CONVERT(
JSON_EXTRACT(
JSON_EXTRACT(
JSON_EXTRACT( input_array, '$[0]' )
, '$.*'
)
, '$[0]'
), SIGNED INTEGER
);
SET idx = 1;
WHILE idx < array_length DO
SET cur_value = CONVERT(
JSON_EXTRACT(
JSON_EXTRACT(
JSON_EXTRACT( input_array, CONCAT( '$[', idx, ']' ) )
, '$.*'
)
, '$[0]'
), SIGNED INTEGER
);
IF cur_value < prev_value
THEN
SET total = total + (prev_value - cur_value);
SET counter = counter + 1;
END IF;
SET prev_value = cur_value;
SET idx = idx + 1;
END WHILE;
RETURN total / counter;
深入挖掘一百万 JSON 个字符串。我很惊讶 只用了 20 分钟。
当您插入行时,进行一些计算并将结果存储在某处。然后使用 that 进行监控。
即使您不能在插入行时执行此操作,也只能对 'new' 行执行此操作。再次将之前的信息保存在某处。
至于 DROP/CREATE...可以通过永久 table 来加快速度,然后在每个 proc 调用开始时仅使用 TRUNCATE TABLE
。
INTEGER(4)
中的(4)
没有任何意义。您将始终获得一个 32 位整数。 (此注释可能对过程没有影响。)