Mysql
 sql >> Base de données >  >> RDS >> Mysql

Comparer deux plages de dates dans le même tableau

À l'aide d'IBM Informix Dynamic Server 11.50.FC6, je peux utiliser cette séquence SQL pour obtenir le résultat souhaité :

Configuration

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);

Requête

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;

Résultat

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

Explication

Il a fallu une bonne quantité d'expérimentation pour obtenir ce "juste". Informix a une fonction constructeur DATE MDY() qui prend trois arguments entiers :le mois, le jour et l'année (le nom est mnémonique). Il dispose également de trois fonctions d'analyse :DAY(), MONTH() et YEAR() qui renvoient le jour, le mois et l'année de l'argument date. La requête interne avec FULL JOIN vous donne les résultats avec des valeurs nulles à gauche et à droite. Le critère en 5 parties dans la clause ON semble nécessaire; sinon, les critères de la requête externe doivent être plus complexes et déroutants - si cela peut être fait pour fonctionner du tout. Ensuite, les critères de la sélection externe garantissent que les bonnes données sont choisies. L'un des avantages des expressions NVL() dans la requête interne est que les colonnes d'ID de magasin sont à la fois identiques et non nulles et qu'aucune colonne de date n'est nulle, de sorte que la clause order by peut être plus simple - sur l'ID de magasin et l'une ou l'autre des colonnes de date. /P>

Dans Informix, il serait aussi possible de retravailler les expressions de date comme :

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

Il y a en fait plusieurs conversions de type en cours dans les coulisses avec cette notation, mais cela vous donne le même résultat et le calcul supplémentaire n'est probablement pas si important.

Il y a aussi un problème d'attente dans Informix ; vous ne pouvez pas ajouter ou soustraire 1 an à ou de n'importe quel 29 février - parce qu'il n'y a pas de 29 février dans l'année suivante ou précédente. Vous auriez besoin d'être prudent avec vos données; si vous ne l'êtes pas, vous pourriez finir par comparer les données du 2008-02-29 avec celles du 2009-02-28 (ainsi que les données du 2008-02-28 avec celles du 2009-02-28). Il existe un processus appelé «comptabilité en partie double», mais ce n'est pas ce que l'on entend par là, et vos calculs pourraient être confus si «2008-02-29 plus 1 an» est 2009-02-28. Informix génère une erreur; ce n'est pas beaucoup plus utile. Vous pourriez probablement coder une procédure stockée pour renvoyer NULL pour le 2008-02-29 plus 1 an puisqu'il n'y a aucune date avec laquelle comparer ses ventes.

Vous devriez pouvoir adapter assez facilement l'arithmétique des dates à MySQL ; le reste du code n'a pas besoin de changer.