2012-05-11 46 views
9

Mam zestaw danych adresów e-mail i dat, w których te adresy e-mail zostały dodane do tabeli. Może istnieć wiele wpisów adresu e-mail dla różnych dat. Na przykład, jeśli mam zestaw danych poniżej. Chciałbym uzyskać datę i liczbę różnych e-maili, które mamy między wspomnianą datą a 3 dniami.Zapytanie o liczbę różnych wartości w kroczącym zakresie dat

Date | email 
-------+---------------- 
1/1/12 | [email protected] 
1/1/12 | [email protected] 
1/1/12 | [email protected] 
1/2/12 | [email protected] 
1/2/12 | [email protected] 
1/3/12 | [email protected] 
1/4/12 | [email protected] 
1/5/12 | [email protected] 
1/5/12 | [email protected] 
1/6/12 | [email protected] 
1/6/12 | [email protected] 
1/6/12 | [email protected] 

Wynik zestaw będzie wyglądać mniej więcej tak, jeśli używamy okres dat 3

date | count(distinct email) 
-------+------ 
1/1/12 | 3 
1/2/12 | 3 
1/3/12 | 3 
1/4/12 | 3 
1/5/12 | 2 
1/6/12 | 2 

mogę uzyskać wyraźną rachubę zakres dat za pomocą poniższego zapytania, ale chcą uzyskać zliczania zakresu w ciągu dnia, więc nie muszę ręcznie aktualizować zakresu dla setek dat.

select test.date, count(distinct test.email) 
from test_table as test 
where test.date between '2012-01-01' and '2012-05-08' 
group by test.date; 

Pomoc jest doceniana.

Odpowiedz

0

w serwerze SQL:

`select test.date, count(distinct test.email) from test_table as test where convert(date,test.date) between '2012-01-01' and '2012-05-08' group by test.date` 

nadzieję, że to pomaga.

+1

To jest oznaczone PostgreSQL. W tym miejscu nie ma funkcji 'convert()'. (Cóż, istnieje funkcja 'convert()', ale jest to konwersja kodowania, a nie typów danych, jak na serwerze SQL.) Odradza się także sygnatury, a twój talent w prawym dolnym zajmuje się tym. Zobacz [tutaj] (http://meta.stackexchange.com/questions/5029/are-taglines-signatures-disallowed) lub [tutaj] (http://stackoverflow.com/faq#signatures). –

11

przypadek testowy:

CREATE TEMP TABLE tbl (day date, email text); 
INSERT INTO tbl VALUES 
('2012-01-01', '[email protected]') 
,('2012-01-01', '[email protected]') 
,('2012-01-01', '[email protected]') 
,('2012-01-02', '[email protected]') 
,('2012-01-02', '[email protected]') 
,('2012-01-03', '[email protected]') 
,('2012-01-04', '[email protected]') 
,('2012-01-05', '[email protected]') 
,('2012-01-05', '[email protected]') 
,('2012-01-06', '[email protected]') 
,('2012-01-06', '[email protected]') 
,('2012-01-06', '[email protected]`'); 

Zapytanie - zwraca tylko dzień, gdzie istnieje wpis w tbl:

SELECT day 
    ,(SELECT count(DISTINCT email) 
     FROM tbl 
     WHERE day BETWEEN t.day - 2 AND t.day -- period of 3 days 
    ) AS dist_emails 
FROM tbl t 
WHERE day BETWEEN '2012-01-01' AND '2012-01-06' 
GROUP BY 1 
ORDER BY 1; 

Albo - powrót wszystkie dni w określonym zakresie, nawet jeśli nie ma wiersze na dzień:

SELECT day 
    ,(SELECT count(DISTINCT email) 
     FROM tbl 
     WHERE day BETWEEN g.day - 2 AND g.day 
    ) AS dist_emails 
FROM (SELECT generate_series('2012-01-01'::date 
          , '2012-01-06'::date, '1d')::date) AS g(day) 

Resul T:

day  | dist_emails 
-----------+------------ 
2012-01-01 | 3 
2012-01-02 | 3 
2012-01-03 | 3 
2012-01-04 | 3 
2012-01-05 | 1 
2012-01-06 | 2 

Brzmiało to jak zadanie dla window functions w pierwszym, ale nie mogę znaleźć sposób, aby określić odpowiednią ramę okna. Również per documentation:

kruszywa funkcje okna, w przeciwieństwie do normalnych funkcji agregujących, nie pozwalają DISTINCT lub ORDER BY być stosowane w liście argumentów funkcji.

Więc zamiast tego rozwiązałem go z skorelowanymi podzapytaniami. Sądzę, że to najmądrzejszy sposób.

Zmieniono nazwę kolumny daty na day, ponieważ niewłaściwe jest stosowanie nazw typów jako identyfikatorów.

BTW, "między wspomnianą datą a 3 dniami" byłby okres dni. Twoja definicja jest tam sprzeczna.

Nieco krótsze, ale wolniej tylko przez kilka dni:

SELECT day, count(DISTINCT email) AS dist_emails 
FROM (SELECT generate_series('2013-01-01'::date 
          , '2013-01-06'::date, '1d')::date) AS g(day) 
LEFT JOIN tbl t ON t.day BETWEEN g.day - 2 AND g.day 
GROUP BY 1 
ORDER BY 1; 
+0

świetna odpowiedź, dziękuję @ErwinBrandstetter – Sean

0

Zamiast określania daty, można zawsze używać DateAdd funkcję:

test.date > dateadd(dd,-7,getdate()) 
+1

W PostgreSQL nie ma "dateadd()". –

0

Przykładem okno wyraźną przesuwnych liczyć:

SELECT b.day, count(DISTINCT a.user_id) 
from glip_production.presences_1d a, 
(SELECT distinct(day), TIMESTAMPADD(day,-6, day) dt_start 
    from glip_production.presences_1d t1) b 
where a.day >= b.dt_start and a.day <= b.day and b.day > '2017-11-01' 
group by b.day 
Powiązane problemy