Mysql
 sql >> Baza danych >  >> RDS >> Mysql

Porównanie dwóch zakresów dat w tej samej tabeli

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.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Funkcja PHP i MySQL usuwa niezaznaczone kategorie z problemu z bazą danych

  2. Puste spacje w nazwach kolumn w MySQL

  3. Jak zaimportować pojedynczą tabelę do bazy danych mysql za pomocą wiersza poleceń?

  4. MySQL przy aktualizacji zduplikowanych kluczy

  5. Porządkowanie kolumn tabeli MySql za pomocą Pythona