Używając IBM Informix Dynamic Server 11.50.FC6, mogę użyć tej sekwencji SQL, aby uzyskać żądany wynik:
Konfiguracja
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);
Zapytanie
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;
Wynik
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
Wyjaśnienie
Uzyskanie tego „właściwego” wymagało sporej ilości eksperymentów. Informix ma funkcję konstruktora DATE MDY(), która przyjmuje trzy argumenty w postaci liczb całkowitych:miesiąc, dzień i rok (nazwa to mnemonik). Posiada również trzy funkcje analityczne:DAY(), MONTH() i YEAR(), które zwracają dzień, miesiąc i rok argumentu daty. Zapytanie wewnętrzne z FULL JOIN daje wyniki z wartościami null po lewej i prawej stronie. Niezbędne wydaje się 5-częściowe kryterium w klauzuli ON; w przeciwnym razie kryteria w zewnętrznym zapytaniu muszą być bardziej złożone i zagmatwane — jeśli w ogóle można to zrobić. Następnie kryteria w selekcji zewnętrznej zapewniają wybór właściwych danych. Jedną z zalet wyrażeń NVL() w zapytaniu wewnętrznym jest to, że kolumny identyfikatora sklepu są takie same i nie mają wartości null, a żadna kolumna daty nie ma wartości null, więc klauzula order by może być prostsza — na identyfikatorze sklepu i dowolnej kolumnie daty.
W Informix możliwe byłoby również przerobienie wyrażeń dat w następujący sposób:
NVL(s1.date, s2.date + 1 UNITS YEAR)
NVL(s2.date, s1.date - 1 UNITS YEAR)
W rzeczywistości istnieje wiele konwersji typu za kulisami z tą notacją, ale daje to ten sam wynik, a dodatkowe obliczenia prawdopodobnie nie są aż tak znaczące.
Istnieje również usterka w czekaniu w Informix; nie można dodać ani odjąć 1 roku do lub od dowolnego 29 lutego - ponieważ nie ma 29 lutego w następnym lub poprzednim roku. Musisz być ostrożny ze swoimi danymi; jeśli nie, możesz skończyć porównując dane za 2008-02-29 z 2009-02-28 (jak również porównując dane za 2008-02-28 z 2009-02-28). Istnieje proces zwany „księgowością podwójnego wpisu”, ale nie o to chodzi, a obliczenia mogą być pomylone, jeśli „2008-02-29 plus 1 rok” to 2009-02-28. Informix generuje błąd; to nie jest dużo bardziej pomocne. Prawdopodobnie możesz zakodować procedurę składowaną, aby zwrócić NULL dla 29.02.2008 plus 1 rok, ponieważ nie ma żadnej daty, z którą można porównać jej sprzedaż.
Powinieneś być w stanie dość łatwo dostosować arytmetykę dat do MySQL; reszta kodu nie musi być zmieniana.