MySQL запрос на усреднение данных, если в пределах графика - PullRequest
0 голосов
/ 06 июня 2019

У меня есть большое количество данных с устройства регистрации данных, хранящихся в БД MySQL, которые я хочу разместить на графике, я хочу показать данные за месяцы - регистрация ведется за секунду.

Я использую PHP и библиотеку Google Charts для рисования графика в качестве клиентской части изображения.

Нет смысла пытаться отобразить 2 628 000 на графике на экране, поэтому я хочу попытаться получить запрос SQL, чтобы дать среднее значение точки данных, скажем, каждый час (от 3600 до 1) вместо каждой секунды, , если оно не выходит за пределы . Причина в том, что весь смысл графика состоит в том, чтобы показать, вышло ли значение за границы и когда это произошло.

Текущий SQL-запрос для получения данных, требуемых, например, за последний месяц, приведен ниже, первая проблема заключается в том, что PHP достигает предела памяти, прежде чем он сможет вернуть данные:

SELECT Tms, Hz FROM log WHERE Tms >= ".$start." AND Tms <=".$finish." ORDER BY Tms ASC

Среднее значение должно быть, например, 60, верхний предел равен 61,5, а нижний предел равен 58,5 - любое значение, выходящее за их пределы, должно быть возвращено как есть, в противном случае должна быть возвращена стоимость данных за часы в среднем за этот час.

РЕДАКТИРОВАТЬ: Чтобы ответить на комментарии:

Структура БД:

ID - double - AUTO_INCREMENT 
Tms - timestamp 
Hz - float

Пример данных:

ID     | Tms        | Hz
1      | 1559347082 | 59.91
2      | 1559347083 | 59.98
3      | 1559347084 | 60.53
4      | 1559347085 | 62.03
5      | 1559347086 | 61.11
6      | 1559347087 | 60.93
7      | 1559347088 | 60.88
.......
3606   | 1559350686 | 59.99

Ожидаемый результат будет иметь массив результатов, все значения в течение часа в среднем, если нет значения за пределами.

Таким образом, для данных выше, пункты 1,2,3 будут возвращены со средним Tms: 1559347083 и средним Hz: 60.14, но следующее значение в массиве результатов будет Tms: 1559347085 и Hz: 62.03.

Результаты:

Tms: 1559347083 | Hz: 60.14
Tms: 1559347085 | Hz: 62.03
Tms: 1559348886 | Hz: 60.17

Максимальное количество точек, которые будут усреднены или сгруппированы, составило бы 3600 строк = 1 час, поэтому график показывает некоторое движение.

Одна из текущих ошибок при попытке выделить большой объем данных:

Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 20480 bytes) 

Это происходит, когда результат помещается в массив, поэтому я могу добавить значения границ, чтобы на графике была четкая линия:

while($row = $result->fetch_assoc()) {
     $dataPoint = array($row['Tms'], '58.5', $row[$graph], '61.5');
....
    array_push($dataPoints, $dataPoint);

Этот массив ($ dataPoints) затем передается функции либо для вывода в виде JSON, либо для вывода в виде CSV с использованием fputcsv

1 Ответ

1 голос
/ 06 июня 2019

Нелогично или бесполезно иметь один запрос, который дает как среднечасовые значения, так и отдельные значения за пределами. Это требует двух запросов. Итак, начнем с первого, среднечасового значения:

SELECT 
  COUNT(ID) AS CountID,
  DATE(Tms) AS DateTms,
  HOUR(Tms) AS HourTms, 
  AVG(Hz) AS AvgHz
FROM 
  log 
WHERE 
  Tms >= '2019-01-01 12:00:00' AND 
  Tms <= '2019-12-12 12:00:00'
GROUP BY 
  HOUR(Tms)
ORDER BY 
  Tms ASC

Я поставил реальные условия в условиях WHERE вместо недокументированных переменных $start и $finish, но их, конечно, можно заменить. Я добавил счетчик, потому что он всегда полезен, и, наконец, потому что мы отчитываемся за каждый час дня, я добавил дату. GROUP BY HOUR (Tms) выполняет группировку по целым часам.

Второй запрос касается значений out of bouds. Это просто:

SELECT 
  ID,
  Tms,
  Hz
FROM 
  log 
WHERE 
  Tms >= '2019-01-01 12:00:00' AND 
  Tms <= '2019-12-12 12:00:00' AND
  (Hz < 58.5 OR Hz > 61.5)
ORDER BY 
  Tms ASC

Вы можете легко объединить результаты этих двух запросов в один массив с PHP. Однако ...

Меня беспокоит, что последний запрос может выдать слишком много данных, когда слишком много выходящих значений. И это, вероятно, то, что вы говорите в своем последующем дополнении к вопросу Чтобы решить эту проблему, вы можете работать со среднечасовым значением выходящих за пределы. Для этого вам придется использовать два запроса: один для значений ниже нижнего предела и один для значений выше верхнего предела. Я покажу первый здесь:

SELECT 
  COUNT(ID) AS CountID,
  DATE(Tms) AS DateTms,
  HOUR(Tms) AS HourTms, 
  AVG(Hz) AS AvgHz
FROM 
  log 
WHERE 
  Tms >= '2019-01-01 12:00:00' AND 
  Tms <= '2019-12-12 12:00:00' AND
  Hz < 58.5
GROUP BY 
  HOUR(Tms)
ORDER BY 
  Tms ASC

Это очень похоже на первый запрос, и это хорошо. Единственным дополнением является ограничение диапазона значения Hz. Другой запрос просто имеет Hz > 61.5. Результаты трех запросов могут быть собраны в массив и отображены на графике.

Три запроса могут быть принудительно введены в один запрос, но я не вижу преимущества этого. С помощью трех отдельных запросов вы можете, например, написать функцию PHP, которая выполняет запрос и получает результаты, и все, что вам нужно изменить, используя параметры функции, это ограничение диапазона и время начала / окончания.

Наконец, немного о вашей базе данных. Я вижу, вы используете удваивает для идентификатора, который, вероятно, должен быть целым числом. Также не забудьте поставить индексы на Tms и Hz, иначе ваши запросы могут быть очень медленными.

...