nieznany bohater: generate_series

kto z was wie o istnieniu funkcji generate_series() w postgresie? a kto z niej kiedykolwiek (nie testowo!) skorzystał?
wydaje mi się, że jest to najbardziej niedoceniania funkcja istniejąca w tym systemie bazodanowym.
co daje? zobacz:

select * from generate_series(0, 100);

na pierwszy rzut oka nic rewolucyjnego? taki tam sobie liczniczek?
zobaczmy więc kilka przykładowych zastosowań.
pierwsze:
mamy tabelkę, z polem id (z seriala), i chcemy sprawdzić czy są dziury w numeracji. tradycyjna metoda to jakieś joiny czy skanowanie sekwencyjne ze zliczaniem dziur w aplikacji klienckiej.
a zobaczmy coś takiego:

create table x as select * from generate_series(1, 10000) as x (i);
delete from x where random() < 0.0010;

w ten sposób uzyskaliśmy tabelę x, z numerycznym polem i. a w numeracji tego pola są dziury.
jak je wylistować? tak:

select * from generate_series(1, 10000) except select i from x;

voila. działa. ślicznie. i szybko 🙂

to było proste i oczywiste.
teraz przykład trochę mniej oczywisty – przykład jest z mojej pracy, ale użyję trochę uproszczonej wersji struktur danych 🙂
stwórzmy sobie tabelkę z użytkownikami:

CREATE TABLE users (id serial PRIMARY KEY, username TEXT, registered date);

ok. teraz – wstawmy do niej 10 użytkowników, z losowymi unikalnymi username'ami, i różnymi, losowymi datami rejestracji:

insert into users (username, registered) select 'user: ' || x.i, '2000-01-01'::date + cast(random()*1000 as int4) from generate_series(1, 10) as x(i);

mamy teraz w users 10 użytkowników. daty rejestracji są losowane z zakresu ‘2000-01-01' do ‘2002-09-27';
teraz. to co potrzebowałem to musiałem wygenerować "faktury" dla użytkowników. w tym zaległe.
idea była taka, że w tabelce z fakturami:

CREATE TABLE invoices (id serial PRIMARY KEY, user_id INT4, paid_from date, paid_to date, status INT4);

są trzymane faktury użytkowników. każda faktura jest na 30 dni. musimy wstawić każdemu użytkownikowi tyle faktur by obejmowały cały okres od daty rejestracji użytkownika do chwili obecnej.
przykładowo:
jeśli mielibyśmy użytkownika który zarejestrował się 2006-12-01 (a dziś jest 2007-01-11) to musielibyśmy wygenerować dla niego 2 faktury:

  1. od 2006-12-01 do 2006-12-30
  2. od 2007-12-31 do 2007-01-29

dalszych nie, bo musimy zakończyć faktury na fakturze "aktualnej".
pierwsze co potrzebujemy to wiedzieć ile maksymalnie nam się może faktur wygenerować dla pojedynczego użytkownika. w naszym przypadku sprawdzamy to poprzez:

select ceil((now()::date - min(registered))/30.0) from users;

u mnie pokazało się 83.
ponieważ lubię proste liczby, zaokrąglę sobie to do 100. nie ma żadnego matematycznego uzasadnienia – po prostu tak wolę.
tak więc. to co nam się przyda to generate_series(1,100), a dokładniej generate_series(0,100).
pierwsze zapytanie które napiszemy zwróci nam dla danego użytkownika (na razie tylko jednego) listę 101 faktur (od 0 do 100):

SELECT u.registered + (x.i * 30), u.registered + (x.i*30) + 29 FROM users u, generate_series(0, 100) as x(i) WHERE u.id = 1;

zwracam uwagę na to, że nie mamy żadnego warunku na połączenie users i generate_series – nie jest to join, ale tzw. kartezjan.
jak widać – zapytanie zadziałało.
teraz musimy je ograniczyć, by zwróciło dane tylko do aktualnych:

SELECT
    u.registered + (x.i * 30), u.registered + (x.i*30) + 29
FROM
    users u, generate_series(0, 100) as x(i)
WHERE
    u.id = 1
    AND now()::date >= u.registered + (x.i * 30)
;

wow. działa.
to teraz. ponieważ mamy do czynienia z kartezjanem a nie joinem, to usunięcie "where u.id = 1" nie spowoduje uszkodzenia danych, tylko wypełnienie ich dla wszystkich userów.
dzięki czemu możemy zapisać finalne zapytanie w postaci:

INSERT INTO invoices (user_id, paid_from, paid_to, status)
SELECT
    u.id,
    u.registered + (x.i * 30),
    u.registered + (x.i*30) + 29,
    0
FROM
    users u, generate_series(0, 100) as x(i)
WHERE
    now()::date >= u.registered + (x.i * 30)
;

czy na pewno zadziałało dobrze?
zobaczmy:

SELECT
    u.id,
    u.registered,
    ( SELECT count(*) FROM invoices i WHERE i.user_id = u.id ) as invoice_count,
    i1.paid_from as invoice1_from,
    i1.paid_to as invoice1_to,
    i2.paid_from as invoice2_from,
    i2.paid_to as invoice2_to,
    now()::date as "now"
FROM
    users u,
    invoices i1,
    invoices i2
WHERE
    i1.id = (SELECT min(t.id) FROM invoices t WHERE t.user_id = u.id)
    AND
    i2.id = (SELECT max(t.id) FROM invoices t WHERE t.user_id = u.id)
;

u mnie wszystko zadziałało ok.
jak widać (mam nadzieję) – generate_series potrafi sporo. generowanie list liczb pozwala na robienie np. robienie zestawień, włącznie z wyświetlaniem sum danych nieistniejących, sprawdzanie braków, "zgadywanie" pewnych wartości.
pozostaje zachęcić was do testowania i bawienia się – to czym i jak się bawicie jest niejednokrotnie ważniejsze od finalnego efektu – efekt jest jednorazowy. zabawa owocuje wiedzą która zostaje i kiedyś sie przyda.
aha. ważna notatka – generate_series zostało dodane w 8.0. no ale przecież chyba nikt już nie używa baz starszych 🙂 (żartuję oczywiście)

trackbacki

od czasu gdy zainstalowałem sobie wp-hashcasha i wywaliłem w diabły akismet, nie mam w ogóle spamerskich komentarzy.
ale.
ale jest coś takiego jak trackback. trackbacki nie podlegają filtrowaniu, są w pełni automatyczne, a na stronach wyświetlają się tak jak komentarze.
raj dla spamerów.
kiedyś strasznie mi tym spamowali jeden post nt. tego kto zagra jokera w batmanie. wyblokowałem trackbacki do tego postu. od kilku dni jednakże dostaję po kilka spamerskich trackbacków do innych postów.
przykład?
do wyciąg z logów apache'a, od "22/Dec/2006:14:26:08 +0000" do teraz. pierwsza kolumna to ilość trackbacków, druga – który post miał ich tyle:

    756 /index.php/2006/07/22/kto-zagra-jokera-w-kontynuacji-batman-begins/trackback/
      2 /index.php/2006/12/07/niagara-2-zapowiedzi/trackback/
      1 /index.php/2007/01/06/wyszukiwanie-wg-nipu/trackback/
      1 /index.php/2007/01/05/nowe-sciany/trackback/
      1 /index.php/2007/01/02/nowe-opony/trackback/
      1 /index.php/2007/01/02/meta-poprawka/trackback/
      1 /index.php/2006/12/27/lorem-ipsum/trackback/
      1 /index.php/2006/12/25/nowy-koncept-jaguara/trackback/
      1 /index.php/2006/12/23/searchmash-firefox/trackback/
      1 /index.php/2006/12/23/nowe-lamborghini/trackback/
      1 /index.php/2006/12/20/lepszy-malloc/trackback/
      1 /index.php/2006/12/07/kolejna-kultowa-firma-przejeta-przez-nudnego-molocha/trackback/
      1 /index.php/2006/12/07/google-zmieni-swiat/trackback/
      1 /index.php/2006/12/01/ciekawy-benchmark/trackback/
      1 /index.php/2006/05/30/jak-wyglada-kokaina/trackback/

śmieszne. niestety – jak do tej pory nikt nie użył trackbacków do niczego innego niż spamowanie, więc po prostu pewnie wyłączę w całości obsługę takich urli (mod_rewrite rządzi).
macie jakieś inne pomysły? czy to co stanowi podwaliny "blogosfery" (blograjdołka:) musi zniknąć?

apple-telefon

od dosyć dawna w sieci pełno było spekulacji nt. tego, że apple ma wyprodukować telefon gsm.
pojawiały się informacje, że będzie to iphone, potem się okazało, że taki produkt faktycznie został wypuszczony, ale jest to telefon cisco (a dokładniej należącego do cisco linksysa). w szczególności – był to nie tyle telefon, co cała seria telefonów.
jednakże okazało się, że w plotkach była część prawdy – apple jednak przygotowywał telefon. i faktycznie nazywa się iPhone.
co potrafi? (jak wygląda można zobaczyć tu).
to co standardowa komórka, plus kilka dodatkowych "myków". muzyka, fotografie (ma wbudowany 2megapikselowy aparat), filmy (komórka jest wyposażona w duży ekran – dużo większy niż w normalnych telefonach. było to możliwe dzięki temu, że telefon nie ma klawiatury.
komórka będzie miała 4 lub 8 giga pamięci. na pokładzie macosx, z kilkoma fajnymi aplikacjami – po pierwsze, normalna przeglądarka – safari. po drugie – zintegrowany google search i mapsy. do tego kilka miłych feature'ów:

  • dzwonienie bezpośrednio z interfejsu google-maps
  • czujnik zbliżeniowy wykrywający, że np. telefon jest blisko ucha i włączający funkcje telefoniczne
  • ekran typu multi-touch
  • czujnik położenia, dzięki czemu aparat wie kiedy jest odwrócony i wie, że trzeba przełączyć ekran w inny tryb pracy (pion/poziom).

całość oczywiście bardzo designerska i wygodna. kiedy w polsce? nie wiadomo. wiadomo, że na razie w stanach sprzedawać go będzie tylko jeden z operatorów (cingular).

mysql – skalowalność. kolejna odsłona

napisałem ostatnio o kolejnych testach jakie robili kolesie z tweakers.net.
bluszcz wtedy skomentował, że chciałby zobaczyć testy postgresa z soliddb lub choćby falconem, a nie z kiepskawym innodb.
prosił i ma 🙂 blog mysql performance przeprowadził testy innodb, myisam i falcona. właśnie pod kątem skalowalności. efekt – ogólna, praktycznie całkowita porażka falcona.
przykłady wykres wyników jednego z testów wygląda tak:

przy czym uwaga: nie wybrałem takiego na którym falcon wyszedł najgorzej. są takie wykresy gdzie linia od wydajności falcona "leży" na osi.
do tego dochodzi jeszcze takie jedno zdanie:
"… Falcon cannot handle LIMIT properly …" (wyjęte z kontekstu, ale sens niezmieniony).
czekam teraz aż ktoś pokaże testy z soliddb. wtedy też na pewno ja zaprezentuję.

medal za marketing – o ile ktoś to kupił

jaka część waszego komputera wydaje się być mało istotna jeśli chodzi o wydajność gier?
trafiłem właśnie na firmę która robi i sprzedaje pewnien podzespół (kartę) do pecetów – dzięki tej karcie wydajność grania ma wzrosnąć, bo odciąża ona główny procesor od pewnych zadań.
jaka to karta? kto zgadnie?
otóż jest to karta sieciowa!
ethernet.
"killer network interface card". jest olbrzymia (jak na kartę sieciową). karta jest zasadniczo dedykowanym komputerkiem z zainstalowanym linuksem. i "inteligentniej" obsługuje sieć zmienijaszając lagi i "latency".
chyba jestem z innej bajki. na stronie producenta można zobaczyć rysunek który pokazuje, że po wstawieniu killer-nic'a, obciążenie procesora spada o połowę.
i sprzedają to za $250. kartę sieciową do gier! eh. pewnie niedługo zobaczymy specializowane "for performance gamers" karty-kontrolery myszki i klawiatury.

acer sprzedaje laptopy z zainstalowanym backdoorem?

tan chew keong wykrył na swoim laptopie coś dziwnego. nieznaną mu kontrolkę activex, ustawioną jako bezpieczną z możliwością zdalnego wykonywania, która to kontrolka obsłguje metodę Run() przyjmującą argumenty: drive, path, parameters.
hmm .. wygenerował testowego html'a i faktycznie – okazało się, że kontrolka ta pozwala zdalnej stronie na wykonanie programu na maszynie klienckiej.
pogrzebał dalej i znalazł identyczną kontrolkę na innym laptopie acera. plik był ten sam, z datą modyfikacji z 1998 roku.
co prawda gdy zupgrade'uje się ie do ie 7.0, to i tak pojawia się pytanie (mimo ustawień w rejestrze) czy pozwolić na wykonanie metody run na kontrolce, ale jeśli używa się ie6.0 (a tak domyślnie jest) – no cóż.
sam nie mam acera – nie mam jak sprawdzić. a czy w waszych acerach też są takie ciekawostki?