Funkcja przyjmuje 2 lub 3 argumenty, które są liczbami lub datą. Trzeci argument (step) nie jest wymagany dla wartości typu biginteger (domyślnie jest równy 1), ale jest konieczny gdy chcemy stworzyć serie dla timestapów:
generate_series(start, stop, step)
Sprawdźmy kilka podstawowych przypadków:
test=# select generate_series(1,5) as d;
d
---
1
2
3
4
5
(5 rows)
test=# select generate_series(5,1,-1) as d;
d
---
5
4
3
2
1
(5 rows)
test=# select generate_series('2010-03-01'::timestamp, '2010-03-05', '24 hours');
generate_series
---------------------
2010-03-01 00:00:00
2010-03-02 00:00:00
2010-03-03 00:00:00
2010-03-04 00:00:00
2010-03-05 00:00:00
(5 rows)
test=# select generate_series(1,2) as id , date(generate_series('2010-03-01'::timestamp, '2010-03-05', '24 hours'));
id | date
----+------------
1 | 2010-03-01
2 | 2010-03-02
1 | 2010-03-03
2 | 2010-03-04
1 | 2010-03-05
2 | 2010-03-01
1 | 2010-03-02
2 | 2010-03-03
1 | 2010-03-04
2 | 2010-03-05
(10 rows)
Funkcja jest także bardzo pomocna przy generowaniu danych testowych. Wystarczy, że użyjemy razem z nią funkcji random:test=# select r from (select generate_series(1,5) as id, round(random()*100) as r) as foo; r ---- 76 93 91 68 34 (5 rows)
Generowanie kolejno zdefiniowanych ciągu znaków:
test=# SELECT myarray[i] as element FROM (SELECT ARRAY['one','two','tree','four','five','six'] As myarray) as foo CROSS JOIN generate_series(1, 6) As i; element --------- one two tree four five six (6 rows)A co z tekstami? Jeśli wystarczy, że będą to ciągi znaków w pełni losowych, to możemy użyć do tego jeszcze funkcji substring i md5:
test=# select c2 from (select generate_series(1,10) as id, substring(md5(random()::text) from 0 for 20) as c2) foo;
c2
---------------------
81b7985f41be65f56cb
dd516dc4e04d39b3356
b40721cfc0738bf98ed
5ca0de341103fbb87de
2e8b3a407257743261f
7e45b2df06fb93d0821
cbec578dcd1a86366fb
83fecb8953dc5b07813
6317b12d53e5da59d87
ad35dc89a0772e49684
(10 rows)
Przejdźmy jednak do konkretnego przypadku raportu, który potrzebuję wygenerować. W tabeli table1 trzymam informacje o klientach, którzy pojawili się w danym czasie (kolumna: appeared_at, typ timestamp). Ja jednak potrzebuję wiedzieć tylko ile było takich osób danego dnia. Raport wymaga aby policzyć klientów dla wszystkich dni (więc dla dni, gdzie klienci się nie pojawili potrzebuję wyświetlić 0). Dlatego potrzebuję użyć RIGHT JOINa z tabelką, która jest serią podstawowych danych o datach z wybranego przedziału i zwykłym iterowaniem (dla lepszego pokazaniu wyniku).
test=# SELECT seq.id, seq.date, concat(data.count,0)
FROM (
SELECT date(appeared_at) AS date, count(*)
FROM table1
WHERE appeared_at BETWEEN '2010-03-01' AND '2010-03-10'
GROUP BY DATE(appeared_at)
) data
RIGHT JOIN (
SELECT generate_series(1,10) as id, DATE(generate_series('2010-03-01'::timestamp, '2010-03-10', '24 hours')) AS date
) seq
ON seq.date = data.date
ORDER BY seq.id;
id | date | concat
----+------------+--------
1 | 2010-03-01 | 20400
2 | 2010-03-02 | 12330
3 | 2010-03-03 | 29770
4 | 2010-03-04 | 15500
5 | 2010-03-05 | 15170
6 | 2010-03-06 | 22150
7 | 2010-03-07 | 140620
8 | 2010-03-08 | 106960
9 | 2010-03-09 | 34480
10 | 2010-03-10 | 0
(10 rows)
Proste, prawda? :-)