<?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=Automatick%C3%A1_kontrola_rodn%C3%A9ho_%C4%8D%C3%ADsla</id>
	<title>Automatická kontrola rodného čísla - Historie editací</title>
	<link rel="self" type="application/atom+xml" href="http://postgres.cz/index.php?action=history&amp;feed=atom&amp;title=Automatick%C3%A1_kontrola_rodn%C3%A9ho_%C4%8D%C3%ADsla"/>
	<link rel="alternate" type="text/html" href="http://postgres.cz/index.php?title=Automatick%C3%A1_kontrola_rodn%C3%A9ho_%C4%8D%C3%ADsla&amp;action=history"/>
	<updated>2026-05-30T19:35:18Z</updated>
	<subtitle>Historie editací této stránky</subtitle>
	<generator>MediaWiki 1.43.3</generator>
	<entry>
		<id>http://postgres.cz/index.php?title=Automatick%C3%A1_kontrola_rodn%C3%A9ho_%C4%8D%C3%ADsla&amp;diff=360&amp;oldid=prev</id>
		<title>194.255.108.253: doplneni vyjimky</title>
		<link rel="alternate" type="text/html" href="http://postgres.cz/index.php?title=Automatick%C3%A1_kontrola_rodn%C3%A9ho_%C4%8D%C3%ADsla&amp;diff=360&amp;oldid=prev"/>
		<updated>2007-12-09T17:41:51Z</updated>

		<summary type="html">&lt;p&gt;doplneni vyjimky&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Nová stránka&lt;/b&gt;&lt;/p&gt;&lt;div&gt;Profesionální programování je více-méně o tvorbě knihoven. Programátor, ktrerý&lt;br /&gt;
staví stále na zelené louce nebude těžko bude produktivní. Klasické jazyky podporují&lt;br /&gt;
vytváření knihoven a modulů. K dispozici je dostatek kvalitních knihoven, kterými&lt;br /&gt;
se můžeme inspirovat při tvorbě vlastních knihoven.&lt;br /&gt;
&lt;br /&gt;
To bohužel neplatí u programování v prostředí uložených procedur. Zatím se nijak zvlášť &lt;br /&gt;
oproti ostatním neprosadil žádný koncept. Jednou z možností, jak organizovat vlastní&lt;br /&gt;
knihovny je vytváření vlastních &amp;quot;inteligentních&amp;quot; datových typů. Jelikož PostgreSQL &lt;br /&gt;
vzniklo v akademickém prostředí, které je co se týče potřeby ukládání specializovaných&lt;br /&gt;
dat podstatně pestřejší a náročnější než ekonomicko-finanční sféra (na kterou bylo &lt;br /&gt;
původně SQL cílené), PostgreSQL od samotného svého počátku disponovalo možností vytváření&lt;br /&gt;
vlastních datových formátů. V klasickém SQL je příliš úzký rejstřík datových typů, v&lt;br /&gt;
čehož důsledku musíme používat obecné typy TEXT nebo BLOB. Díky jejich obecnosti dokážeme&lt;br /&gt;
do nich uložit libovolnou textovou nebo binární hodnotu, ale nic víc. Tyto typy neposkytují&lt;br /&gt;
žádné další (specializované) operace.&lt;br /&gt;
&lt;br /&gt;
Vlastní datový typ v PostgreSQL může být implementován pouze v jazycích C nebo Java.&lt;br /&gt;
Kromě skutečných datových typů ještě existují složené datové typy (obdoba typu RECORD v&lt;br /&gt;
Pascalu). Kromě vlastních binárních datových typů můžeme definovat vlastní doménové&lt;br /&gt;
typy. Následující příklad obsahuje podporu rodného čísla právě pomocí domén.&lt;br /&gt;
&lt;br /&gt;
Rodné číslo je hodnota s celkem dost vysokou redundanci, zvlášť když máme k dispozici&lt;br /&gt;
datum narozeni a pohlaví osoby. Ve formuláři nedokáže zabránit záměrně falšované hodnotě,&lt;br /&gt;
ale s celkem s vysokou pravděpodobností dokaze zachytit překlepy. Vzhledem&lt;br /&gt;
k tomu, že obsahuje citlivé udaje (věk, pohlaví) předpokládá se, že bude v dohledné době&lt;br /&gt;
nahrazen neutrálním jedinečným identifikátorem osob (uvažuje se o číslu sociálního&lt;br /&gt;
zabezpečení).&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE OR REPLACE FUNCTION check_form_rodne_cislo(varchar, boolean)&lt;br /&gt;
RETURNS boolean AS $$&lt;br /&gt;
DECLARE &lt;br /&gt;
  str_parts varchar[];&lt;br /&gt;
  num_parts integer[];&lt;br /&gt;
  after_1953 boolean;&lt;br /&gt;
  birthday_str varchar;&lt;br /&gt;
BEGIN&lt;br /&gt;
  SELECT INTO str_parts regexp_matches &lt;br /&gt;
     FROM regexp_matches($1, E&amp;#039;^(\\d{2})(\\d{2})(\\d{2})(/)?(\\d{3,4})$&amp;#039;);&lt;br /&gt;
  IF FOUND THEN &lt;br /&gt;
    -- Test existence lomitka v rodnem cisle rizeny druhym parametrem,&lt;br /&gt;
    -- ve vyznamu NULL (nema vyznam), true - povinne, false - nepovinne.&lt;br /&gt;
    IF $2 &amp;lt;&amp;gt; (str_parts[4] IS NOT NULL) THEN&lt;br /&gt;
      RETURN false;&lt;br /&gt;
    END IF;&lt;br /&gt;
    -- Test modulo 11 se provadi pouze tehdy, pokud existuje&lt;br /&gt;
    -- kontrolni cislice, tj. pocinaje rokem 1954.&lt;br /&gt;
    after_1953 := char_length(str_parts[5]) = 4;&lt;br /&gt;
    IF after_1953&lt;br /&gt;
                   AND to_number($1, CASE WHEN str_parts[4] IS NULL&lt;br /&gt;
                                          THEN &amp;#039;9999999999&amp;#039;&lt;br /&gt;
                                          ELSE &amp;#039;999999/9999&amp;#039; END) % 11 &amp;lt;&amp;gt; 0 THEN&lt;br /&gt;
      -- Nicmene z tohoto pravidla existuje vyjimka cca 1000                                                                                                           &lt;br /&gt;
      -- rodnych cisel, kdy modulo 9 mistneho cisla je rovno 10                                                                                                        &lt;br /&gt;
      -- a kontrolni cislice je 0                                                                                                                                      &lt;br /&gt;
      IF NOT (to_number($1, CASE WHEN str_parts[4] IS NULL&lt;br /&gt;
                                          THEN &amp;#039;999999999&amp;#039;&lt;br /&gt;
                                          ELSE &amp;#039;999999/999&amp;#039; END) % 11 = 10&lt;br /&gt;
          AND to_number($1, CASE WHEN str_parts[4] IS NULL&lt;br /&gt;
                                          THEN &amp;#039;?????????9&amp;#039;&lt;br /&gt;
                                          ELSE &amp;#039;??????/???9&amp;#039; END) = 0) THEN&lt;br /&gt;
            RETURN false;&lt;br /&gt;
      END IF;&lt;br /&gt;
    END IF;&lt;br /&gt;
    -- Kontrola validniho datumu, po roce 2004 se mohou&lt;br /&gt;
    -- k mesici pricitat hodnoty 20 (pro muze) a 70 pro zeny.&lt;br /&gt;
    num_parts := ARRAY[to_number(str_parts[1],&amp;#039;99&amp;#039;),&lt;br /&gt;
                       to_number(str_parts[2],&amp;#039;99&amp;#039;),&lt;br /&gt;
                       to_number(str_parts[3],&amp;#039;99&amp;#039;)];&lt;br /&gt;
    num_parts := ARRAY[CASE WHEN NOT after_1953 &lt;br /&gt;
                            THEN 1900 + num_parts[1]&lt;br /&gt;
                            WHEN after_1953 AND num_parts[1] &amp;gt;= 54 &lt;br /&gt;
                            THEN 1900 + num_parts[1]&lt;br /&gt;
                            ELSE 2000 + num_parts[1] END,&lt;br /&gt;
                       CASE WHEN num_parts[2] &amp;gt; 70&lt;br /&gt;
                            THEN num_parts[2] - 70&lt;br /&gt;
                            WHEN num_parts[2] &amp;gt; 50&lt;br /&gt;
                            THEN num_parts[2] - 50&lt;br /&gt;
                            WHEN num_parts[2] &amp;gt; 20&lt;br /&gt;
                            THEN num_parts[2] - 20&lt;br /&gt;
                            ELSE num_parts[2] END,&lt;br /&gt;
                       num_parts[3]];&lt;br /&gt;
    birthday_str := replace(to_char(num_parts[1],&amp;#039;9999&amp;#039;)&lt;br /&gt;
                            || to_char(num_parts[2],&amp;#039;09&amp;#039;) &lt;br /&gt;
                            || to_char(num_parts[3],&amp;#039;09&amp;#039;),&lt;br /&gt;
                               &amp;#039; &amp;#039;,&amp;#039;&amp;#039;);&lt;br /&gt;
    RETURN birthday_str = to_char(to_date(birthday_str, &amp;#039;YYYYMMDD&amp;#039;), &lt;br /&gt;
                                  &amp;#039;YYYYMMDD&amp;#039;);&lt;br /&gt;
  END IF;&lt;br /&gt;
  RETURN false;&lt;br /&gt;
END;&lt;br /&gt;
$$ LANGUAGE plpgsql;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
V případě binárního vlastního typu potřebujeme ještě konverzní funkce z&lt;br /&gt;
typu rc, rc1 a rc2 do typů date a boolean, které použijeme v definici&lt;br /&gt;
přetypování (CREATE CAST). Nic takového pro domény neexistuje. Nicméně mohu&lt;br /&gt;
napsat funkce to_date a to_boolean a ty používat. Aby nedocházelo k opakování&lt;br /&gt;
kódu, existuje implicitní přetypování rc1-&amp;gt;rc a rc2-&amp;gt;rc, což je korektní&lt;br /&gt;
(rc je obecnější než rc1 nebo rc2). Funkce to_bool a to_date musím volat&lt;br /&gt;
explicitně.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE DOMAIN rc  VARCHAR CHECK (check_form_rodne_cislo(value, null::boolean));&lt;br /&gt;
&lt;br /&gt;
-- Predpokladam validni vstup (overeny funkci check_form_rodne_cislo).&lt;br /&gt;
-- Typ parametru varchar je zvolen proto, protoze je predkem domen rc,&lt;br /&gt;
-- rc1 a rc2. Domeny se lisi vztahem vuci lomitku uvnitr rodneho cisla.&lt;br /&gt;
CREATE OR REPLACE FUNCTION to_date(rc)&lt;br /&gt;
RETURNS date AS $$&lt;br /&gt;
DECLARE &lt;br /&gt;
  str_parts varchar[];&lt;br /&gt;
  num_parts integer[];&lt;br /&gt;
  after_1953 boolean;&lt;br /&gt;
  birthday_str varchar;&lt;br /&gt;
BEGIN&lt;br /&gt;
  SELECT INTO str_parts regexp_matches &lt;br /&gt;
     FROM regexp_matches($1, E&amp;#039;^(\\d{2})(\\d{2})(\\d{2})(/)?(\\d{3,4})$&amp;#039;);&lt;br /&gt;
  IF FOUND THEN &lt;br /&gt;
    after_1953 := char_length(str_parts[5]) = 4;&lt;br /&gt;
    -- Po roce 2004 se mohou k mesici pricitat &lt;br /&gt;
    -- hodnoty 20 (pro muze) a 70 pro zeny. Pred timto rokem&lt;br /&gt;
    -- se vzdy pricita 50 pro zeny&lt;br /&gt;
    num_parts := ARRAY[to_number(str_parts[1],&amp;#039;99&amp;#039;),&lt;br /&gt;
                       to_number(str_parts[2],&amp;#039;99&amp;#039;),&lt;br /&gt;
                       to_number(str_parts[3],&amp;#039;99&amp;#039;)];&lt;br /&gt;
    num_parts := ARRAY[CASE WHEN NOT after_1953 &lt;br /&gt;
                            THEN 1900 + num_parts[1]&lt;br /&gt;
                            WHEN after_1953 AND num_parts[1] &amp;gt;= 54 &lt;br /&gt;
                            THEN 1900 + num_parts[1]&lt;br /&gt;
                            ELSE 2000 + num_parts[1] END,&lt;br /&gt;
                       CASE WHEN num_parts[2] &amp;gt; 70&lt;br /&gt;
                            THEN num_parts[2] - 70&lt;br /&gt;
                            WHEN num_parts[2] &amp;gt; 50&lt;br /&gt;
                            THEN num_parts[2] - 50&lt;br /&gt;
                            WHEN num_parts[2] &amp;gt; 20&lt;br /&gt;
                            THEN num_parts[2] - 20&lt;br /&gt;
                            ELSE num_parts[2] END,&lt;br /&gt;
                       num_parts[3]];&lt;br /&gt;
    RETURN to_date(&lt;br /&gt;
                   replace(to_char(num_parts[1],&amp;#039;9999&amp;#039;)&lt;br /&gt;
                            || to_char(num_parts[2],&amp;#039;09&amp;#039;) &lt;br /&gt;
                            || to_char(num_parts[3],&amp;#039;09&amp;#039;),&lt;br /&gt;
                               &amp;#039; &amp;#039;,&amp;#039;&amp;#039;),&lt;br /&gt;
                   &amp;#039;YYYYMMDD&amp;#039;);&lt;br /&gt;
  END IF;&lt;br /&gt;
  RAISE NOTICE &amp;#039;Incorrect format of type rodne cislo (value: %)&amp;#039;, $1;&lt;br /&gt;
END;&lt;br /&gt;
$$ LANGUAGE plpgsql;&lt;br /&gt;
&lt;br /&gt;
-- Predpokladam validni vstup (overeny funkci check_form_rodne_cislo).&lt;br /&gt;
-- Typ parametru varchar je zvolen proto, protoze je predkem domen rc,&lt;br /&gt;
-- rc1 a rc2. Domeny se lisi vztahem vuci lomitku uvnitr rodneho cisla.&lt;br /&gt;
CREATE OR REPLACE FUNCTION to_bool(rc)&lt;br /&gt;
RETURNS boolean AS $$&lt;br /&gt;
DECLARE &lt;br /&gt;
  str_part varchar[];&lt;br /&gt;
  birthday_mm integer;&lt;br /&gt;
BEGIN&lt;br /&gt;
  SELECT INTO str_part regexp_matches &lt;br /&gt;
     FROM regexp_matches($1, E&amp;#039;^\\d{2}(\\d{2})\\d{2}/?\\d{3,4}$&amp;#039;);&lt;br /&gt;
  IF FOUND THEN &lt;br /&gt;
    birthday_mm := to_number(str_part[1],&amp;#039;09&amp;#039;);&lt;br /&gt;
    RETURN CASE WHEN birthday_mm &amp;gt; 50 &lt;br /&gt;
                THEN false&lt;br /&gt;
                ELSE true END;&lt;br /&gt;
  END IF;&lt;br /&gt;
  RAISE NOTICE &amp;#039;Incorrect format of type rodne cislo (value: %)&amp;#039;, $1;&lt;br /&gt;
END;&lt;br /&gt;
$$ LANGUAGE plpgsql;&lt;br /&gt;
&lt;br /&gt;
-- Registrace domen &lt;br /&gt;
CREATE DOMAIN rc1 VARCHAR CHECK (check_form_rodne_cislo(value, false));&lt;br /&gt;
CREATE DOMAIN rc2 VARCHAR CHECK (check_form_rodne_cislo(value, true));&lt;br /&gt;
&lt;br /&gt;
-- vytvoreni konverznich pravidel, rc je zevseobecnenim rc1 a rc2&lt;br /&gt;
CREATE CAST (rc1  AS rc) WITHOUT FUNCTION AS IMPLICIT;&lt;br /&gt;
CREATE CAST (rc2  AS rc) WITHOUT FUNCTION AS IMPLICIT;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Použití&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE TABLE foo (r rc1);&lt;br /&gt;
INSERT INTO foo(&amp;#039;7307--/----&amp;#039;);&lt;br /&gt;
-- ziskani narozeni z udaju v db&lt;br /&gt;
SELECT to_date(r)&lt;br /&gt;
   FROM foo;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>194.255.108.253</name></author>
	</entry>
</feed>