Как рассчитать скользящее среднее в MYSQL

0

У меня есть приложение, которое хранит котировки акций в моей базе данных MySQL.

У меня есть таблица под названием stock_history:

mysql> desc stock_history;
+-------------------+---------------+------+-----+---------+----------------+
| Field             | Type          | Null | Key | Default | Extra          |
+-------------------+---------------+------+-----+---------+----------------+
| id                | int(11)       | NO   | PRI | NULL    | auto_increment |
| date              | date          | NO   | MUL | NULL    |                |
| close             | decimal(12,5) | NO   | MUL | NULL    |                |
| dmal_3            | decimal(12,5) | YES  | MUL | NULL    |                |
+-------------------+---------------+------+-----+---------+----------------+
5 rows in set (0.01 sec)

Это все значения в этой таблице:

mysql> select date, close, dmal_3 from stock_history order by date asc;
+------------+----------+----------+
| date       | close    |   dmal_3 | 
+------------+----------+----------+-
| 2000-01-03 |  2.00000 |     NULL | 
| 2000-01-04 |  4.00000 |     NULL | 
| 2000-01-05 |  6.00000 |     NULL | 
| 2000-01-06 |  8.00000 |     NULL | 
| 2000-01-07 | 10.00000 |     NULL | 
| 2000-01-10 | 12.00000 |     NULL | 
| 2000-01-11 | 14.00000 |     NULL | 
| 2000-01-12 | 16.00000 |     NULL | 
| 2000-01-13 | 18.00000 |     NULL | 
| 2000-01-14 | 20.00000 |     NULL | 
+------------+----------+----------+-
10 rows in set (0.01 sec)

Я гарантирован, что на каждую дату будет 0 или 1 запись.

Могу ли я написать один запрос, который будет вставлять трехдневную скользящую среднюю (т. dmal_3 Средние цены закрытия этого дня и два предыдущих торговых дня до этого) в поле dmal_3? Как?

Когда запрос будет выполнен, я хочу, чтобы таблица выглядела так:

mysql> select date, close, dmal_3 from stock_history order by date asc;
+------------+----------+----------+
| date       | close    |   dmal_3 | 
+------------+----------+----------+
| 2000-01-03 |  2.00000 |     NULL | 
| 2000-01-04 |  4.00000 |     NULL | 
| 2000-01-05 |  6.00000 |  4.00000 | 
| 2000-01-06 |  8.00000 |  6.00000 | 
| 2000-01-07 | 10.00000 |  8.00000 | 
| 2000-01-10 | 12.00000 | 10.00000 | 
| 2000-01-11 | 14.00000 | 12.00000 | 
| 2000-01-12 | 16.00000 | 14.00000 | 
| 2000-01-13 | 18.00000 | 16.00000 | 
| 2000-01-14 | 20.00000 | 18.00000 | 
+------------+----------+----------+
10 rows in set (0.01 sec)
  • 0
    Это смущает. Для идеальных результатов в 2000-01-06 вас установлено 2000-01-06 значение 6. Но эта дата и предыдущие 2 торговые даты имеют значения закрытия 8.00, 10.00 и 12.00. Таким образом, среднее значение должно быть 30/3 или 10. Имеет смысл как 6, если то, что вы хотите, является средним числом последних трех последовательных дат (торговых или неторговых дат), которые, следовательно, будут 8 + 10 + 0 или 18/3. (6). Также не имеет смысла, почему последние два в вашем наборе данных равны нулю. Вы знаете близкое значение для обоих.
  • 1
    Подожди, извини. Я забыл, в каком направлении движется время.
Теги:

1 ответ

1
Лучший ответ

Это то, что я называю хорошим вызовом. Сначала мое решение создает счетчик для значений и использует его как таблицу. Из него я выбираю все и присоединяюсь к тому же запросу, что и подзапрос, проверяющий положение счетчика на обоих. Как только запрос работает, для обновления требуется просто внутреннее соединение с фактической таблицей. Вот это мое решение:

update stock_history tb1
   inner join 
(
select a.id, 
    case when a.step < 3 then null
    else
    (select avg(b.close) 
       from (
            select hh.*, 
                   @stp:=@stp+1 stp
              from stock_history hh,
                   (select @sum:=0, @stp:=0) x
             order by hh.dt
             limit 17823232
             ) b
      where b.stp >= a.step-2 and b.stp <= a.step
     ) 
     end dmal_3
  from (select h1.*, 
               @step:=@step+1 step
          from stock_history h1,
               (select @sum:=0, @step:=0) x
         order by h1.dt
         limit 17823232
        ) a
) x on tb1.id = x.id
set tb1.dmal_3 = x.dmal_3;

Я изменил имена столбцов для удобства моего теста. Здесь это рабочий SQLFiddle: http://sqlfiddle.com/#!9/e7dc00/1

Если у вас есть какие-либо сомнения, сообщите мне, чтобы я мог уточнить!

редактировать

limit 17823232 предложение limit 17823232 было добавлено там в подзапросах, потому что я не знаю, в какую версию MySql вы находитесь. В зависимости от этого (> = 5.7, точно не уверен) оптимизатор базы данных будет игнорировать внутренний order by статьям, как это должно быть. Я просто выбрал случайный большой номер, который обычно можно использовать максимально допустимым.

Единственный столбец с другим именем colunm между вашей таблицей и моей является date которую я назвал dt потому что date является зарезервированным словом, и вы должны использовать обратные обратные (') для использования таких столбцов, поэтому я оставлю ее как dt в предыдущем запросе.

  • 0
    Откуда взялся 17823232?!?!?
  • 0
    И не могли бы вы использовать те же имена столбцов, которые я использовал для ясности (я знаю, что вы делаете в вашем sqlfiddle. Но было бы хорошо сделать это и здесь)
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню