2010-08-21 46 views
49

Tengo una tabla con 2 columnas, fecha y puntaje. Tiene como máximo 30 entradas, para cada uno de los últimos 30 días.MySQL ¿cómo completar las fechas que faltan?

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

Mi problema es que algunas fechas que no aparecen - Quiero ver:

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 
... 

Lo que necesito de la única consulta es conseguir: 19,21,9,14,0,0 , 10,0,0,14 ... Eso significa que las fechas faltantes se llenan con 0.

Sé cómo obtener todos los valores y en el lenguaje del lado del servidor iterar a través de las fechas y perder los espacios en blanco. Pero, ¿es posible hacerlo en mysql, para que ordene el resultado por fecha y obtenga las piezas faltantes?

EDITAR: En esta tabla hay otra columna llamada ID de usuario, entonces tengo 30,000 usuarios y algunos de ellos tienen la puntuación en esta tabla. Borro las fechas todos los días si la fecha es < hace 30 días porque necesito los últimos 30 días para cada usuario. La razón es que estoy haciendo un gráfico de la actividad del usuario en los últimos 30 días y para trazar un gráfico necesito los 30 valores separados por coma. Así que puedo decir en la consulta consígame la actividad USERID = 10203 y la consulta me daría los 30 puntajes, uno para cada uno de los últimos 30 días. Espero ser más claro ahora.

+0

Sí, es posible, pero * ¿por qué? ¿Lo harías? – NullUserException

+0

Todavía no lo entiendo. No obtenga datos innecesarios de la base de datos si puede llenar esos vacíos con lo que está trazando el gráfico y ahorrará un poco de sobrecarga. – NullUserException

+2

pero luego tengo que SELECCIONAR los datos para USERID, obtengo, por ejemplo, 20 filas de fecha y puntaje y luego tengo que hacer un bucle en el idioma del lado del servidor (ASP) para verificar si hay una fecha hace 30 días, si es no haga que 0 else haga el valor de la base de datos ... ¿No es esto más engorroso que obtener valores de base de datos de 30 y simplemente construir la cadena? – Jerry2

Respuesta

49

MySQL no tiene funcionalidad recursiva, por lo que se queda con el uso de la tabla de números truco -

  1. Crear una tabla que sólo posee un número de incremento - fácil de hacer uso de un 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. Rellene la tabla usando:

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

    ... de tantos valores como necesite

  3. Use DATE_ADD para construir una lista de fechas, aumentando los días según el valor NUMBERS.id. Reemplazar "2010-06-06" y "2010-06-14" con sus respectivas fechas de inicio y fin (pero usar el mismo formato, AAAA-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 en su mesa de los datos en base a la porción de tiempo:

    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` 
    

Si desea mantener el formato de fecha, utilice el DATE_FORMAT function:

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

Gracias. ¿Es ésta una operación rápida de la que desaconseja el uso de dicho método y el cálculo del lado del servidor? – Jerry2

+4

@ Jerry2: Mi preferencia es hacer todo el procesamiento de datos en la base de datos, a falta de material de presentación realmente complicado. No envidio hacer esto en el código de la aplicación, siempre que sea * un viaje a la base de datos * ... –

+0

Para usar los índices, las condiciones (cláusulas WHERE y ON) pueden reescribirse a 'WHERE n.id < DATEDIFF ('2010-06-14', '2010-06-06') 'y' LEFT JOIN TABLE y ON y.date = DATE_FORMAT (x.ts, '% d.% M.% Y') ' –

13

usted puede lograr esto usando un Calendario Tabla. Esa es una tabla que usted crea una vez y llena con un rango de fechas (por ejemplo, un conjunto de datos para cada día 2000-2050, que depende de sus datos). Luego puede hacer una combinación externa de su tabla con la tabla de calendario. Si falta una fecha en su tabla, devuelve 0 para el puntaje.

+0

True , pero una tabla de números es más flexible: mira mi respuesta para ver un ejemplo. IE: ¿y si ahora necesitas números secuenciales también? ¿Quieres una tabla por tipo de datos? –

+1

Necesitar números secuenciales sería otro caso de uso ;-) Si tiene que apuntar a diferentes DBMS (es decir, Oracle, MySQL, SQL-Server) su enfoque necesitaría una declaración ligeramente modificada, y sospecho que el enfoque DATE_ADD es más lento que un calendario tabla (pero creo que no es relevante aquí) – Soundlink

+0

En [SQL Server 2005, un CTE recursivo era * apenas * más rápido que el truco de la tabla NUMBERS] (http://stackoverflow.com/questions/1478951/tsql-generate-a- resultset-of-incrementing-dates/1479028 # 1479028) –

Cuestiones relacionadas