<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="cs">
	<id>http://postgres.cz/index.php?action=history&amp;feed=atom&amp;title=Pou%C5%BEit%C3%AD_pomocn%C3%A9_tabulky_obsahuj%C3%ADc%C3%AD_kalend%C3%A1%C5%99</id>
	<title>Použití pomocné tabulky obsahující kalendář - Historie editací</title>
	<link rel="self" type="application/atom+xml" href="http://postgres.cz/index.php?action=history&amp;feed=atom&amp;title=Pou%C5%BEit%C3%AD_pomocn%C3%A9_tabulky_obsahuj%C3%ADc%C3%AD_kalend%C3%A1%C5%99"/>
	<link rel="alternate" type="text/html" href="http://postgres.cz/index.php?title=Pou%C5%BEit%C3%AD_pomocn%C3%A9_tabulky_obsahuj%C3%ADc%C3%AD_kalend%C3%A1%C5%99&amp;action=history"/>
	<updated>2026-05-30T20:27:42Z</updated>
	<subtitle>Historie editací této stránky</subtitle>
	<generator>MediaWiki 1.43.3</generator>
	<entry>
		<id>http://postgres.cz/index.php?title=Pou%C5%BEit%C3%AD_pomocn%C3%A9_tabulky_obsahuj%C3%ADc%C3%AD_kalend%C3%A1%C5%99&amp;diff=380&amp;oldid=prev</id>
		<title>imported&gt;Pavel v 30. 7. 2009, 19:41</title>
		<link rel="alternate" type="text/html" href="http://postgres.cz/index.php?title=Pou%C5%BEit%C3%AD_pomocn%C3%A9_tabulky_obsahuj%C3%ADc%C3%AD_kalend%C3%A1%C5%99&amp;diff=380&amp;oldid=prev"/>
		<updated>2009-07-30T19:41:27Z</updated>

		<summary type="html">&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Nová stránka&lt;/b&gt;&lt;/p&gt;&lt;div&gt;Pomocné tabulky se v SQL používají poměrně často. Umožňují doplnit určitá data, která potřebujeme pro výpočet. Například, pro určení průměrných denních výdajů potřebujeme doplnit základní data i o dny, kdy nedošlo k výdaji (jenom proto aby se zvýšil počet dní). Typicky pomocná tabulka bude obsahovat číselnou řadu nebo kalendář. V PostgreSQL máme k dispozici funkci generate_series, která vygeneruje řádky, takže pomocná tabulka může být dynamická. Relativně obtížně se určuje, zda-li se jedná o pracovní den, či nikoliv. K tomu použiji vlastní funkce:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE OR REPLACE FUNCTION velnedele(_rok INTEGER) RETURNS DATE AS $$&lt;br /&gt;
DECLARE B INTEGER; D INTEGER; E INTEGER; Q INTEGER;&lt;br /&gt;
  DD INTEGER; MM INTEGER;&lt;br /&gt;
BEGIN&lt;br /&gt;
    IF _rok &amp;lt; 1900 OR _rok &amp;gt; 2099 THEN&lt;br /&gt;
      RAISE EXCEPTION &amp;#039;Out of range&amp;#039;;&lt;br /&gt;
  END IF;&lt;br /&gt;
  B := 255 - 11 * ($1 % 19); D := ((B - 21) % 30) + 21;&lt;br /&gt;
  IF D &amp;gt; 38 THEN D := D - 1; END IF;&lt;br /&gt;
  E := ($1 + $1/4 + D + 1) % 7; Q := D + 7 - E;&lt;br /&gt;
  IF Q &amp;lt; 32 THEN DD:=Q; MM := 3; ELSE DD := Q - 31; MM := 4; END IF;&lt;br /&gt;
  RETURN to_date(to_char(DD, &amp;#039;00&amp;#039;) || &lt;br /&gt;
    to_char(MM, &amp;#039;00&amp;#039;) || to_char(_rok,&amp;#039;0000&amp;#039;), &amp;#039;DD MM YYYY&amp;#039;);&lt;br /&gt;
END;&lt;br /&gt;
$$ LANGUAGE plpgsql IMMUTABLE STRICT; &lt;br /&gt;
&lt;br /&gt;
-- vrati nazev nebo NULL, pokud den neni statnim svatkem&lt;br /&gt;
CREATE OR REPLACE FUNCTION StatniSvatek(d date)&lt;br /&gt;
RETURNS varchar AS $$&lt;br /&gt;
DECLARE &lt;br /&gt;
  vnedele date;&lt;br /&gt;
  result varchar;&lt;br /&gt;
BEGIN&lt;br /&gt;
  result := CASE to_char(d, &amp;#039;MMDD&amp;#039;)&lt;br /&gt;
                WHEN &amp;#039;0101&amp;#039; THEN &amp;#039;Nový rok&amp;#039;&lt;br /&gt;
                WHEN &amp;#039;0501&amp;#039; THEN &amp;#039;Svátek práce&amp;#039;&lt;br /&gt;
                WHEN &amp;#039;0508&amp;#039; THEN &amp;#039;Den osvobození&amp;#039;&lt;br /&gt;
                WHEN &amp;#039;0705&amp;#039; THEN &amp;#039;Den slovanských věrozvěstů Cyrila a Metoděje&amp;#039;&lt;br /&gt;
                WHEN &amp;#039;0706&amp;#039; THEN &amp;#039;Den upálení mistra Jana Husa&amp;#039;&lt;br /&gt;
                WHEN &amp;#039;0928&amp;#039; THEN &amp;#039;Den české státnosti&amp;#039;&lt;br /&gt;
                WHEN &amp;#039;1028&amp;#039; THEN &amp;#039;Den vzniku samostatného československého státu&amp;#039;&lt;br /&gt;
                WHEN &amp;#039;1117&amp;#039; THEN &amp;#039;Den boje za svobodu a demokracii&amp;#039;&lt;br /&gt;
                WHEN &amp;#039;1224&amp;#039; THEN &amp;#039;Štědrý den&amp;#039;&lt;br /&gt;
                WHEN &amp;#039;1225&amp;#039; THEN &amp;#039;1. svátek vánoční&amp;#039;&lt;br /&gt;
                WHEN &amp;#039;1226&amp;#039; THEN &amp;#039;2. svátek vánoční&amp;#039; END;&lt;br /&gt;
  IF result IS NULL THEN&lt;br /&gt;
    vnedele := velnedele(EXTRACT(&amp;#039;year&amp;#039; FROM d)::integer);&lt;br /&gt;
    result := CASE&lt;br /&gt;
                  WHEN d = vnedele     THEN &amp;#039;Neděle velikonoční&amp;#039;&lt;br /&gt;
                  WHEN d = vnedele + 1 THEN &amp;#039;Pondělí velikonoční&amp;#039; END;&lt;br /&gt;
  END IF;&lt;br /&gt;
  RETURN result;&lt;br /&gt;
END;&lt;br /&gt;
$$ LANGUAGE plpgsql IMMUTABLE STRICT;&lt;br /&gt;
&lt;br /&gt;
CREATE OR REPLACE FUNCTION DenDlouze(d date)&lt;br /&gt;
RETURNS varchar AS $$&lt;br /&gt;
SELECT CASE EXTRACT(&amp;#039;dow&amp;#039; FROM $1) &lt;br /&gt;
            WHEN 0 THEN &amp;#039;Neděle&amp;#039;&lt;br /&gt;
            WHEN 1 THEN &amp;#039;Pondělí&amp;#039;&lt;br /&gt;
            WHEN 2 THEN &amp;#039;Úterý&amp;#039;&lt;br /&gt;
            WHEN 3 THEN &amp;#039;Středa&amp;#039;&lt;br /&gt;
            WHEN 4 THEN &amp;#039;Čtvrtek&amp;#039;&lt;br /&gt;
            WHEN 5 THEN &amp;#039;Pátek&amp;#039;&lt;br /&gt;
            WHEN 6 THEN &amp;#039;Sobota&amp;#039; END;&lt;br /&gt;
$$ LANGUAGE sql IMMUTABLE STRICT;&lt;br /&gt;
&lt;br /&gt;
CREATE OR REPLACE FUNCTION DenKratce(d date)&lt;br /&gt;
RETURNS varchar AS $$&lt;br /&gt;
SELECT CASE EXTRACT(&amp;#039;dow&amp;#039; FROM $1) &lt;br /&gt;
            WHEN 0 THEN &amp;#039;Ne&amp;#039;&lt;br /&gt;
            WHEN 1 THEN &amp;#039;Po&amp;#039;&lt;br /&gt;
            WHEN 2 THEN &amp;#039;Út&amp;#039;&lt;br /&gt;
            WHEN 3 THEN &amp;#039;St&amp;#039;&lt;br /&gt;
            WHEN 4 THEN &amp;#039;Čt&amp;#039;&lt;br /&gt;
            WHEN 5 THEN &amp;#039;Pá&amp;#039;&lt;br /&gt;
            WHEN 6 THEN &amp;#039;So&amp;#039; END;&lt;br /&gt;
$$ LANGUAGE sql IMMUTABLE STRICT;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pomocna tabulka pro rok 2007 může mít následující obsah:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
postgres=# SELECT d, DenDlouze(d), DenKratce(d), &lt;br /&gt;
                  NOT(EXTRACT(&amp;#039;dow&amp;#039; FROM d) IN (0,6) OR StatniSvatek(d) IS NOT NULL) AS prac_den, &lt;br /&gt;
                  StatniSvatek(d) &lt;br /&gt;
              FROM (SELECT date &amp;#039;2007-01-01&amp;#039; + i AS d &lt;br /&gt;
                       FROM generate_series(355,364) g(i))s;&lt;br /&gt;
     d      | dendlouze | denkratce | prac_den |   statnisvatek    &lt;br /&gt;
------------+-----------+-----------+----------+-------------------&lt;br /&gt;
 2007-12-22 | Sobota    | So        | f        | &lt;br /&gt;
 2007-12-23 | Neděle    | Ne        | f        | &lt;br /&gt;
 2007-12-24 | Pondělí   | Po        | f        | Štědrý den&lt;br /&gt;
 2007-12-25 | Úterý     | Út        | f        | 1. svátek vánoční&lt;br /&gt;
 2007-12-26 | Středa    | St        | f        | 2. svátek vánoční&lt;br /&gt;
 2007-12-27 | Čtvrtek   | Čt        | t        | &lt;br /&gt;
 2007-12-28 | Pátek     | Pa        | t        | &lt;br /&gt;
 2007-12-29 | Sobota    | So        | f        | &lt;br /&gt;
 2007-12-30 | Neděle    | Ne        | f        | &lt;br /&gt;
 2007-12-31 | Pondělí   | Po        | t        | &lt;br /&gt;
(10 rows)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Tuto tabulku si uložím do skutečné tabulky kalendar.&lt;br /&gt;
&lt;br /&gt;
Jak s touto tabulkou pracovat?&lt;br /&gt;
* Počet pracovních dní (od, do)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
postgres=# SELECT count(*) &lt;br /&gt;
              FROM kalendar&lt;br /&gt;
             WHERE d BETWEEN &amp;#039;2007-10-01&amp;#039; AND &amp;#039;2007-11-01&amp;#039; AND prac_den;&lt;br /&gt;
 count &lt;br /&gt;
-------&lt;br /&gt;
    24&lt;br /&gt;
(1 row)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* Odečet n pracovních dnů od určitého dne. Např. 10 pracovních dnů od 20.12.2007 (pozn. ukázkový příklad síly neprocedurálního SQL, s použitím procedur a cyklu lze tuto úlohu řešit řádově rychleji, po delší době úloha, kde by bylo možné úspěšně použít kurzory):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
postgres=# SELECT * &lt;br /&gt;
              FROM kalendar k &lt;br /&gt;
             WHERE k.prac_den AND 10 = (SELECT count(*) &lt;br /&gt;
                                           FROM kalendar k2 &lt;br /&gt;
                                          WHERE k2.d BETWEEN k.d AND date &amp;#039;2007-12-20&amp;#039; - 1 &lt;br /&gt;
                                                AND k2.prac_den);&lt;br /&gt;
     d      | dendlouze | denkratce | prac_den | statnisvatek &lt;br /&gt;
------------+-----------+-----------+----------+--------------&lt;br /&gt;
 2007-12-06 | Čtvrtek   | Čt        | t        | &lt;br /&gt;
(1 row)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* všechny svátky během pracovního týdne:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
postgres=# SELECT * &lt;br /&gt;
              FROM kalendar &lt;br /&gt;
             WHERE statnisvatek IS NOT NULL AND denkratce NOT IN (&amp;#039;So&amp;#039;,&amp;#039;Ne&amp;#039;);&lt;br /&gt;
     d      | dendlouze | denkratce | prac_den |                 statnisvatek                 &lt;br /&gt;
------------+-----------+-----------+----------+----------------------------------------------&lt;br /&gt;
 2007-01-01 | Pondělí   | Po        | f        | Nový rok&lt;br /&gt;
 2007-04-09 | Pondělí   | Po        | f        | Pondělí velikonoční&lt;br /&gt;
 2007-05-01 | Úterý     | Út        | f        | Svátek práce&lt;br /&gt;
 2007-05-08 | Úterý     | Út        | f        | Den osvobození&lt;br /&gt;
 2007-07-05 | Čtvrtek   | Čt        | f        | Den slovanských věrozvěstů Cyrila a Metoděje&lt;br /&gt;
 2007-07-06 | Pátek     | Pa        | f        | Den upálení mistra Jana Husa&lt;br /&gt;
 2007-09-28 | Pátek     | Pa        | f        | Den české státnosti&lt;br /&gt;
 2007-12-24 | Pondělí   | Po        | f        | Štědrý den&lt;br /&gt;
 2007-12-25 | Úterý     | Út        | f        | 1. svátek vánoční&lt;br /&gt;
 2007-12-26 | Středa    | St        | f        | 2. svátek vánoční&lt;br /&gt;
(10 rows)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Inspirováno článkem http://sqlserver2000.databases.aspfaq.com/why-should-i-consider-using-an-auxiliary-calendar-table.html.&lt;br /&gt;
&lt;br /&gt;
Joe Celko v článku http://www.simple-talk.com/content/article.aspx?article=632 ještě doporučuje přidat sloupec &amp;#039;&amp;#039;julian_business_nbr&amp;#039;&amp;#039; tj číslo pracovního dne. V případě svátků se toto číslo nemění. Pro výpočet tohoto čísla lze použít uloženou proceduru nebo window funkci sum - kumulativní:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE OR REPLACE FUNCTION prac_den(date)&lt;br /&gt;
RETURNS boolean AS $$&lt;br /&gt;
  SELECT NOT(EXTRACT(&amp;#039;dow&amp;#039; FROM $1) IN (0,6) OR StatniSvatek($1) IS NOT NULL);&lt;br /&gt;
$$ LANGUAGE sql IMMUTABLE STRICT;&lt;br /&gt;
&lt;br /&gt;
postgres=# SELECT d, DenDlouze(d), DenKratce(d), &lt;br /&gt;
                  prac_den(d), &lt;br /&gt;
                  SUM(CASE WHEN prac_den(d) THEN 1 ELSE 0 END) OVER (ORDER BY d) AS cislo_prac_dne,  &lt;br /&gt;
                  StatniSvatek(d) &lt;br /&gt;
              FROM (SELECT date &amp;#039;2007-01-01&amp;#039; + i AS d &lt;br /&gt;
                       FROM generate_series(0,366) g(i))s  LIMIT 10;&lt;br /&gt;
     d      | dendlouze | denkratce | prac_den | cislo_prac_dne |   statnisvatek    &lt;br /&gt;
------------+-----------+-----------+----------+----------------+-------------------&lt;br /&gt;
 2007-12-22 | Sobota    | So        | f        |            248 | ----&lt;br /&gt;
 2007-12-23 | Neděle    | Ne        | f        |            248 | ----&lt;br /&gt;
 2007-12-24 | Pondělí   | Po        | f        |            248 | Štědrý den&lt;br /&gt;
 2007-12-25 | Úterý     | Út        | f        |            248 | 1. svátek vánoční&lt;br /&gt;
 2007-12-26 | Středa    | St        | f        |            248 | 2. svátek vánoční&lt;br /&gt;
 2007-12-27 | Čtvrtek   | Čt        | t        |            249 | ----&lt;br /&gt;
 2007-12-28 | Pátek     | Pá        | t        |            250 | ----&lt;br /&gt;
 2007-12-29 | Sobota    | So        | f        |            250 | ----&lt;br /&gt;
 2007-12-30 | Neděle    | Ne        | f        |            250 | ----&lt;br /&gt;
 2007-12-31 | Pondělí   | Po        | t        |            251 | ----&lt;br /&gt;
(10 rows)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Díky sloupci &amp;#039;&amp;#039;cislo_prac_dne&amp;#039;&amp;#039; snadno můžeme provádět operace nad pracovním kalendářem:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
postgres=# SELECT * &lt;br /&gt;
              FROM kalendar &lt;br /&gt;
             WHERE cislo_prac_dne = (SELECT cislo_prac_dne &lt;br /&gt;
                                        FROM kalendar&lt;br /&gt;
                                       WHERE d = &amp;#039;2009-07-30&amp;#039;) + 0 &lt;br /&gt;
               AND prac_den;&lt;br /&gt;
     d      | dendlouze | denkratce | prac_den | cislo_prac_dne | statnisvatek &lt;br /&gt;
------------+-----------+-----------+----------+----------------+--------------&lt;br /&gt;
 2009-07-30 | Čtvrtek   | Čt        | t        |            650 | ----&lt;br /&gt;
(1 row)&lt;br /&gt;
&lt;br /&gt;
postgres=# SELECT * &lt;br /&gt;
              FROM kalendar&lt;br /&gt;
             WHERE cislo_prac_dne = (SELECT cislo_prac_dne &lt;br /&gt;
                                        FROM kalendar&lt;br /&gt;
                                       WHERE d = &amp;#039;2009-07-30&amp;#039;) + 3&lt;br /&gt;
               AND prac_den;&lt;br /&gt;
     d      | dendlouze | denkratce | prac_den | cislo_prac_dne | statnisvatek &lt;br /&gt;
------------+-----------+-----------+----------+----------------+--------------&lt;br /&gt;
 2009-08-04 | Úterý     | Út        | t        |            653 | ----&lt;br /&gt;
(1 row)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>imported&gt;Pavel</name></author>
	</entry>
</feed>