sql >> Base de Datos >  >> RDS >> Mysql

Comparar dos rangos de fechas dentro de la misma tabla

Usando IBM Informix Dynamic Server 11.50.FC6, puedo usar esta secuencia SQL para obtener el resultado que necesita:

Configuración

CREATE TABLE sales
(
    id       INTEGER NOT NULL,
    id_store INTEGER NOT NULL,
    date     DATE NOT NULL,
    total    DECIMAL(10,2) NOT NULL
);

INSERT INTO sales VALUES( 1, 1, '2010-01-01', 500.00);
INSERT INTO sales VALUES( 2, 1, '2010-01-02', 185.00);
INSERT INTO sales VALUES( 3, 1, '2010-01-03', 135.00);
INSERT INTO sales VALUES( 4, 1, '2009-01-01', 165.00);
INSERT INTO sales VALUES( 5, 1, '2009-01-02', 175.00);
INSERT INTO sales VALUES( 6, 5, '2010-01-01', 130.00);
INSERT INTO sales VALUES( 7, 5, '2010-01-02', 135.00);
INSERT INTO sales VALUES( 8, 5, '2010-01-03', 130.00);
INSERT INTO sales VALUES( 9, 6, '2010-01-01', 100.00);
INSERT INTO sales VALUES(10, 6, '2010-01-02',  12.00);
INSERT INTO sales VALUES(11, 6, '2010-01-03',  85.00);
INSERT INTO sales VALUES(12, 6, '2009-01-01', 135.00);
INSERT INTO sales VALUES(13, 6, '2009-01-02', 400.00);
INSERT INTO sales VALUES(14, 6, '2009-01-07',  21.00);
INSERT INTO sales VALUES(15, 6, '2009-01-08',  45.00);
INSERT INTO sales VALUES(16, 8, '2009-01-09', 123.00);
INSERT INTO sales VALUES(17, 8, '2009-01-10', 581.00);

Consulta

SELECT *
  FROM (SELECT s1.id AS s1id,
               NVL(s1.id_store, s2.id_store) AS s1store,
               NVL(s1.date, MDY(MONTH(s2.date), DAY(s2.date),
                                YEAR(s2.date)+1)) AS s1date,
               s1.total AS s1total,
               s2.id AS s2id,
               NVL(s2.id_store, s1.id_store) AS s2store,
               NVL(s2.date, MDY(MONTH(s1.date), DAY(s1.date),
                                YEAR(s1.date)-1)) AS s2date,
               s2.total AS s2total
          FROM sales AS s1 FULL JOIN sales AS s2
            ON s1.id_store = s2.id_store
           AND s1.date BETWEEN '2010-01-01' AND '2010-01-10'
           AND s2.date BETWEEN '2009-01-01' AND '2009-01-10'
           AND DAY(s1.date)   = DAY(s2.date)
           AND MONTH(s1.date) = MONTH(s2.date)
       ) AS s3
 WHERE s1_date BETWEEN '2010-01-01' AND '2010-01-10'
   AND s2_date BETWEEN '2009-01-01' AND '2009-01-10'
 ORDER BY s1_id_store ASC, s1_date ASC;

Resultado

s1id s1store  s1date     s1total  s2id s2store  s2date     s2total
 1       1    2010-01-01  500.00   4       1    2009-01-01  165.00
 2       1    2010-01-02  185.00   5       1    2009-01-02  175.00
 3       1    2010-01-03  135.00           1    2009-01-03             
 6       5    2010-01-01  130.00           5    2009-01-01             
 7       5    2010-01-02  135.00           5    2009-01-02             
 8       5    2010-01-03  130.00           5    2009-01-03             
 9       6    2010-01-01  100.00  12       6    2009-01-01  135.00
10       6    2010-01-02   12.00  13       6    2009-01-02  400.00
11       6    2010-01-03   85.00           6    2009-01-03             
         6    2010-01-07          14       6    2009-01-07   21.00
         6    2010-01-08          15       6    2009-01-08   45.00
         8    2010-01-09          16       8    2009-01-09  123.00
         8    2010-01-10          17       8    2009-01-10  581.00

Explicación

Tomó una buena cantidad de experimentación para hacer esto 'bien'. Informix tiene una función constructora DATE MDY() que toma tres argumentos enteros:el mes, el día y el año (el nombre es mnemotécnico). También tiene tres funciones de análisis:DÍA(), MES() y AÑO() que devuelven el día, mes y año del argumento de fecha. La consulta interna con FULL JOIN le brinda los resultados con valores nulos en los lados izquierdo y derecho. El criterio de las 5 partes en la cláusula ON parece ser necesario; de lo contrario, los criterios en la consulta externa tienen que ser más complejos y confusos, si es que se puede hacer que funcione. Luego, los criterios en la selección externa aseguran que se elijan los datos correctos. Una ventaja de las expresiones NVL() en la consulta interna es que las columnas de ID de la tienda son iguales y no son nulas, y ninguna columna de fecha es nula, por lo que la cláusula order by puede ser más simple:en la ID de la tienda y en cualquiera de las columnas de fecha.

En Informix, también sería posible modificar las expresiones de fecha como:

NVL(s1.date, s2.date + 1 UNITS YEAR)
NVL(s2.date, s1.date - 1 UNITS YEAR)

En realidad, hay conversiones de tipos múltiples detrás de escena con esa notación, pero le da el mismo resultado y el cálculo adicional probablemente no sea tan significativo.

También hay una falla en la espera en Informix; no puede sumar o restar 1 año a o desde cualquier 29 de febrero, porque no hay 29 de febrero en el año siguiente o anterior. Debería tener cuidado con sus datos; si no es así, podría terminar comparando los datos de 2008-02-29 con 2009-02-28 (además de comparar los datos de 2008-02-28 con 2009-02-28). Hay un proceso llamado 'contabilidad de partida doble', pero esto no es lo que significa, y sus cálculos podrían confundirse si '2008-02-29 más 1 año' es 2009-02-28. Informix genera un error; eso no es mucho más útil. Podría codificar un procedimiento almacenado, probablemente, para devolver NULL para 2008-02-29 más 1 año ya que no hay ninguna fecha con la que comparar sus ventas.

Debería poder adaptar la aritmética de fechas a MySQL con bastante facilidad; el resto del código no necesita cambiar.