1

I come from MS SQL Server and I'm relatively new to MySQL / MariaDB 10 (at least in a deeper way than just "SELECT * FROM [Table]"). I now searched for several hours in Google and StackOverflow, but I haven't found a soluton to my problem yet. If it's relevant in any way: I use MySQL Workbench for writing my code.

The Background

I have a new data logging project for saving and displaying data from several temperature and humidity sensors within the house. I save it in following table:

ID Time Device Temperature Humidity
1 2022-01-09 13:34:00 1 20.1 52.3
2 2022-01-09 13:35:00 1 20.0 52.3
3 2022-01-09 13:36:00 1 20.1 52.4
4 2022-01-09 13:37:00 1 20.1 52.5
5 2022-01-09 13:38:00 1 20.0 52.5
6 2022-01-09 13:39:00 1 20.1 52.6

I query the needed data for a chart using a stored procedure. Especially the on 0.1°C rounded temperature values have the disadvantage that they naturally often change between a value of 0.1 when the temperature is pretty stable. So I thought of a moving average to smooth the values over the last 10 minutes which works perfectly with an average window function.

Here a simplified version of my procedure:

CREATE PROCEDURE `stpGetSensorData`(sensorId INT, startDate VARCHAR(8))
BEGIN
    DECLARE FromDate DATE;
    SET FromDate = STR_TO_DATE(startDate, '%Y%m%d');

    SELECT 
        L.ID,
        L.Time,
        L.Device,
        AVG(L.Temperature) OVER (ORDER BY L.Time ROWS BETWEEN 10 PRECEDING AND 0 FOLLOWING) AS Temperature,
        AVG(L.Humidity) OVER (ORDER BY L.Time ROWS BETWEEN 10 PRECEDING AND 0 FOLLOWING) AS Humidity
    FROM
        LoggedData AS L
    WHERE
        Device = sensorId
        AND Time < DATE_ADD(FromDate, INTERVAL 1 DAY)
        AND Time >= FromDate
    ORDER BY Time DESC;
END

The challenge

Now I thought I let the end user decide about the size of the window, i.e. an average over the last 5, 10, 30, 60, ... minutes. But when I try to insert a parameter in the window function, it leads to the error: "averageRows is not valid at this position".

Here the code:

CREATE PROCEDURE `stpGetSensorData`(sensorId INT, startDate VARCHAR(8), averageRows INT)
BEGIN
    DECLARE FromDate DATE;
    SET FromDate = STR_TO_DATE(startDate, '%Y%m%d');

    SELECT 
        L.ID,
        L.Time,
        L.Device,
        AVG(L.Temperature) OVER (ORDER BY L.Time ROWS BETWEEN averageRows PRECEDING AND 0 FOLLOWING) AS Temperature,
        AVG(L.Humidity) OVER (ORDER BY L.Time ROWS BETWEEN averageRows PRECEDING AND 0 FOLLOWING) AS Humidity
    FROM
        LoggedData AS L
    WHERE
        Device = sensorId
        AND Time < DATE_ADD(FromDate, INTERVAL 1 DAY)
        AND Time >= FromDate
    ORDER BY Time DESC;
END

I guess it's possible to solve this using Dynamic SQL, but I try to avoid Dynamic SQL whereever possible and thought there must be a 'normal' solution as well and I'm just too blind to see it.

Any smart ideas?

  • 2
    you can't use variables at that position, so don't avoid dynamic sql – nbk Jan 17 '22 at 01:22
  • Create separate queries in your stored procedure and call needed query in `CASE averageRows WHEN 10 THEN {query with the frame of 10 minutes} WHEN 30 ...` statement. Alternatively - use subquery/CTE instead of window function. – Akina Jan 17 '22 at 05:04
  • *So I thought of a moving average to smooth the values over the last 10 minutes which works perfectly with an average window function.* This is incorrect. Your frame is 10 ROWS, not 10 minutes. Use RANGE frame definition. PS. `AND 0 FOLLOWING` == `AND CURRENT ROW`. And the last variant seems to be more clear. – Akina Jan 17 '22 at 05:06
  • @Akina: The way of using CASE is not a bad idea, at least as long as there are fixed windows. As soon as the user has the possibility to enter a size, it's not possible anymore. And you're completly right for sure that it's 10 ROWS. That was a little inaccurate of me. But that's not the point of the difficulty here. The part "AND 0 FOLLOWING" was just a leftover from testing with different averages of not just past values. :) I guess I will give both versions a try: the CASE and the Dynamic SQL one, like nbk says it would be the only way, to be completly flexible. Thank you both so far! – AtoMedia Design Jan 17 '22 at 09:22

0 Answers0