У меня есть приложение, которое хранит котировки акций в моей базе данных 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)
Это то, что я называю хорошим вызовом. Сначала мое решение создает счетчик для значений и использует его как таблицу. Из него я выбираю все и присоединяюсь к тому же запросу, что и подзапрос, проверяющий положение счетчика на обоих. Как только запрос работает, для обновления требуется просто внутреннее соединение с фактической таблицей. Вот это мое решение:
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
в предыдущем запросе.
2000-01-06
вас установлено2000-01-06
значение 6. Но эта дата и предыдущие 2 торговые даты имеют значения закрытия 8.00, 10.00 и 12.00. Таким образом, среднее значение должно быть 30/3 или 10. Имеет смысл как 6, если то, что вы хотите, является средним числом последних трех последовательных дат (торговых или неторговых дат), которые, следовательно, будут 8 + 10 + 0 или 18/3. (6). Также не имеет смысла, почему последние два в вашем наборе данных равны нулю. Вы знаете близкое значение для обоих.