Сравнение двух диапазонов дат в одной таблице - PullRequest
3 голосов
/ 05 апреля 2010

У меня есть таблица с продажами в магазине следующим образом:

SQL> select * from sales;

        ID ID_STORE DATE       TOTAL
---------- -------- ---------- -------------------------------
         1        1 2010-01-01    500.00
         2        1 2010-01-02    185.00
         3        1 2010-01-03    135.00
         4        1 2009-01-01    165.00
         5        1 2009-01-02    175.00
         6        5 2010-01-01    130.00
         7        5 2010-01-02    135.00
         8        5 2010-01-03    130.00
         9        6 2010-01-01    100.00
         10       6 2010-01-02     12.00
         11       6 2010-01-03     85.00
         12       6 2009-01-01    135.00
         13       6 2009-01-02    400.00
         14       6 2009-01-07     21.00
         15       6 2009-01-08     45.00
         16       8 2009-01-09    123.00
         17       8 2009-01-10    581.00

17 rows selected.

Что мне нужно сделать, это сравнить два диапазона дат в этой таблице.Допустим, мне нужно знать разницу в продажах с 01 января 2009 года по 10 января 2009 года ПРОТИВ 01 января 2010 года по 10 января 2010 года.

Я хотел бы создать запрос, который возвращает что-то вроде этого:

ID_STORE_A DATE_A     TOTAL_A   ID_STORE_B DATE_B     TOTAL_B
---------- ---------- --------- ---------- ---------- -------------------
         1 2010-01-01    500.00          1 2009-01-01    165.00
         1 2010-01-02    185.00          1 2009-01-02    175.00
         1 2010-01-03    135.00          1 NULL          NULL

         5 2010-01-01    130.00          5 NULL          NULL
         5 2010-01-02    135.00          5 NULL          NULL
         5 2010-01-03    130.00          5 NULL          NULL

         6 2010-01-01    100.00          6 2009-01-01    135.00
         6 2010-01-02     12.00          6 2009-01-02    400.00
         6 2010-01-03     85.00          6 NULL          NULL
         6 NULL          NULL            6 2009-01-07     21.00
         6 NULL          NULL            6 2009-01-08     45.00
         6 NULL          NULL            8 2009-01-09    123.00
         6 NULL          NULL            8 2009-01-10    581.00

Итак, даже если в том или ином диапазоне нет продаж, ему просто нужно заполнить пустое пространство NULL.

Пока что я пришел с этим быстрым запросом, ноИногда «даты» от продаж до продаж2 различаются в каждой строке:

SELECT sales.*, sales2.*
  FROM sales
  LEFT JOIN sales AS sales2 
    ON (sales.id_store=sales2.id_store)
 WHERE sales.date >= '2010-01-01' 
   AND sales.date <= '2010-01-10' 
   AND sales2.date >= '2009-01-01' 
   AND sales2.date <= '2009-01-10' 
ORDER BY sales.id_store ASC, sales.date ASC, sales2.date ASC

Что мне не хватает?

Ответы [ 2 ]

3 голосов
/ 17 апреля 2010

Используя IBM Informix Dynamic Server 11.50.FC6, я могу использовать эту последовательность SQL для получения требуемого результата:

Настройка

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

Запрос

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;

Результат

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

Объяснение

Потребовалось немало экспериментов, чтобы понять это "правильно". В Informix есть функция конструктора DATE MDY (), которая принимает три целочисленных аргумента: месяц, день и год (имя мнемоническое). Он также имеет три функции анализа: DAY (), MONTH () и YEAR (), которые возвращают день, месяц и год аргумента даты. Внутренний запрос с FULL JOIN дает результаты с нулями слева и справа. Критерий из 5 частей в предложении ON представляется необходимым; в противном случае критерии во внешнем запросе должны быть более сложными и запутанными - если их вообще можно заставить работать. Тогда критерии внешнего выбора гарантируют, что правильные данные выбраны. Одно из преимуществ выражений NVL () во внутреннем запросе состоит в том, что столбцы идентификатора хранилища одинаковы и не равны нулю, а столбец даты не равен нулю, поэтому предложение order by может быть проще - для идентификатора хранилища и любого столбца даты.

В Informix также возможно переработать выражения даты как:

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

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

В Informix также есть проблемы с ожиданием; Вы не можете прибавлять или вычитать 1 год к или от любого 29 февраля - потому что нет 29 февраля в следующем или предыдущем году. Вы должны быть осторожны с вашими данными; в противном случае вы можете сравнить данные за 2008-02-29 с 2009-02-28 (а также сравнить данные за 2008-02-28 с 2009-02-28). Существует процесс, который называется «двойная бухгалтерия», но это не то, что подразумевается под ним, и ваши вычисления могут быть сбиты с толку, если «2008-02-29 плюс 1 год» - 2009-02-28. Informix генерирует ошибку; это не намного более полезно. Вы можете написать хранимую процедуру, возможно, для возврата NULL за 2008-02-29 плюс 1 год, поскольку нет даты для сравнения ее продаж с.

Вы должны быть в состоянии довольно легко адаптировать арифметику даты к MySQL; остальной код менять не нужно.

1 голос
/ 05 апреля 2010

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

... ON (    sales.id_store = sales2.id_store 
        AND sales.date = ADDDATE(sales2.date, INTERVAL 1 YEAR) 
        ...
       )
...