Хранение ежегодно повторяемых значений в базе данных MySQL - PullRequest
3 голосов
/ 09 марта 2010

Последние несколько дней я бился головой об стол, и поэтому я обращаюсь к вам, Переполнение стека.

Программное обеспечение, над которым я работаю, содержит данные, чувствительные ко времени. Обычное решение для этого - даты вступления в силу.

EFF_DT      XPIR_DT     VALUE
2000-05-01  2000-10-31  100
2000-11-01  (null)      90

Это было бы легко. К сожалению, нам нужны данные, которые повторяются на ежегодной основе произвольно в будущем. Другими словами, каждый 1 мая (начиная с 2000 года) мы можем захотеть, чтобы эффективное значение равнялось 100, а каждый 1 ноября мы можем захотеть изменить его на 90.

Это может быть правдой долгое время (> 50 лет), и поэтому я не хочу просто создавать сотню записей. То есть я не хочу этого делать:

EFF_DT      XPIR_DT     VALUE
2000-05-01  2000-10-31  100
2000-11-01  2001-04-30  90
2001-05-01  2001-10-31  100
2001-11-01  2002-04-30  90
2002-05-01  2002-10-31  100
2002-11-01  2003-04-30  90
...
2049-05-01  2049-10-31  100
2049-11-01  2050-04-30  90
2050-05-01  2050-10-31  100
2050-11-01  2051-04-30  90

Эти значения также могут изменяться со временем. Значения до 2000 года могли быть постоянными (без триггеров), и значения на ближайшее десятилетие могут отличаться от значений за последнее:

EFF_DT      XPIR_DT     REPEATABLE  VALUE
1995-01-01  2000-04-30  false       85
2000-05-01  2010-04-30  true        100
2000-11-01  2010-10-31  true        90
2010-05-01  (null)      true        120
2010-11-01  (null)      true        115

У нас уже есть текстовый файл (из унаследованного приложения), в котором данные хранятся в форме, очень близкой к этой, так что есть преимущества в том, чтобы придерживаться этого типа структуры как можно ближе.

Затем возникает вопрос о поиске: какое значение будет применяться к сегодняшнему дню, 2010-03-09?

Кажется, что лучший способ сделать это - найти самый последний экземпляр каждой даты вступления в силу (из всех активных строк), а затем посмотреть, какая из них самая большая.

EFF_DT      MOST_RECENT XPIR_DT     VALUE
2000-05-01  2009-05-01  2010-04-30  100
2000-11-01  2009-11-01  2010-10-31  90

Значение на сегодня будет 90, поскольку 2009-11-01 позже, чем 2009-05-01.

На, скажем, 2007-06-20:

EFF_DT      MOST_RECENT XPIR_DT     VALUE
2000-05-01  2007-05-01  2010-04-30  100
2000-11-01  2006-11-01  2010-10-31  90

Значение будет равно 100, поскольку 2007-05-01 позже, чем 2006-11-01.

Как использовать наиболее эффективный способ вычисления поля MOST_RECENT с использованием функций даты MySQL?

Или кто-нибудь может придумать лучший способ сделать это?

Язык - Java, если это имеет значение. Спасибо всем!

Ответы [ 3 ]

2 голосов
/ 09 марта 2010

Предположим, что желаемая дата '2007-06-20'.

Вам необходимо объединить неповторяющиеся элементы с повторяющимися, чтобы вы могли сделать что-то подобное (непроверенное и, вероятно, нуждается в некотором размышлении, но должно дать вам общее представление):

select * from (
  select * from mytable 
  where 
    repeatable = false
    and 
    EFF_DT <= '2007-06-20' < XPIR_DT
  union all
  select * from mytable
  where
    repeatable = true
    and EFF_DT <= str_to_date(concat("2007", "-", month(EFF_DT), "-", day(EFF_DT)), "%Y-%m-%d") < XPIR_DT
)
order by EFF_DT desc limit 1
1 голос
/ 10 марта 2010

Мне приходилось делать аналогичные вещи с повторяющимися встречами и событиями, и вы можете обнаружить, что MySQL будет намного счастливее с "статическим" стилем дат, который вам не нужен - каждый повторяющийся экземпляр прописан в сотнях строки.

Если возможно, я бы подумал о создании отдельной таблицы для их выравнивания, сохраняя даты вступления в силу / истечения срока их действия (чтобы соответствовать устаревшим данным и выступая в качестве родителя), и отношение 1: многие между две таблицы (т. е. «event_id» на сглаженных данных, ссылающихся на PK оригинала). Написание всех этих записей, очевидно, займет больше времени, но это непосредственно уменьшает нагрузку от их чтения (где вещи обычно должны быть быстрее).

Создание хранимой процедуры или внешней программы для обработки пересчета плоской таблицы start_date / end_date / value должно быть достаточно простым, учитывая общий интервал. Запрос данных может быть таким простым, как WHERE @somedate BETWEEN start_date AND end_date, вместо все более сложных конверсий и математики дат.

Опять же, INSERT и UPDATE будут медленнее, но «сотни строк» ​​даже не затрагивают поверхность, на которую способен MySQL. Если это просто 2 даты, int и какой-то ключ int, написание нескольких сотен записей должно занять не пару секунд, а на другом сервере. Если бы мы говорили о миллионах записей, то, возможно, что-то могло бы быть изменено (вам действительно нужно отслеживать на 50 лет вперед или только на следующие 5? Можно ли перевести пересчет в непиковое время через cron? И т. Д.), Но даже тогда MySQL просто быть гораздо более эффективным по сравнению с вычислением разницы каждый раз.

Также может представлять интерес: Какой лучший способ моделирования повторяющихся событий в приложении календаря? & Структура данных для хранения повторяющихся событий?

0 голосов
/ 09 марта 2010

Вот запрос, который вы можете использовать для вычисления более позднего EFF_DT для набора данных. Вы должны будете заполнить там, где предложение, потому что я не уверен, как эти данные организованы.

select EFF_DT  form  date_table where 1 order by EFF_DT desc limit 1

Триггер 90 и 100 более сложен, но вы должны быть в состоянии позаботиться об этом, используя mysql data и time . Это сложный вопрос, и я не на 100% от того, что вы пытаетесь сделать. Но этот запрос проверяет, больше ли месяц XPIR_DT, чем май (5-й месяц), но меньше, чем ноябрь (11-й месяц). Если это правда, то SQL-запрос вернет 90, если его ложь, то вы получите 100.

select if((month(XPIR_DT)>=5) and (month(XPIR_DT)<11),90,100) from date_table where id=1
...