2010-08-21 2 views
49

У меня есть стол с 2 колонками, дата и оценка. Он имеет не более 30 записей за каждый из последних 30 дней.MySQL, как заполнить отсутствующие даты в диапазоне?

date  score 
----------------- 
1.8.2010 19 
2.8.2010 21 
4.8.2010 14 
7.8.2010 10 
10.8.2010 14 

Моя проблема заключается в том, что некоторые даты отсутствуют, - я хочу видеть:

date  score 
----------------- 
1.8.2010 19 
2.8.2010 21 
3.8.2010 0 
4.8.2010 14 
5.8.2010 0 
6.8.2010 0 
7.8.2010 10 
... 

Что мне нужно от одного запроса, чтобы получить: 19,21,9,14,0,0 , 10,0,0,14 ... Это означает, что недостающие даты заполняются 0.

Я знаю, как получить все значения и на языке сервера, итерации по датам и отсутствию пробелов. Но можно ли это сделать в mysql, чтобы сортировать результат по дате и получать недостающие фрагменты.

EDIT: В этой таблице есть еще один столбец с именем UserID, поэтому у меня есть 30 000 пользователей, а некоторые из них имеют счет в этой таблице. Я удаляю даты каждый день, если дата < 30 дней назад, потому что мне нужен последний 30-дневный балл для каждого пользователя. Причина в том, что я делаю график активности пользователя за последние 30 дней и для построения графика мне нужны 30 значений, разделенных запятой. Поэтому я могу сказать, что в запросе введите мне USERID = 10203, и запрос получит мне 30 баллов, по одному за каждый из последних 30 дней. Надеюсь, теперь я более ясен.

+0

Да, это возможно, но * почему бы вам это сделать? – NullUserException

+0

Я все еще не понимаю. Не извлекайте ненужные данные из базы данных, если вы можете заполнить эти пробелы тем, что рисует график, и вы сэкономите некоторые издержки. – NullUserException

+2

, но тогда я должен ВЫБРАТЬ данные для USERID, я получаю, например, 20 строк даты и оценки, а затем мне нужно зациклиться на моем языке на стороне сервера (ASP), чтобы проверить, есть ли дата 30 дней назад, если это не сделать 0 еще сделать значение базы данных ... Разве это не больше, чем выборка из значений базы данных 30 и просто построение строки? – Jerry2

ответ

49

MySQL не имеет рекурсивную функцию, так что вы оставили с помощью ЧИСЛА таблицы трюка -

  1. Создать таблицу, которая содержит только увеличивающееся значение - легко сделать с помощью auto_increment:

    DROP TABLE IF EXISTS `example`.`numbers`; 
    CREATE TABLE `example`.`numbers` (
        `id` int(10) unsigned NOT NULL auto_increment, 
        PRIMARY KEY (`id`) 
    ) ENGINE=InnoDB DEFAULT CHARSET=latin1; 
    
  2. Заполните таблицу, используя:

    INSERT INTO `example`.`numbers` 
        (`id`) 
    VALUES 
        (NULL) 
    

    ... для так много значений, сколько вам нужно.

  3. Используйте DATE_ADD для создания списка дат, увеличивая количество дней на основе значения NUMBERS.id. Заменить "2010-06-06" и "2010-06-14" с вашими соответствующими датами начала и окончания (но использует один и тот же формат, YYYY-MM-DD) -

    SELECT `x`.* 
        FROM (SELECT DATE_ADD('2010-06-06', INTERVAL `n`.`id` - 1 DAY) 
          FROM `numbers` `n` 
         WHERE DATE_ADD('2010-06-06', INTERVAL `n`.`id` -1 DAY) <= '2010-06-14') x 
    
  4. LEFT JOIN на ваш стол данных на основе временной части:

    SELECT `x`.`ts` AS `timestamp`, 
          COALESCE(`y`.`score`, 0) AS `cnt` 
        FROM (SELECT DATE_FORMAT(DATE_ADD('2010-06-06', INTERVAL `n`.`id` - 1 DAY), '%m/%d/%Y') AS `ts` 
          FROM `numbers` `n` 
          WHERE DATE_ADD('2010-06-06', INTERVAL `n`.`id` - 1 DAY) <= '2010-06-14') x 
    LEFT JOIN TABLE `y` ON STR_TO_DATE(`y`.`date`, '%d.%m.%Y') = `x`.`ts` 
    

Если вы хотите сохранить формат даты, используйте DATE_FORMAT function:

DATE_FORMAT(`x`.`ts`, '%d.%m.%Y') AS `timestamp` 
+1

спасибо. Является ли это быстрой операцией, не советовал бы вам использовать такой подход и собирать расчеты на стороне сервера? – Jerry2

+4

@ Jerry2: Мое предпочтение состоит в том, чтобы делать так же большую обработку данных в базе данных, за исключением действительно связанных материалов презентации. Я не завидую делать это в коде приложения, до тех пор, пока это * одна поездка в базу данных * ... –

+0

Чтобы использовать индексы, условия (предложения WHERE и ON) могут быть переписаны на 'WHERE n.id < DATEIFF ('2010-06-14', '2010-06-06') 'и' LEFT JOIN TABLE y ON y.date = DATE_FORMAT (x.ts, '% d.% M.% Y') ' –

13

You может выполнить это, используя таблицу Таблица. Это таблица, которую вы создаете один раз и заполняете диапазоном дат (например, один набор данных для каждого дня 2000-2050, который зависит от ваших данных). Затем вы можете сделать внешнее соединение таблицы против таблицы календаря. Если в вашей таблице отсутствует дата, вы возвращаете 0 для оценки.

+0

True , но таблица чисел более гибкая - см. мой ответ для примера. IE: что, если теперь вам нужны последовательные номера? Вам нужна таблица для каждого типа данных? –

+1

Необходимыми последовательными номерами были бы другие варианты использования ;-) Если вам нужно настроить таргетинг на различные СУБД (например, Oracle, MySQL, SQL-Server), вашему подходу понадобится слегка модифицированный оператор, и я подозреваю, что подход DATE_ADD медленнее, чем календарь table (но я думаю, что здесь не уместно) – Soundlink

+0

На [SQL Server 2005 рекурсивный CTE был * чуть быстрее, чем трюк таблицы NUMBERS] (http://stackoverflow.com/questions/1478951/tsql-generate-a- resultset-of-incrementing-dates/1479028 # 1479028) –