<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
	<channel>
		<title><![CDATA[Forum PostgreSQL - pobieranie danych dla jednego rekordu z innego wczesniejszego rekordu]]></title>
		<link>https://forum.postgresql.org.pl/viewtopic.php?id=619</link>
		<description><![CDATA[Najświeższe odpowiedzi w pobieranie danych dla jednego rekordu z innego wczesniejszego rekordu.]]></description>
		<lastBuildDate>Wed, 24 Aug 2011 12:53:48 +0000</lastBuildDate>
		<generator>PunBB</generator>
		<item>
			<title><![CDATA[Odp: pobieranie danych dla jednego rekordu z innego wczesniejszego rekordu]]></title>
			<link>https://forum.postgresql.org.pl/viewtopic.php?pid=2657#p2657</link>
			<description><![CDATA[no niestety, jedyne rozwiazania dot swiat na jakie ja sie natknalem to wlasnie tabela albo rozne funkcje w php kombinujace z datami. dla moich potrzeb bardziej optymalna wydaje sie byc tabela gdyz cale zapyanie jest generowane do sql

btw najwyrazniej podczas gdy ty pisales odp ja pisalem edit do wczesniejszego posta :P moglbys sie do niego ustosunkowac? bo nawet po ostatnich poprawkach testy pokazaly ze niestety to nie dziala dla wyzszych agregat czasowych niz dzienna]]></description>
			<author><![CDATA[dummy@example.com (norris_85)]]></author>
			<pubDate>Wed, 24 Aug 2011 12:53:48 +0000</pubDate>
			<guid>https://forum.postgresql.org.pl/viewtopic.php?pid=2657#p2657</guid>
		</item>
		<item>
			<title><![CDATA[Odp: pobieranie danych dla jednego rekordu z innego wczesniejszego rekordu]]></title>
			<link>https://forum.postgresql.org.pl/viewtopic.php?pid=2655#p2655</link>
			<description><![CDATA[Proponowałbym trochę inaczej rozpisać te dwie funkcje tj.:

[code]/* Zwraca prawdę jeżeli dzień jest wolny od pracy,
   a fałsz w przeciwnym wypadku */
CREATE OR REPLACE FUNCTION isdayfree(date)
RETURNS boolean AS $BODY$
BEGIN   
    IF EXISTS (SELECT free_day FROM days_free WHERE free_day = $1) -- zakładając, że free_day ma typ date
        OR date_part('isodow', $1) IN (6, 7)
    THEN
        RETURN true;
    ELSE
        RETURN False;
    END IF;
END;
$BODY$
LANGUAGE plpgsql;

/* Zwraca następny dzień biznesowy */
CREATE OR REPLACE FUNCTION getNextBusinessDay(date)
RETURNS date AS $BODY$
DECLARE
    resultDate date := $1;
BEGIN
    resultDate := resultDate + 1;
    WHILE isdayfree(resultDate) = true LOOP
        resultDate := resultDate + 1;
    END LOOP;
    RETURN resultDate;
END;
$BODY$
LANGUAGE plpgsql;[/code]

Teraz w funkcji getNextBusinessDay znajduje się zwykła pętla typu DO-WHILE, która zatrzymuje się w momencie znalezienia następnego dnia biznesowego (opiera się na funkcji isdayfree, dlatego zupełnie jej nie interesuje w jaki sposób dzień jest ustalany jako wolny od pracy).

Uproszczone zapytanie zmienia się w jednym miejscu tj. date_part('isodow', valid_from_date) NOT IN (6, 7) na isdayfree(valid_from_date) = false:

[code]SELECT
    sum(saldo) AS saldo,
    date_part('doy', next_day) AS day_of_year,
    date_part('week', next_day) AS week_of_year,
    date_part('month', next_day) AS month_of_year,
    date_part('quarter', next_day) AS quarter,
    date_part('year', next_day) AS year,    
    count(id) AS count
FROM 
    balance a INNER JOIN (SELECT id, getNextBusinessDay(valid_from_date) AS next_day FROM balance) b
    USING (id)
WHERE
    (next_day BETWEEN '2011-01-01' AND '2011-01-31') AND
    isdayfree(valid_from_date) = false AND
    ghost = false AND
    product_3_id IN (59, 58, 60) AND
    product_group_id IN (3)
GROUP BY
    next_day
ORDER BY
    day_of_year;[/code]

Dla wcześniej rozpisanego zestawu wynik zapytania to:

[code] saldo | day_of_year | week_of_year | month_of_year | quarter | year | count
-------+-------------+--------------+---------------+---------+------+-------
  2600 |           4 |            1 |             1 |       1 | 2011 |     3
   400 |          10 |            2 |             1 |       1 | 2011 |     1
(2 rows)[/code]

Jak widać dzień 6 stycznia został pominięty w zestawieniu. Powinno to także działać poprawnie dla innych zestawów. Swoją drogą myślałem, że może w jakiś sposób lokalizacja posiada informacje o świętach (nie trzeba by wtedy robić dedykowanej tabeli), ale najwyraźniej nie ma czegoś takiego i trzeba to robić samodzielnie.]]></description>
			<author><![CDATA[dummy@example.com (gszpetkowski)]]></author>
			<pubDate>Wed, 24 Aug 2011 11:40:41 +0000</pubDate>
			<guid>https://forum.postgresql.org.pl/viewtopic.php?pid=2655#p2655</guid>
		</item>
		<item>
			<title><![CDATA[Odp: pobieranie danych dla jednego rekordu z innego wczesniejszego rekordu]]></title>
			<link>https://forum.postgresql.org.pl/viewtopic.php?pid=2651#p2651</link>
			<description><![CDATA[odlozylem temat na bok ale przyszla w koncu pora ogarnac go i tak...
troche sie nameczylem zeby przepisac swoj skrypt na to co zaproponowales i... DZIALA :) 

mam tylko jeszcze male "ale".  twoja funkcja getNaextBusinessDay() nie oblsluguje swiat. napisalem wiec mala funkcje 
[code]

CREATE OR REPLACE FUNCTION isdayfree(date)
  RETURNS boolean AS
$BODY$
begin    

if exists (select distinct free_day from days_free where date_part('day',free_day)=date_part('day', $1) 
    and date_part('month',free_day)=date_part('month', $1) and date_part('year',free_day)=date_part('year', $1)) 
then return true;
else return False;
end if;

end;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
[/code]

ktora sprawdza czy dany dzien jest sietem wolnym od pracy czy tez nie. mam jednak problem z zaimplementowaniem tego do twojej funkcji tak by dzialalo poprawnie. probowalem ja modyfikowac na dwa sposoby:
[code]
CREATE OR REPLACE FUNCTION getnextbusinessday(date)
  RETURNS date AS
$BODY$
    SELECT
        CASE 
            WHEN date_part('isodow', $1)=5 THEN CAST ($1 + interval '3 days' AS date) -- piątek
            WHEN date_part('isodow', $1)=6 or isDayFree($1)=true THEN CAST ($1 + interval '2 days' AS date) -- sobota
            when date_part('isodow', $1) not in (5,6) and isDayFree($1)=false then CAST ($1 + interval '1 days' AS date) -- pozostałe
        END;
$BODY$
  LANGUAGE sql VOLATILE
  COST 100;
[/code]
lub
[code]
CREATE OR REPLACE FUNCTION getnextbusinessday(date)
  RETURNS date AS
$BODY$begin

if not isDayFree($1) 
then 
return 
        CASE date_part('isodow', $1)
        WHEN 5 THEN CAST ($1 + interval '3 days' AS date) -- piątek
        WHEN 6 THEN CAST ($1 + interval '2 days' AS date) -- sobota
        ELSE CAST ($1 + interval '1 days' AS date) -- pozostałe
        END;
else return  CAST ($1 + interval '2 days' AS date);
end if;

end; $BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;
[/code]

obie te modyfikacje dzialaja z tym ze w wyniku dostaje:
[code]
saldo_prev | day_of_year | week_of_year | month_of_year | quarter |  year
-----------+-------------+--------------+---------------+---------+---------
55396551   |            4|             1|              1|        1|    2011
18768179   |            5|             1|              1|        1|    2011
18799097   |            6|             1|              1|        1|    2011
18801235   |            8|             1|              1|        1|    2011
18865999   |           10|             2|              1|        1|    2011
[/code]

a jak wiadomo to 06.01 a nie 07.01 byl swietem wolnym od pracyw zwiazku z czym dla 07.01 wartoscia salda jest oryginalna wartosc z 05.01, a 06.01 i 08.01 nie powinien sie pojawic

EDIT: po dokladniejszych testach musze stwirdzic ze twoj pomysl dziala nie do konca poprawnie, bo o ile jesli dane agreguje dziennie to jest wszystko w porzatku (pomijajac juz nawet te swieta) to w przypadku gdy chce dane agragowac tygodniowo, miesiecznie, kwartalnie lub rocznie jest problem. agregacje czasowe sa tworzone w php i wrzucane do sql za pomoca zmiennych ktore generuja dopowiednie fragmenty SELECTA. i o ile poradzielm sobie z uwzglednieniem twoich propozycji w wylistowaniu kolum i grup w tym selekcie o tyle nie wiem jak zmienic twoja funkcje tak by przesuwala daty o wlasciwe interwaly :/

bardziej obrazowo mowiac.
w agregacie tygodniowej: saldem 2 tygodni powinno byc saldo pierwszego, saldem trzeciego tgodnia saldo drugiego itd
w agregacie miesieczne: saldem stycznia bedzie saldo grudnia, saldem lutego bedzie saldo stycznia, saldem marca bedzie saldo lutego id
podobnie rzecz bedzie sie miala dla agregaty kwartalnej czy rocznej

wszystko analogicznie jak w przypadku agregaty dziennej o ktorej dotad dyskutowalismy.]]></description>
			<author><![CDATA[dummy@example.com (norris_85)]]></author>
			<pubDate>Wed, 24 Aug 2011 08:57:18 +0000</pubDate>
			<guid>https://forum.postgresql.org.pl/viewtopic.php?pid=2651#p2651</guid>
		</item>
		<item>
			<title><![CDATA[Odp: pobieranie danych dla jednego rekordu z innego wczesniejszego rekordu]]></title>
			<link>https://forum.postgresql.org.pl/viewtopic.php?pid=2628#p2628</link>
			<description><![CDATA[[quote=norris_85]gdyz z racji tego ze w sobote, i niedz nie ma sprzedazy sql zwraca w niektorych miejscach bledne dane. np nie zwrca wcale sprzedazy z 10,01 gdzie powinien zwrocic sprzedaz z 7.01, za to ta wlasnie sprzedaz zwraca 8.01 kiedy to zadnej sprzedazy byc nie ma prawa., podobnie dzieje sie z kolejnymi roznicami gdzie sprzedaz piatkowa powinna pojawic sie w poniedzialek a sql zwraca mi ja w sobote.[/quote]

Jeśli dobrze rozumiem, to można napisać prostą funkcję np. getNextBusinessDay, która zwraca następny dzień roboczy:

[code]CREATE OR REPLACE FUNCTION getNextBusinessDay(date)
RETURNS date AS $$
    SELECT
        CASE date_part('isodow', $1)
            WHEN 5 THEN CAST ($1 + interval '3 days' AS date) -- piątek
            WHEN 6 THEN CAST ($1 + interval '2 days' AS date) -- sobota
            ELSE CAST ($1 + interval '1 days' AS date) -- pozostałe
        END;
$$ LANGUAGE SQL;[/code]

Wcześniejsze zapytanie wyglądałoby nast.:

[code]SELECT
    sum(saldo) AS saldo,
    date_part('doy', next_day) AS day_of_year,
    date_part('week', next_day) AS week_of_year,
    date_part('month', next_day) AS month_of_year,
    date_part('quarter', next_day) AS quarter,
    date_part('year', next_day) AS year,    
    count(id) AS count
FROM 
    balance a INNER JOIN (SELECT id, getNextBusinessDay(valid_from_date) AS next_day FROM balance) b
    USING (id)
WHERE
    (next_day BETWEEN '2011-01-01' AND '2011-01-31') AND
    date_part('isodow', valid_from_date) NOT IN (6, 7) AND
    ghost = false AND
    product_3_id IN (59, 58, 60) AND
    product_group_id IN (3)
GROUP BY
    next_day
ORDER BY
    day_of_year;[/code]

Wynik:

[code] saldo | day_of_year | week_of_year | month_of_year | quarter | year | count 
-------+-------------+--------------+---------------+---------+------+-------
  2600 |           4 |            1 |             1 |       1 | 2011 |     3
  1000 |           7 |            1 |             1 |       1 | 2011 |     2
   400 |          10 |            2 |             1 |       1 | 2011 |     1
(3 rows)[/code]]]></description>
			<author><![CDATA[dummy@example.com (gszpetkowski)]]></author>
			<pubDate>Wed, 10 Aug 2011 13:14:26 +0000</pubDate>
			<guid>https://forum.postgresql.org.pl/viewtopic.php?pid=2628#p2628</guid>
		</item>
		<item>
			<title><![CDATA[Odp: pobieranie danych dla jednego rekordu z innego wczesniejszego rekordu]]></title>
			<link>https://forum.postgresql.org.pl/viewtopic.php?pid=2627#p2627</link>
			<description><![CDATA[jasne, zdaje sobie sprawe ze postgres udostepnia tego typu funkcje (osobiscie zamiast extract wole uzywc date_part, a dla wyznaczania dni tygodnia isodow ale to tylko jako oft), jednakze jest to tylko fragment znacznie wiekszego zapytania. dalej tabela date jest niestety potrzena gdyz rozszerza ona funkcjonalnosci tych wszystkich funkcji postgresowych dzieki indeksowaniu rekordow czy tez powiazaniu jej z tabela days_free gdzie sa umieszczone szystkie swieta wolne od pracy. pozatym projekt korzysta z roznych technologi a dzieki tej tabeli znacznie upraszcza sie prace na datach, no ale to mimo ze odp na twoje pytanie to rowniez oft :)

to co podales jako wynik zapytania zgadza sie gdy dla danych ktore podales jako wsadowe chcemy wyluskac wlasciwe im wyniki, a mi chodzi o to by zmodyfikowac tak tego sqla aby wartosc salda dla jednej daty pobieral z daty wczesniejszej (daty te beda sie roznic w zaleznosci na jaki okres - dzienny, tygodniowy, miesieczny, kwartalny czy roczny bedziemy chcieli pobierac dane stad tez potrzeba uzycia tabeli date dzieki ktorej bardzo latwo zagregowac te daty na odpowiedni okres).  

w uproszczeniu chce z wyniku ktory otrzymales w swoim przykladzie otrzymac cos takiego:

[quote]
 saldo | day_of_year | week_of_year | month_of_year | quarter | year | count 
----------+-------------+--------------+--------------+---------+--------+-------
   1000 |           2 |            1 |             1 |       1 | 2011 |     3
     800 |           3 |            1 |             1 |       1 | 2011 |     2
   1000 |           7 |            1 |             1 |       1 | 2011 |     1
(3 rows)
[/quote]]]></description>
			<author><![CDATA[dummy@example.com (norris_85)]]></author>
			<pubDate>Wed, 10 Aug 2011 12:33:38 +0000</pubDate>
			<guid>https://forum.postgresql.org.pl/viewtopic.php?pid=2627#p2627</guid>
		</item>
		<item>
			<title><![CDATA[Odp: pobieranie danych dla jednego rekordu z innego wczesniejszego rekordu]]></title>
			<link>https://forum.postgresql.org.pl/viewtopic.php?pid=2626#p2626</link>
			<description><![CDATA[Dobrze by było jednak trochę to uprościć. Ta tabela date jest na pewno potrzebna ? W [url=http://www.postgresql.org/docs/current/static/functions-datetime.html#FUNCTIONS-DATETIME-EXTRACT]dokumentacji[/url] Postgresa można znaleźć funkcję extract, która pozwala osiągnąć ten sam efekt i pierwsze zapytanie można zapisać jako:

[code]SELECT
    sum(saldo) AS saldo,
    extract(doy from valid_from_date) AS day_of_year,
    extract(week from valid_from_date) AS week_of_year,
    extract(month from valid_from_date) AS month_of_year,
    extract(quarter from valid_from_date) AS quarter,
    extract(year from valid_from_date) AS year,    
    count(id) AS count
FROM
    balance
WHERE
    (valid_from_date BETWEEN '2011-01-01' AND '2011-01-31') AND
    extract(dow from valid_from_date) NOT IN (0, 6) AND -- vide dokumentacja
    ghost = false AND
    product_3_id IN (59, 58, 60) AND
    product_group_id IN (3)
GROUP BY
    valid_from_date
ORDER BY
    day_of_year;[/code]

Uproszczony przykład:

[code]DROP TABLE IF EXISTS balance;
CREATE TABLE balance
(
    id serial PRIMARY KEY,
    ghost boolean,
    valid_from_date date,
    saldo numeric,
    product_group_id integer,
    product_3_id integer
);

INSERT INTO balance(ghost, valid_from_date, saldo, product_group_id, product_3_id) VALUES
    (false, '2011-01-01', 1000, 3, 59),
    (false, '2011-01-02', 800, 3, 60),
    (false, '2011-01-03', 500, 3, 58),
    (false, '2011-01-03', 2000, 3, 60),
    (false, '2011-01-06', 700, 3, 59),
    (false, '2011-01-06', 300, 3, 58),
    (false, '2011-01-07', 400, 3, 60),
    (false, '2011-01-03', 100, 3, 60);[/code]

Wynik:

[code] saldo | day_of_year | week_of_year | month_of_year | quarter | year | count 
-------+-------------+--------------+---------------+---------+------+-------
  2600 |           3 |            1 |             1 |       1 | 2011 |     3
  1000 |           6 |            1 |             1 |       1 | 2011 |     2
   400 |           7 |            1 |             1 |       1 | 2011 |     1
(3 rows)[/code]]]></description>
			<author><![CDATA[dummy@example.com (gszpetkowski)]]></author>
			<pubDate>Wed, 10 Aug 2011 12:08:30 +0000</pubDate>
			<guid>https://forum.postgresql.org.pl/viewtopic.php?pid=2626#p2626</guid>
		</item>
		<item>
			<title><![CDATA[pobieranie danych dla jednego rekordu z innego wczesniejszego rekordu]]></title>
			<link>https://forum.postgresql.org.pl/viewtopic.php?pid=2625#p2625</link>
			<description><![CDATA[problem przedstawia sie nastepujaco. 
mam dwie tabele: 
- jedna przechowujaca szczegolowe dane nt kolejnych dat 
- druga przchowujaca szczegolowe dane sprzedanego produktu w tym m.in. date sprzedazy tego produktu

napisalem taki sql aby pobrac dane sprzedazy zagregowane na konkretny dzien

[code]
SELECT sum(b.saldo) as saldo, day_of_year,week_of_year,month_of_year,quarter,year, count(b.id) as count, de.year as join_year, de.month_of_year as join_month_of_year
                            FROM data.balance as b
                            INNER JOIN date de ON b.valid_from_id = de.id AND de.day_of_week != 6 AND de.day_of_week != 7
                            where b.ghost = false    AND ( b.valid_from_id >= (SELECT public.get_date_data_id('2011-01-01')) AND b.valid_from_id <= (SELECT public.get_date_data_id('2011-01-31')) ) AND product_3_id IN ( 59,58,60 ) AND b.product_group_id IN ( 3 )   
                            GROUP BY day_of_year,week_of_year,month_of_year,quarter,year ,  de.month_of_year , de.year
                            order by day_of_year
[/code]

i dziala to prawidlowo.
teraz jednak potrzebuje pobrac ta sama sprzedaz tyle ze ma byc to wykonane tak by np sprzedaz z 3.01 byla przypisana do daty 4.01, sprzedaz z 4.01, przypisana do daty 5,01, sprzedaz z 5.01 przypisana do daty 6.01 itd... 
napisalem wiec sql ktory teoretycznie to wykonuje:

[code]
SELECT sum(b.saldo) as saldo_prev, day_of_year,week_of_year,month_of_year,quarter,year,
         count(b.id) as count_prev, de.year as join_year, de.month_of_year as join_month_of_year
        FROM data.balance as b
        INNER JOIN (SELECT day_of_year,week_of_year,month_of_year,quarter,year FROM date where date >= '2011-01-01' AND date <= '2011-01-31' group by day_of_year,week_of_year,month_of_year,quarter,year) as de ON  (SELECT day_of_year from date where id = b.valid_from_id) =  de.day_of_year - 1
        where b.ghost = false   AND ( b.valid_from_id >= (SELECT public.get_date_data_id((SELECT public.get_date_prev('2011-01-01', 'day', 'from'))::text)) AND b.valid_from_id <= (SELECT public.get_date_data_id((SELECT public.get_date_prev('2011-01-31', 'day','to'))::TEXT)) ) AND product_3_id IN ( 59,58,60 ) AND b.product_group_id IN ( 3 )
        GROUP BY day_of_year,week_of_year,month_of_year,quarter,year ,  de.month_of_year , de.year
        order by day_of_year
[/code]

teoretycznie, gdyz z racji tego ze w sobote, i niedz nie ma sprzedazy sql zwraca w niektorych miejscach bledne dane. np nie zwrca wcale sprzedazy z 10,01 gdzie powinien zwrocic sprzedaz z 7.01, za to ta wlasnie sprzedaz zwraca 8.01 kiedy to zadnej sprzedazy byc nie ma prawa., podobnie dzieje sie z kolejnymi roznicami gdzie sprzedaz piatkowa powinna pojawic sie w poniedzialek a sql zwraca mi ja w sobote.

znajdzie sie tutaj ktos na tyle madry aby mi pomoc rozgrysc ten problem?]]></description>
			<author><![CDATA[dummy@example.com (norris_85)]]></author>
			<pubDate>Wed, 10 Aug 2011 08:59:21 +0000</pubDate>
			<guid>https://forum.postgresql.org.pl/viewtopic.php?pid=2625#p2625</guid>
		</item>
	</channel>
</rss>
