<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:georss="http://www.georss.org/georss" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:media="http://search.yahoo.com/mrss/"
	>

<channel>
	<title>Coffee Free Code</title>
	<atom:link href="http://coffeefreecode.wordpress.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://coffeefreecode.wordpress.com</link>
	<description>programming :: python :: database</description>
	<lastBuildDate>Sat, 07 Jan 2012 23:04:35 +0000</lastBuildDate>
	<language>pl</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.com/</generator>
<cloud domain='coffeefreecode.wordpress.com' port='80' path='/?rsscloud=notify' registerProcedure='' protocol='http-post' />
<image>
		<url>http://s2.wp.com/i/buttonw-com.png</url>
		<title>Coffee Free Code</title>
		<link>http://coffeefreecode.wordpress.com</link>
	</image>
	<atom:link rel="search" type="application/opensearchdescription+xml" href="http://coffeefreecode.wordpress.com/osd.xml" title="Coffee Free Code" />
	<atom:link rel='hub' href='http://coffeefreecode.wordpress.com/?pushpress=hub'/>
		<item>
		<title>Linksys WAG120N &#8211; zmiana adresu IP z poziomu skryptu</title>
		<link>http://coffeefreecode.wordpress.com/2011/03/18/linksys-wag120n-zmiana-adresu-ip-z-poziomu-skryptu/</link>
		<comments>http://coffeefreecode.wordpress.com/2011/03/18/linksys-wag120n-zmiana-adresu-ip-z-poziomu-skryptu/#comments</comments>
		<pubDate>Fri, 18 Mar 2011 14:56:55 +0000</pubDate>
		<dc:creator>Krzysztof Kosyl</dc:creator>
				<category><![CDATA[Notki]]></category>
		<category><![CDATA[ip]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[neostrada]]></category>
		<category><![CDATA[network]]></category>
		<category><![CDATA[router]]></category>
		<category><![CDATA[script]]></category>
		<category><![CDATA[shell]]></category>

		<guid isPermaLink="false">http://coffeefreecode.wordpress.com/?p=109</guid>
		<description><![CDATA[Korzystając z Internetu czasem warto zmienić adres IP pod jakim jesteśmy widziani w sieci. Umożliwi to przykładowo pobranie następnego pliku bez oczekiwania. Oczywiście nasz ISP musi udostępniać taką możliwość (tak jest, dla przykładu z Neostradą). W ruterze Linksys WAG120N można to zrobić za pomocą panelu kontrolnego dostępnego poprzez protokół HTTP. Czyli trzeba: wejść w przeglądarce [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=coffeefreecode.wordpress.com&amp;blog=3346273&amp;post=109&amp;subd=coffeefreecode&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Korzystając z Internetu czasem warto zmienić adres IP pod jakim jesteśmy widziani w sieci. Umożliwi to przykładowo pobranie następnego pliku bez oczekiwania. Oczywiście nasz ISP musi udostępniać taką możliwość (tak jest, dla przykładu z Neostradą).</p>
<p>W ruterze <b>Linksys WAG120N</b> można to zrobić za pomocą panelu kontrolnego dostępnego poprzez protokół HTTP. Czyli trzeba:</p>
<ol>
<li>wejść w przeglądarce pod właściwy adres</li>
<li>wpisać nazwę użytkownika i hasło (lub zaakceptować zapamiętane przez przeglądarkę)</li>
<li>przejść do <i>Status</i> (co)</li>
<li>kliknąć <i>Disconnect</i></li>
<li>poczekać chwilkę</li>
<li>kliknąć <i>Connect</i></li>
<li>poczekać chwilkę</li>
</ol>
<p>Jak widać jest to sporo pracy. Szczęśliwie można to zautomatyzować za pomocą skryptu. Wystarczy skorzystać z <a href="http://pl.wikipedia.org/wiki/Sniffer">sniffera</a> (ja użyłem <a href="http://www.wireshark.org/">Wireshark</a>).</p>
<p>W wyniku tego powstał poniższy skrypt powłoki (testowany w Bashu i Zsh). Mam nadzieję, że się komuś przyda. Wystarczy dokleić poniższy kod do pliku konfiguracyjnego powłoki (odpowiednio <tt>.bashrc</tt> lub <tt>.zshrc</tt>). No i należy pamiętać, że działa dużo lepiej jeśli odpowiednio ustawi się zmienne <tt>USER</tt> i <tt>PASS</tt>.</p>
<p><pre class="brush: bash;">
my-ip() {
  wget 'http://checkip.dyndns.org/' --quiet -O- | sed -e \
    's/[^0-9]*\([0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+\).*/\1/g'
}

change-ip() {
  router-op() {
    USER=&quot;user&quot;
    PASS=&quot;password&quot;
    ADDR=&quot;http://192.168.1.1/setup.cgi&quot;
    wget --http-user=$USER --http-password=$PASS $ADDR \
      --post-data $1 --quiet -O/dev/null 
  }
  REST='&amp;this_file=Status.htm&amp;next_file=Status.htm&amp;message='
  echo Old IP: `my-ip`
  router-op 'ctype=pppoa&amp;ifstatus=Up&amp;todo=disconnect'$REST
  sleep 4
  router-op 'ctype=pppoa&amp;ifstatus=Up&amp;todo=connect'$REST
  sleep 8
  echo New IP: `my-ip`
}

</pre></p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/coffeefreecode.wordpress.com/109/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/coffeefreecode.wordpress.com/109/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/coffeefreecode.wordpress.com/109/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/coffeefreecode.wordpress.com/109/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/coffeefreecode.wordpress.com/109/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/coffeefreecode.wordpress.com/109/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/coffeefreecode.wordpress.com/109/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/coffeefreecode.wordpress.com/109/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/coffeefreecode.wordpress.com/109/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/coffeefreecode.wordpress.com/109/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/coffeefreecode.wordpress.com/109/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/coffeefreecode.wordpress.com/109/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/coffeefreecode.wordpress.com/109/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/coffeefreecode.wordpress.com/109/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=coffeefreecode.wordpress.com&amp;blog=3346273&amp;post=109&amp;subd=coffeefreecode&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://coffeefreecode.wordpress.com/2011/03/18/linksys-wag120n-zmiana-adresu-ip-z-poziomu-skryptu/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/eb87bb07d5570b63195d2183d30579e6?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">kosqx</media:title>
		</media:content>
	</item>
		<item>
		<title>Zużycie pamięci w Pythonie</title>
		<link>http://coffeefreecode.wordpress.com/2011/02/28/zuzycie-pamieci-w-pythonie/</link>
		<comments>http://coffeefreecode.wordpress.com/2011/02/28/zuzycie-pamieci-w-pythonie/#comments</comments>
		<pubDate>Mon, 28 Feb 2011 21:18:09 +0000</pubDate>
		<dc:creator>Krzysztof Kosyl</dc:creator>
				<category><![CDATA[Artykuły]]></category>

		<guid isPermaLink="false">http://coffeefreecode.wordpress.com/?p=71</guid>
		<description><![CDATA[Poniższe dane zostały zbadane za pomocą prostego skryptu dostępnego na GitHubie Typ danych 32 bit 64 bit Python 2.6.4 Python 3.1.1+ Python 2.6.5 Python 3.1.2 empty 1.57 2.62 2.22 3.88 none 4.15 4.23 8.38 8.52 one 4.15 4.23 8.38 8.52 tuple 69.16 69.64 132.87 131.68 named 77.39 78.02 141.76 140.73 list 85.12 85.64 164.86 163.61 [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=coffeefreecode.wordpress.com&amp;blog=3346273&amp;post=71&amp;subd=coffeefreecode&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Poniższe dane zostały zbadane za pomocą prostego skryptu dostępnego na <a href="https://github.com/kosqx/blog-code/tree/master/2011-02-python-memory-usage">GitHubie</a></p>
<table>
<thead>
<tr>
<td rowspan="2">Typ danych</td>
<td colspan="2">32 bit</td>
<td colspan="2">64 bit</td>
</tr>
<tr>
<td>Python 2.6.4</td>
<td>Python 3.1.1+</td>
<td>Python 2.6.5</td>
<td>Python 3.1.2</td>
</tr>
</thead>
<tbody>
<tr>
<td>empty</td>
<td>1.57</td>
<td>2.62</td>
<td>2.22</td>
<td>3.88</td>
</tr>
<tr>
<td>none</td>
<td>4.15</td>
<td>4.23</td>
<td>8.38</td>
<td>8.52</td>
</tr>
<tr>
<td>one</td>
<td>4.15</td>
<td>4.23</td>
<td>8.38</td>
<td>8.52</td>
</tr>
</tbody>
<tbody>
<tr>
<td>tuple</td>
<td>69.16</td>
<td>69.64</td>
<td>132.87</td>
<td>131.68</td>
</tr>
<tr>
<td>named</td>
<td>77.39</td>
<td>78.02</td>
<td>141.76</td>
<td>140.73</td>
</tr>
<tr>
<td>list</td>
<td>85.12</td>
<td>85.64</td>
<td>164.86</td>
<td>163.61</td>
</tr>
<tr>
<td>dict</td>
<td>179.63</td>
<td>182.25</td>
<td>345.49</td>
<td>345.34</td>
</tr>
<tr>
<td>flat</td>
<td>40.41</td>
<td>40.71</td>
<td>65.98</td>
<td>65.89</td>
</tr>
<tr>
<td>array</td>
<td>16.43</td>
<td>16.23</td>
<td>16.23</td>
<td>16.39</td>
</tr>
<tr>
<td>object</td>
<td>212.39</td>
<td>215.39</td>
<td>412.52</td>
<td>411.39</td>
</tr>
<tr>
<td>slots</td>
<td>69.15</td>
<td>69.64</td>
<td>124.49</td>
<td>123.42</td>
</tr>
</tbody>
</table>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/coffeefreecode.wordpress.com/71/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/coffeefreecode.wordpress.com/71/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/coffeefreecode.wordpress.com/71/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/coffeefreecode.wordpress.com/71/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/coffeefreecode.wordpress.com/71/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/coffeefreecode.wordpress.com/71/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/coffeefreecode.wordpress.com/71/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/coffeefreecode.wordpress.com/71/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/coffeefreecode.wordpress.com/71/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/coffeefreecode.wordpress.com/71/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/coffeefreecode.wordpress.com/71/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/coffeefreecode.wordpress.com/71/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/coffeefreecode.wordpress.com/71/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/coffeefreecode.wordpress.com/71/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=coffeefreecode.wordpress.com&amp;blog=3346273&amp;post=71&amp;subd=coffeefreecode&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://coffeefreecode.wordpress.com/2011/02/28/zuzycie-pamieci-w-pythonie/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/eb87bb07d5570b63195d2183d30579e6?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">kosqx</media:title>
		</media:content>
	</item>
		<item>
		<title>Optymalizacja zapytań w PostgreSQL</title>
		<link>http://coffeefreecode.wordpress.com/2011/01/31/optymalizacja-zapytan-w-postgresql/</link>
		<comments>http://coffeefreecode.wordpress.com/2011/01/31/optymalizacja-zapytan-w-postgresql/#comments</comments>
		<pubDate>Mon, 31 Jan 2011 21:53:51 +0000</pubDate>
		<dc:creator>Krzysztof Kosyl</dc:creator>
				<category><![CDATA[Artykuły]]></category>
		<category><![CDATA[explain]]></category>
		<category><![CDATA[postgresql]]></category>

		<guid isPermaLink="false">http://coffeefreecode.wordpress.com/?p=14</guid>
		<description><![CDATA[Uwagi techniczne Załączony program Do tego dokumentu dołączony jest program użyty do uzyskania opisanych tutaj danych. Został on napisany w Pythonie. Wymagania: Python 2.5 PostgreSQL 8.0 lub nowsze psycopg2 (często paczkowany jako python-psycopg2) Program czyta zapytania z pliku pg.test. Konfiguracja połączenia do bazy podana jest w zmiennej DSN na początku pliku programu (czyli db.py). Użycie: [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=coffeefreecode.wordpress.com&amp;blog=3346273&amp;post=14&amp;subd=coffeefreecode&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<h2>Uwagi techniczne</h2>
<h3>Załączony program</h3>
<p>Do tego dokumentu dołączony jest program użyty do uzyskania opisanych tutaj danych. Został on napisany w Pythonie. Wymagania:</p>
<ul>
<li>Python 2.5</li>
<li>PostgreSQL 8.0 lub nowsze</li>
<li>psycopg2 (często paczkowany jako python-psycopg2)</li>
</ul>
<p>Program czyta zapytania z pliku pg.test. Konfiguracja połączenia do bazy podana jest w zmiennej DSN na początku pliku programu (czyli db.py).</p>
<p>Użycie:</p>
<dl class="command">
<dt>python db_test.py load [rozmiar]</dt>
<dd>tworzy strukturę bazy i wstawia do niej dane, jeśli rozmiar zostanie podany wtedy zostanie stworzone tyle rekordów ile podano</dd>
<dt>python db_test.py explain [test1] [test2] &#8230; [testN]</dt>
<dd>wyświetla plany zapytań, jeśli nie zostały podane jawnie to wykonuje wszystkie z pliku  pg.test</dd>
<dt>python db_test.py speed [test1] [test2] &#8230; [testN]</dt>
<dd>testuje czas wykonania zapytań zapytań, jeśli nie zostały podane jawnie to wykonuje wszystkie z pliku  pg.test</dd>
</dl>
<p>W poniższych wynikach komend pozwalam sobie na pominięcie niektórych linii.</p>
<h3>Struktura bazy danych</h3>
<p>Struktura bazy jest tworzona za pomocą poleceń:</p>
<pre>
CREATE TABLE item_0(id serial PRIMARY KEY, pay float, section char(1), name varchar(50));
...
CREATE TABLE item_4(id serial PRIMARY KEY, pay float, section char(1), name varchar(50))

CREATE INDEX idx_item_1_pay     ON item_1 USING btree (pay);
CREATE INDEX idx_item_1_section ON item_1 USING btree (section);
CREATE INDEX idx_item_1_name    ON item_1             (name)")

CREATE INDEX idx_item_2_pay     ON item_2 USING hash (pay);
CREATE INDEX idx_item_2_section ON item_2 USING hash (section);
CREATE INDEX idx_item_2_name    ON item_2            (name varchar_pattern_ops);

CREATE INDEX idx_item_3_pay0    ON item_3            (pay) WHERE pay BETWEEN 0000 AND  1000;
...
CREATE INDEX idx_item_3_pay10   ON item_3            (pay) WHERE pay BETWEEN 9000 AND 10000;
CREATE INDEX idx_item_3_name    ON item_3            (substring(name, 0, 1));

CREATE INDEX idx_item_4_multi   ON item_4            (section, pay);
</pre>
<p class="new">Do tabel są wstawiane losowo wygenerowane rekordy, tak aby każda z nich miała identyczną zawartość. Czyli ostatecznie tabele różnią się tylko indeksami.</p>
<p>Wszystkie tu podane testy zostały wykonane na bazie zawierającej po 1 milion rekordów w jednej tabeli.</p>
<h3>Warunki testu</h3>
<p class="new">Test był prowadzony na Intel Core2 Duo T7300 @ 2.00GHz; 3GB pamięci operacyjnej.</p>
<p class="new">Bazy bardzo intensywnie korzystaja z cachowania wyników w pamięci. Aby dało się porównać wyniki to podane tu listingi pochodzą z drugiego uruchomienia danego zapytania.</p>
<p class="new">Aby móc zobaczyć różnicę to przykład z 1 uruchomienia:</p>
<pre>
<span>python pg.py explain index_equal</span>
<div class="new">  SELECT pay FROM item_0 WHERE pay = 9500
      Seq Scan on item_0  (cost=0.00..20609.56 rows=101 width=8) (actual time=1.683..253.070 rows=127 loops=1)
        Filter: (pay = 9500::double precision)
      Total runtime: 253.288 ms
  real time <strong>433.31 ms</strong>

  SELECT pay FROM item_1 WHERE pay = 9500
      Bitmap Heap Scan on item_1  (cost=5.24..418.64 rows=113 width=8) (actual time=0.199..0.777 rows=127 loops=1)
        Recheck Cond: (pay = 9500::double precision)
        -&gt;  Bitmap Index Scan on idx_item_1_pay  (cost=0.00..5.21 rows=113 width=0) (actual time=0.133..0.133 rows=127 loops=1)
              Index Cond: (pay = 9500::double precision)
      Total runtime: 1.051 ms
  real time <strong>823.78 ms</strong>

  SELECT pay FROM item_2 WHERE pay = 9500
      Bitmap Heap Scan on item_2  (cost=5.33..418.74 rows=113 width=8) (actual time=0.362..0.930 rows=127 loops=1)
        Recheck Cond: (pay = 9500::double precision)
        -&gt;  Bitmap Index Scan on idx_item_2_pay  (cost=0.00..5.30 rows=113 width=0) (actual time=0.285..0.285 rows=127 loops=1)
              Index Cond: (pay = 9500::double precision)
      Total runtime: 1.207 ms
  real time <strong>879.79 ms</strong>

  SELECT pay FROM item_3 WHERE pay = 9500
      Bitmap Heap Scan on item_3  (cost=5.09..397.57 rows=107 width=8) (actual time=0.184..0.787 rows=127 loops=1)
        Recheck Cond: (pay = 9500::double precision)
        -&gt;  Bitmap Index Scan on idx_item_3_pay9  (cost=0.00..5.06 rows=107 width=0) (actual time=0.119..0.119 rows=127 loops=1)
              Index Cond: (pay = 9500::double precision)
      Total runtime: 1.079 ms
  real time <strong>827.77 ms</strong>

  SELECT pay FROM item_4 WHERE pay = 9500
      Seq Scan on item_4  (cost=0.00..20615.00 rows=113 width=8) (actual time=5.528..337.332 rows=127 loops=1)
        Filter: (pay = 9500::double precision)
      Total runtime: 337.577 ms
  real time <strong>1898.83 ms</strong>
</div>
</pre>
<p class="new">A tu z 2 uruchomienia:</p>
<pre>
<span>python pg.py explain index_equal</span>
<div class="new">  SELECT pay FROM item_0 WHERE pay = 9500
      Seq Scan on item_0  (cost=0.00..20609.56 rows=101 width=8) (actual time=1.616..259.265 rows=127 loops=1)
        Filter: (pay = 9500::double precision)
      Total runtime: 259.491 ms
  real time <strong>336.76 ms</strong>

  SELECT pay FROM item_1 WHERE pay = 9500
      Bitmap Heap Scan on item_1  (cost=5.24..418.64 rows=113 width=8) (actual time=0.066..0.332 rows=127 loops=1)
        Recheck Cond: (pay = 9500::double precision)
        -&gt;  Bitmap Index Scan on idx_item_1_pay  (cost=0.00..5.21 rows=113 width=0) (actual time=0.042..0.042 rows=127 loops=1)
              Index Cond: (pay = 9500::double precision)
      Total runtime: 0.503 ms
  real time <strong>2.18 ms</strong>

  SELECT pay FROM item_2 WHERE pay = 9500
      Bitmap Heap Scan on item_2  (cost=5.33..418.74 rows=113 width=8) (actual time=0.124..0.402 rows=127 loops=1)
        Recheck Cond: (pay = 9500::double precision)
        -&gt;  Bitmap Index Scan on idx_item_2_pay  (cost=0.00..5.30 rows=113 width=0) (actual time=0.100..0.100 rows=127 loops=1)
              Index Cond: (pay = 9500::double precision)
      Total runtime: 0.583 ms
  real time <strong>1.96 ms</strong>

  SELECT pay FROM item_3 WHERE pay = 9500
      Bitmap Heap Scan on item_3  (cost=5.09..397.57 rows=107 width=8) (actual time=0.062..0.331 rows=127 loops=1)
        Recheck Cond: (pay = 9500::double precision)
        -&gt;  Bitmap Index Scan on idx_item_3_pay9  (cost=0.00..5.06 rows=107 width=0) (actual time=0.038..0.038 rows=127 loops=1)
              Index Cond: (pay = 9500::double precision)
      Total runtime: 0.498 ms
  real time <strong>2.65 ms</strong>

  SELECT pay FROM item_4 WHERE pay = 9500
      Seq Scan on item_4  (cost=0.00..20615.00 rows=113 width=8) (actual time=1.682..269.960 rows=127 loops=1)
        Filter: (pay = 9500::double precision)
      Total runtime: 270.174 ms
  real time <strong>269.69 ms</strong>
</div>
</pre>
<p class="new">Jak widać różnica jest olbrzymia, ale tylko w &#8222;real time&#8221;. Wyniki samego &#8222;EXPLAIN&#8221; się nie różnią.</p>
<h3>Format listingu</h3>
<pre>
<span>python pg.py explain index_equal</span>
  SELECT pay FROM item_0 WHERE pay = 9500
      <tt>Seq Scan on item_0  (cost=0.00..20609.56 rows=101 width=8)</tt> <tt>(actual time=1.616..259.265 rows=127 loops=1)</tt>
        <tt>Filter: (pay = 9500::double precision)</tt>
      <tt>Total runtime: 259.491 ms</tt>
  <tt>real time 336.76 ms</tt>

  <tt>result size 54611 records</tt>
</pre>
<p class="new">Poszczególne elementy wyniku oznaczono kolorami:</p>
<dl>
<dt><tt>EXPLAIN</tt></dt>
<dd>
    Wypisuje kolejne kroki jakie będą wykonane podczas wykonywania zapytania. Podaje <b>oszacowane</b> statystyki.
  </dd>
<dt><tt>EXPLAIN ANALYZE</tt></dt>
<dd>
    Użycie EXPLAIN ANALYZE w miejsce EXPLAIN spowoduje <b>dokładniejsze</b> oszacowanie statystyk zapytania. Poda również oszacowany czas w milisekundach.
  </dd>
<dt><tt>pg.py</tt></dt>
<dd>
    Sprawdza ile czasu na prawdę wymaga zapytanie (łącznie z pobraniem danych z bazy), sprawdza ile wyników uzyskano oraz czy wyniki w grupie zapytań są identyczne
  </dd>
</dl>
<p class="new">W EXPLAIN bardzo ważne jest, że ono tylko szacuje dane i wyniki typu <i>rows=101</i> należy traktować jak sugestię rzędu wyniku a nie wiążące dane.</p>
<h2>Testy</h2>
<h3>Indeksy</h3>
<h4>Zapytania równościowe</h4>
<pre>
<span>python pg.py explain index_equal</span>
  SELECT pay FROM item_0 WHERE pay = 9500
      Seq Scan on item_0  (cost=0.00..20609.56 rows=101 width=8) (actual time=1.560..255.903 rows=127 loops=1)
        Filter: (pay = 9500::double precision)
      Total runtime: 256.110 ms
  real time <strong>261.04 ms</strong>

  SELECT pay FROM item_1 WHERE pay = 9500
      Bitmap Heap Scan on item_1  (cost=5.24..418.64 rows=113 width=8) (actual time=0.070..0.346 rows=127 loops=1)
        Recheck Cond: (pay = 9500::double precision)
        -&gt;  Bitmap Index Scan on idx_item_1_pay  (cost=0.00..5.21 rows=113 width=0) (actual time=0.044..0.044 rows=127 loops=1)
              Index Cond: (pay = 9500::double precision)
      Total runtime: 0.518 ms
  real time <strong>1.98 ms</strong>

  SELECT pay FROM item_2 WHERE pay = 9500
      Bitmap Heap Scan on item_2  (cost=5.33..418.74 rows=113 width=8) (actual time=0.130..0.416 rows=127 loops=1)
        Recheck Cond: (pay = 9500::double precision)
        -&gt;  Bitmap Index Scan on idx_item_2_pay  (cost=0.00..5.30 rows=113 width=0) (actual time=0.104..0.104 rows=127 loops=1)
              Index Cond: (pay = 9500::double precision)
      Total runtime: 0.590 ms
  real time <strong>3.32 ms</strong>

  SELECT pay FROM item_3 WHERE pay = 9500
      Bitmap Heap Scan on item_3  (cost=5.09..397.57 rows=107 width=8) (actual time=0.083..0.379 rows=127 loops=1)
        Recheck Cond: (pay = 9500::double precision)
        -&gt;  Bitmap Index Scan on idx_item_3_pay9  (cost=0.00..5.06 rows=107 width=0) (actual time=0.055..0.055 rows=127 loops=1)
              Index Cond: (pay = 9500::double precision)
      Total runtime: 0.545 ms
  real time <strong>3.44 ms</strong>

  SELECT pay FROM item_4 WHERE pay = 9500
      Seq Scan on item_4  (cost=0.00..20615.00 rows=113 width=8) (actual time=1.416..260.032 rows=127 loops=1)
        Filter: (pay = 9500::double precision)
      Total runtime: 260.255 ms
  real time <strong>300.01 ms</strong>
<div class="new"> SELECT pay FROM item_4 WHERE section in ('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k') AND pay = 9500
      Bitmap Heap Scan on item_4  (cost=47.68..320.83 rows=73 width=8) (actual time=0.209..0.507 rows=127 loops=1)
        Recheck Cond: ((section = ANY ('{a,b,c,d,e,f,g,h,i,j,k}'::bpchar[])) AND (pay = 9500::double precision))
        -&gt;  Bitmap Index Scan on idx_item_4_multi  (cost=0.00..47.66 rows=73 width=0) (actual time=0.184..0.184 rows=127 loops=1)
              Index Cond: ((section = ANY ('{a,b,c,d,e,f,g,h,i,j,k}'::bpchar[])) AND (pay = 9500::double precision))
      Total runtime: 0.675 ms
  real time <strong>2.26 ms</strong>
</div>

  result size 127 records
</pre>
<p>Jak widać przy zapytaniach równościowych index Hash (item_2) sprawuje się niemal tak dobrze jak B-Tree (item_1).</p>
<p>Również zgodna z oczekiwaniami jest powolność korzystania z danych bez zastosowania indeksu. W takiej sytuacji baza musi wykonać <strong>Seq Scan</strong> co niestety musi być  czasochłonne. W tym przypadku zastosowanie indeksu przyśpieszyło zapytanie ponad 100 krotnie.</p>
<p>Zastosowanie indeksów częściowych (item_3) w tym przypadku okazało się być wolniejsze niż skorzystanie z pojedynczego indeksu. Indeksy częściowe są czasem wymieniane jako mechanizm mający być odpowiednikiem partycjonowania danych w Oracle.</p>
<p>PostgreSQL nie potrafi skorzystać z indeksów na wielu kolumnach w sposób odbiegający od podstawowego. Obawia się to Seq Scan (item_4).</p>
<p class="new">Można jednak zmodyfikować zapytanie tak aby optymalizmowi wydawało się, że musi skorzystać z kolumny <tt>section</tt>. Wystarczy dodać warunek który jest zawsze prawdziwy, co w tym przypadku oznacza podanie wszystkich możliwych wartości <tt>section</tt>. Takie zapytanie jest prawie tak samo dobre jak zwykłe z użyciem B-Tree.</p>
<p class="new"> W zapytaniu 3 (item_2) EXPLAIN sugeruje, że zapytanie to wykona się wolniej od zapytania 4 (item_3). W praktyce jest odwrotnie, choć różnice nie są znaczące ani w ilości milisekund różnicy ani proporcji pomiędzy nimi. Rożnicę tą można wyjaśnić niechęcią PostgreSQL do korzystania z indeksów typu Hash. Najprawdopodobniej baza danych nie lubi również cachować tego indeksu. W praktyce objawia się to tym, że jeśli na danej kolumnie jest index zarówno B-Tree jak i Hash to postgres w zapytaniu równościowym skorzysta z B-Tree.</p>
<h4>Zapytania nierównościowe</h4>
<pre>
<span>python pg.py explain index_greater</span>
  SELECT pay FROM item_0 WHERE pay &gt; 9500
      Seq Scan on item_0  (cost=0.00..20609.56 rows=52586 width=8) (actual time=0.024..346.241 rows=54611 loops=1)
        Filter: (pay &gt; 9500::double precision)
      Total runtime: 419.762 ms
  real time <strong>481.87 ms</strong>

  SELECT pay FROM item_1 WHERE pay &gt; 9500
      Bitmap Heap Scan on item_1  (cost=1056.13..9873.98 rows=56228 width=8) (actual time=15.688..180.376 rows=54611 loops=1)
        Recheck Cond: (pay &gt; 9500::double precision)
        -&gt;  Bitmap Index Scan on idx_item_1_pay  (cost=0.00..1042.07 rows=56228 width=0) (actual time=13.559..13.559 rows=54611 loops=1)
              Index Cond: (pay &gt; 9500::double precision)
      Total runtime: 255.899 ms
  real time <strong>319.65 ms</strong>

  SELECT pay FROM item_2 WHERE pay &gt; 9500
      Seq Scan on item_2  (cost=0.00..20615.00 rows=59879 width=8) (actual time=0.025..333.938 rows=54611 loops=1)
        Filter: (pay &gt; 9500::double precision)
      Total runtime: 403.646 ms
  real time <strong>488.11 ms</strong>

  SELECT pay FROM item_3 WHERE pay &gt; 9500
      Seq Scan on item_3  (cost=0.00..20615.00 rows=48570 width=8) (actual time=0.030..333.590 rows=54611 loops=1)
        Filter: (pay &gt; 9500::double precision)
      Total runtime: 402.078 ms
  real time <strong>470.62 ms</strong>

  SELECT pay FROM item_4 WHERE pay &gt; 9500
      Seq Scan on item_4  (cost=0.00..20615.00 rows=53272 width=8) (actual time=0.024..347.594 rows=54611 loops=1)
        Filter: (pay &gt; 9500::double precision)
      Total runtime: 417.228 ms
  real time <strong>509.56 ms</strong>

  result size 54611 records
</pre>
<p>Zgodnie z przewidywaniami tylko zapytanie takie zapytanie potrafi skorzystać tylko z indeksu B-Tree (item_1). To co dziwne &#8211; nie przyśpieszyło to znacząco zapytania. Możliwe, że wynika to z braku fastrygi w indeksie.</p>
<p class="new">
<h3>Funkcje agregujące</h3>
<h4>Wartość minimalna</h4>
<pre>
<span>python pg.py explain minimum</span>
TEST minimum
  SELECT min(pay) FROM item_0
      Aggregate  (cost=20609.57..20609.58 rows=1 width=8) (actual time=2993.836..2993.838 rows=1 loops=1)
        -&gt;  Seq Scan on item_0  (cost=0.00..18110.65 rows=999565 width=8) (actual time=0.018..1434.374 rows=1000000 loops=1)
      Total runtime: 2993.877 ms
  real time <strong>424.77 ms</strong>

  SELECT pay FROM item_0 ORDER BY pay LIMIT 1
      Limit  (cost=23108.48..23108.48 rows=1 width=8) (actual time=3095.285..3095.287 rows=1 loops=1)
        -&gt;  Sort  (cost=23108.48..25607.39 rows=999565 width=8) (actual time=3095.281..3095.281 rows=1 loops=1)
              Sort Key: pay
              Sort Method:  top-N heapsort  Memory: 17kB
              -&gt;  Seq Scan on item_0  (cost=0.00..18110.65 rows=999565 width=8) (actual time=0.018..1555.827 rows=1000000 loops=1)
      Total runtime: 3095.317 ms
  real time <strong>515.28 ms</strong>

  SELECT min(pay) FROM item_1
      Result  (cost=0.06..0.07 rows=1 width=0) (actual time=0.024..0.026 rows=1 loops=1)
        InitPlan
          -&gt;  Limit  (cost=0.00..0.06 rows=1 width=8) (actual time=0.018..0.019 rows=1 loops=1)
                -&gt;  Index Scan using idx_item_1_pay on item_1  (cost=0.00..58431.91 rows=1000000 width=8) (actual time=0.015..0.015 rows=1 loops=1)
                      Filter: (pay IS NOT NULL)
      Total runtime: 0.049 ms
  real time <strong>0.48 ms</strong>

  SELECT pay FROM item_1 ORDER BY pay LIMIT 1
      Limit  (cost=0.00..0.06 rows=1 width=8) (actual time=0.018..0.019 rows=1 loops=1)
        -&gt;  Index Scan using idx_item_1_pay on item_1  (cost=0.00..58431.91 rows=1000000 width=8) (actual time=0.015..0.015 rows=1 loops=1)
      Total runtime: 0.038 ms
  real time <strong>0.34 ms</strong>

  result size 1 records
</pre>
<p>Nie jest żadnym zaskoczeniem, że w zapytaniu o wartość minimalną kluczową rolę gra indeks. Zapytania korzystające z niego korzystające jest 6000 razy szybsze. </p>
<p>Warto tu zwrócić uwagę, że PostgreSQL w drugim zapytaniu użył metody sortowania <strong>top-N heapsort</strong>, która świetnie pracuje w zapytaniach <tt>ORDER BY ... LIMIT ...</tt>.</p>
<h4>Policzenie wszystkich elementów</h4>
<pre>
<span>python pg.py explain count</span>
  SELECT count(*) FROM item_0
      Aggregate  (cost=20609.57..20609.58 rows=1 width=0) (actual time=2766.064..2766.066 rows=1 loops=1)
        -&gt;  Seq Scan on item_0  (cost=0.00..18110.65 rows=999565 width=0) (actual time=0.026..1425.761 rows=1000000 loops=1)
      Total runtime: 2766.111 ms
  real time <strong>251.70 ms</strong>

  SELECT count(*) FROM item_1
      Aggregate  (cost=20615.00..20615.01 rows=1 width=0) (actual time=2769.023..2769.025 rows=1 loops=1)
        -&gt;  Seq Scan on item_1  (cost=0.00..18115.00 rows=1000000 width=0) (actual time=0.111..1400.902 rows=1000000 loops=1)
      Total runtime: 2769.070 ms
  real time <strong>218.34 ms</strong>

  SELECT count(pay) FROM item_0
      Aggregate  (cost=20609.57..20609.58 rows=1 width=8) (actual time=2882.692..2882.693 rows=1 loops=1)
        -&gt;  Seq Scan on item_0  (cost=0.00..18110.65 rows=999565 width=8) (actual time=0.025..1447.978 rows=1000000 loops=1)
      Total runtime: 2882.732 ms
  real time <strong>295.68 ms</strong>

  SELECT count(pay) FROM item_1
      Aggregate  (cost=20615.00..20615.01 rows=1 width=8) (actual time=3183.981..3183.983 rows=1 loops=1)
        -&gt;  Seq Scan on item_1  (cost=0.00..18115.00 rows=1000000 width=8) (actual time=0.026..1592.733 rows=1000000 loops=1)
      Total runtime: 3184.022 ms
  real time <strong>294.60 ms</strong>

  result size 1 records
</pre>
<p>Niestety PostgreSQL nie potrafi sobie dobrze poradzić z ustaleniem ilości elementów w tabeli. Ta operacja mogła by być wykonywana w czasie stałym a tu jej wykonanie wymaga <tt>Seq Scan</tt>. Warto zwrócić uwagę, że baza przechowuje oszacowania tych danych (<tt>rows=999565</tt> oraz <tt>rows=1000000</tt>).</p>
<h3>Zapytania o unikalne elementy</h3>
<h4>Wszystkie unikalne elementy</h4>
<pre>
<span>python pg.py explain distinct</span>
  SELECT DISTINCT pay FROM item_1
      Unique  (cost=0.00..60931.91 rows=8778 width=8) (actual time=0.061..9573.897 rows=9201 loops=1)
        -&gt;  Index Scan using idx_item_1_pay on item_1  (cost=0.00..58431.91 rows=1000000 width=8) (actual time=0.058..8092.649 rows=1000000 loops=1)
      Total runtime: 9588.750 ms
  real time <strong>7175.85 ms</strong>

  SELECT pay FROM item_1 GROUP BY pay
      HashAggregate  (cost=20615.00..20702.78 rows=8778 width=8) (actual time=3150.550..3163.936 rows=9201 loops=1)
        -&gt;  Seq Scan on item_1  (cost=0.00..18115.00 rows=1000000 width=8) (actual time=0.037..1447.830 rows=1000000 loops=1)
      Total runtime: 3175.569 ms
  real time <strong>592.40 ms</strong>

  result size 9201 records
</pre>
<p>Pierwsze rozwiązanie cechuje się prostotą, czytelnością oraz tym, że łatwo je wymyślić. Niestety pomimo zastosowania indeksu nie jest wstanie wygrać z haszowaniem. Okazuje się być 12 krotnie wolniejsze.</p>
<h4>Wybrane unikalne elementy</h4>
<pre>
<span>python pg.py explain distinct_where</span>
  SELECT DISTINCT pay FROM item_1 WHERE pay   Index Scan using idx_item_1_pay on item_1  (cost=0.00..39314.56 rows=240959 width=8) (actual time=0.071..1965.921 rows=239234 loops=1)
              Index Cond: (pay &lt; 3000::double precision)
      Total runtime: 2323.769 ms
  real time <strong>1741.87 ms</strong>

  SELECT pay FROM item_1 WHERE pay   Bitmap Heap Scan on item_1  (cost=4515.79..15642.78 rows=240959 width=8) (actual time=58.506..467.832 rows=239234 loops=1)
              Recheck Cond: (pay   Bitmap Index Scan on idx_item_1_pay  (cost=0.00..4455.55 rows=240959 width=0) (actual time=56.219..56.219 rows=239234 loops=1)
                    Index Cond: (pay &lt; 3000::double precision)
      Total runtime: 880.148 ms
  real time <strong>277.76 ms</strong>

  SELECT pay FROM item_1 GROUP BY pay HAVING pay   Bitmap Heap Scan on item_1  (cost=4515.79..15642.78 rows=240959 width=8) (actual time=58.250..467.493 rows=239234 loops=1)
              Recheck Cond: (pay   Bitmap Index Scan on idx_item_1_pay  (cost=0.00..4455.55 rows=240959 width=0) (actual time=55.926..55.926 rows=239234 loops=1)
                    Index Cond: (pay &lt; 3000::double precision)
      Total runtime: 888.473 ms
  real time <strong>283.04 ms</strong>

  result size 2200 records
</pre>
<p>Jest to lekko zmodyfikowany poprzedni przypadek, różniący się tylko warunkiem. W tym przypadku grupowanie nie ma już tak dużej przewagi bo tylko(?) 6 krotną.</p>
<p>Warto zwrócić uwagę, że w tym przypadku często powtarzana zasada &#8222;stosuj WHERE tam gdzie się da a HAVING dopiero jeśli musisz&#8221; tu się nie sprawdza. Po prostu PostgreSQL przepisał te zapytania na takie same plany.</p>
<h4>Zliczanie unikalnych elementów</h4>
<pre>
<span>python pg.py explain count_distinct</span>
TEST count_distinct
  SELECT count(DISTINCT pay) AS a FROM item_1
      Aggregate  (cost=20615.00..20615.01 rows=1 width=8) (actual time=5789.449..5789.451 rows=1 loops=1)
        -&gt;  Seq Scan on item_1  (cost=0.00..18115.00 rows=1000000 width=8) (actual time=0.022..1433.576 rows=1000000 loops=1)
      Total runtime: 5789.501 ms
  real time <strong>3190.76 ms</strong>

  SELECT count(*) from (SELECT 1 FROM item_1 GROUP BY pay) as job;
      Aggregate  (cost=20812.51..20812.52 rows=1 width=0) (actual time=3190.341..3190.342 rows=1 loops=1)
        -&gt;  HashAggregate  (cost=20615.00..20702.78 rows=8778 width=8) (actual time=3164.037..3178.169 rows=9201 loops=1)
              -&gt;  Seq Scan on item_1  (cost=0.00..18115.00 rows=1000000 width=8) (actual time=0.025..1420.910 rows=1000000 loops=1)
      Total runtime: 3190.464 ms
  real time <strong>590.71 ms</strong>

  result size 1 records
</pre>
<p>Również w tym przypadku <tt>GROUP BY</tt> okazuje się być o wiele szybsze od <tt>DISTINCT</tt>.</p>
<h3>Zapytania o prefiksy napisów</h3>
<p>W bazach danych często zachodzi potrzeba zapytania o napisy zawierające dany podciąg. O ile wyszukiwanie pełnotekstowe jest zagadnieniem, któremu warto poświecić książkę to zapytanie o napisy zaczynajace się na dany prefix są relatywnie prostym ale równie przydatnym zagadnieniem.</p>
<h4>Bez przygotowanej do tego bazy danych</h4>
<p>W tych zapytaniach zostanie użyta tabela  prostym indeksem B-Tree założonym na jej kolumnę &#8222;name&#8221;.</p>
<pre>
<span>python pg.py explain string_start</span>
  SELECT pay FROM item_1 WHERE name LIKE 'a%'
      Seq Scan on item_1  (cost=0.00..20615.00 rows=62340 width=8) (actual time=0.058..347.848 rows=18502 loops=1)
        Filter: ((name)::text ~~ 'a%'::text)
      Total runtime: 371.993 ms
  real time <strong>414.74 ms</strong>

  SELECT pay FROM item_1 WHERE name ~ '^a.*'
      Seq Scan on item_1  (cost=0.00..20615.00 rows=62340 width=8) (actual time=0.161..1011.445 rows=18502 loops=1)
        Filter: ((name)::text ~ '^a.*'::text)
      Total runtime: 1035.623 ms
  real time <strong>1090.11 ms</strong>

  SELECT pay FROM item_1 WHERE substr(name, 0, 1) = 'a'
      Seq Scan on item_1  (cost=0.00..23115.00 rows=5000 width=8) (actual time=682.164..682.164 rows=0 loops=1)
        Filter: (substr((name)::text, 0, 1) = 'a'::text)
      Total runtime: 682.194 ms
  real time <strong>694.45 ms</strong>

  SELECT pay FROM item_1 WHERE 'a' &lt;= name AND name &lt;= 'b'
      Bitmap Heap Scan on item_1  (cost=1879.43..10929.53 rows=62340 width=8) (actual time=32.185..165.301 rows=37046 loops=1)
        Recheck Cond: (('a'::text &lt;= (name)::text) AND ((name)::text &lt;= 'b'::text))
        -&gt;  Bitmap Index Scan on idx_item_1_name  (cost=0.00..1863.85 rows=62340 width=0) (actual time=29.822..29.822 rows=37046 loops=1)
              Index Cond: (('a'::text &lt;= (name)::text) AND ((name)::text &lt;= 'b'::text))
      Total runtime: 212.977 ms
  real time <strong>257.41 ms</strong>

  result size 18502 records
</pre>
<p>Jak widać najszybszym rozwiązaniem jest rozwiązanie korzystające z operatorów mniejszości. Wynika to głównie z tego, że jako jedyne zostało przepisane tak aby skorzystać z indeksu.</p>
<p>Operator LIKE okazuje się być całkiem wydajny. Jest trzykrotnie szybszy od wyrażeń regularnych oraz dwukrotnie szybszy od zapytania porównującego podnapis.</p>
<pre>
<span>python pg.py explain string_start_opt</span>
  SELECT pay FROM item_2 WHERE name LIKE 'a%'
      Index Scan using idx_item_2_name on item_2  (cost=0.00..8.47 rows=47825 width=8) (actual time=0.067..163.999 rows=18502 loops=1)
        Index Cond: (((name)::text ~&gt;=~ 'a'::text) AND ((name)::text ~&lt;~ 'b'::text))
        Filter: ((name)::text ~~ 'a%'::text)
      Total runtime: 188.630 ms
  real time <strong>237.19 ms</strong>

  SELECT pay FROM item_2 WHERE name ~ '^a.*'
      Index Scan using idx_item_2_name on item_2  (cost=0.00..8.47 rows=47825 width=8) (actual time=0.074..185.303 rows=18502 loops=1)
        Index Cond: (((name)::text ~&gt;=~ 'a'::text) AND ((name)::text ~&lt;~ 'b'::text))
        Filter: ((name)::text ~ '^a.*'::text)
      Total runtime: 209.987 ms
  real time <strong>259.40 ms</strong>

  SELECT pay FROM item_3 WHERE substr(name, 0, 1) = 'a'
      Seq Scan on item_3  (cost=0.00..23115.00 rows=5000 width=8) (actual time=699.043..699.043 rows=0 loops=1)
        Filter: (substr((name)::text, 0, 1) = 'a'::text)
      Total runtime: 699.074 ms
  real time <strong>707.79 ms</strong>

  result size 18502 records
</pre>
<p>Tu został dodany index <tt>CREATE INDEX idx_item_2_name ON item_2 (name <b>varchar_pattern_ops</b>)</tt>. Zastosowanie <a href="http://www.postgresql.org/docs/8.3/interactive/indexes-opclass.html">varchar_pattern_ops</a> umożliwia korzystanie z specjalnego operatora umożliwiającego porównywanie bajt po bajcie. Umożliwia on dobre wykorzystanie indeksów w LIKE/wyrażeniach regularnych.</p>
<p>Doskonale to widać w tym przykładzie. Dwa pierwsze zapytania wykonały się szybciej niż najszybsze z poprzedniego zestawu.</p>
<p>Natomiast indeks na wartości (<tt>CREATE INDEX idx_item_3_name ON item_3 (substring(name, 0, 1))</tt>) nie zdał egzaminu i został totalnie zignorowany.</p>
<h3>Losowy element</h3>
<pre>
<span>python pg.py explain random</span>
TEST random
  SELECT id FROM item_1 ORDER BY random() LIMIT 1
      Limit  (cost=25615.00..25615.00 rows=1 width=4) (actual time=3210.675..3210.677 rows=1 loops=1)
        -&gt;  Sort  (cost=25615.00..28115.00 rows=1000000 width=4) (actual time=3210.672..3210.672 rows=1 loops=1)
              Sort Key: (random())
              Sort Method:  top-N heapsort  Memory: 17kB
              -&gt;  Seq Scan on item_1  (cost=0.00..20615.00 rows=1000000 width=4) (actual time=0.040..1665.266 rows=1000000 loops=1)
      Total runtime: 3210.711 ms
  real time <strong>658.06 ms</strong>

  SELECT id FROM item_1 LIMIT 1 OFFSET floor(random() * (1000000 - 1))
      Limit  (cost=1811.50..1811.52 rows=1 width=4) (actual time=1346.399..1346.401 rows=1 loops=1)
        -&gt;  Seq Scan on item_1  (cost=0.00..18115.00 rows=1000000 width=4) (actual time=0.007..733.375 rows=489524 loops=1)
      Total runtime: 1346.435 ms
  real time <strong>88.49 ms</strong>

  SELECT id FROM item_1 WHERE id &gt;= floor(random() * (select max(id) from item_1)) LIMIT 1
      Limit  (cost=0.04..0.14 rows=1 width=4) (actual time=0.040..0.041 rows=1 loops=1)
        InitPlan
          -&gt;  Result  (cost=0.03..0.04 rows=1 width=0) (actual time=0.021..0.022 rows=1 loops=1)
                InitPlan
                  -&gt;  Limit  (cost=0.00..0.03 rows=1 width=4) (actual time=0.014..0.015 rows=1 loops=1)
                        -&gt;  Index Scan Backward using item_1_pkey on item_1  (cost=0.00..31886.34 rows=1000000 width=4) (actual time=0.012..0.012 rows=1 loops=1)
                              Filter: (id IS NOT NULL)
        -&gt;  Seq Scan on item_1  (cost=0.00..33115.00 rows=333333 width=4) (actual time=0.037..0.037 rows=1 loops=1)
              Filter: ((id)::double precision &gt;= floor((random() * ($1)::double precision)))
      Total runtime: 0.076 ms
  real time <strong>1.54 ms</strong>

  result size 1 records
</pre>
<p>Zapytania o losowy element bywają potrzebne częściej niż się wydaje.</p>
<p>Metoda pierwsza jest najbardziej standardowa i ma wiele zalet:
<ul>
<li>prawdziwa losowość</li>
<li>możliwość pobrania za jednym zamachem nie jednego ale wielu elementów (nie powtarzających się)</li>
<li>przewidywalny czas wykonania</li>
</ul>
<p>Niestety jest powolna i nie da się temu zaradzić.</p>
<p> Metoda druga ma jedną poważną wadę &#8211; do OFFSET nie da się wstawić podzapytania &#8211; więc w praktyce aby z niej korzystać trzeba się posłużyć PL/pgSQL lub inną finezyjną metodą.</p>
<p>Metoda trzecia potrafi być bardzo szybka ale ma 2 wady:</p>
<ul>
<li>Jej czas wykonania trudno przewidzieć &#8211; czasami jest kilkudziesięciokrotnie wolniejsza (niestety w głównym (zewnętrznym) zapytaniu jest wykonywany Seq Scan z losowym limitem)</li>
<li>Nie zwraca w pełni losowych wyników. Jeśli odstępy pomiędzy id rekordów są nierównomierne to rekordy które są &#8222;gęsto&#8221; mają mniejszą szansę na wylosowanie</li>
</ul>
<p>Ciekawy opis jak też jeszcze jedno rozwiązanie można znaleźć <a href="http://www.depesz.com/index.php/2007/01/25/losowy-rekord-z-bazy-danych/">tu</a>.</p>
<h3>Podzapytania</h3>
<pre>
<span>python pg.py explain subselect</span>
  SELECT pay FROM item_1 WHERE pay BETWEEN 4000 AND 5000
      Bitmap Heap Scan on item_1  (cost=2317.48..12067.28 rows=108987 width=8) (actual time=34.468..276.750 rows=108995 loops=1)
        Recheck Cond: ((pay &gt;= 4000::double precision) AND (pay &lt;= 5000::double precision))
        -&gt;  Bitmap Index Scan on idx_item_1_pay  (cost=0.00..2290.23 rows=108987 width=0) (actual time=32.190..32.190 rows=108995 loops=1)
              Index Cond: ((pay &gt;= 4000::double precision) AND (pay &lt;= 5000::double precision))
      Total runtime: 419.148 ms
  real time <strong>553.88 ms</strong>

  SELECT pay FROM (SELECT pay FROM item_1 WHERE 4000 &lt;= pay) AS job WHERE pay &lt;= 5000;
      Bitmap Heap Scan on item_1  (cost=2317.48..12067.28 rows=108987 width=8) (actual time=34.796..275.877 rows=108995 loops=1)
        Recheck Cond: ((4000::double precision &lt;= pay) AND (pay &lt;= 5000::double precision))
        -&gt;  Bitmap Index Scan on idx_item_1_pay  (cost=0.00..2290.23 rows=108987 width=0) (actual time=32.491..32.491 rows=108995 loops=1)
              Index Cond: ((4000::double precision &lt;= pay) AND (pay &lt;= 5000::double precision))
      Total runtime: 420.154 ms
  real time <strong>573.61 ms</strong>

  result size 108995 records
</pre>
<p>Jak widać na tym prostym zapytaniu PostgreSQL potrafi rozwijać zagnieżdżone zapytania w FROM.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/coffeefreecode.wordpress.com/14/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/coffeefreecode.wordpress.com/14/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/coffeefreecode.wordpress.com/14/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/coffeefreecode.wordpress.com/14/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/coffeefreecode.wordpress.com/14/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/coffeefreecode.wordpress.com/14/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/coffeefreecode.wordpress.com/14/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/coffeefreecode.wordpress.com/14/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/coffeefreecode.wordpress.com/14/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/coffeefreecode.wordpress.com/14/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/coffeefreecode.wordpress.com/14/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/coffeefreecode.wordpress.com/14/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/coffeefreecode.wordpress.com/14/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/coffeefreecode.wordpress.com/14/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=coffeefreecode.wordpress.com&amp;blog=3346273&amp;post=14&amp;subd=coffeefreecode&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://coffeefreecode.wordpress.com/2011/01/31/optymalizacja-zapytan-w-postgresql/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/eb87bb07d5570b63195d2183d30579e6?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">kosqx</media:title>
		</media:content>
	</item>
		<item>
		<title>Reddit albo zdrowie (psychiczne)</title>
		<link>http://coffeefreecode.wordpress.com/2011/01/30/reddit-albo-zdrowie-psychiczne/</link>
		<comments>http://coffeefreecode.wordpress.com/2011/01/30/reddit-albo-zdrowie-psychiczne/#comments</comments>
		<pubDate>Sun, 30 Jan 2011 17:52:31 +0000</pubDate>
		<dc:creator>Krzysztof Kosyl</dc:creator>
				<category><![CDATA[Notki]]></category>
		<category><![CDATA[reddit]]></category>
		<category><![CDATA[usability]]></category>

		<guid isPermaLink="false">http://coffeefreecode.wordpress.com/?p=64</guid>
		<description><![CDATA[Po dłuższym czasie używania reddit.com postanowiłem zarejstrować się w tym serwisie. Po to by móc w łatwy sposób oglądać wiadomości z kilku grup (jak choćby programming, compsci, coding czy python). Niestety samo dodawanie z użytecznością (usability) nie ma za wiele wspólnego. Zamiast zastosować klasyczne checkboxy czy ich proste odpowiedniki użyto czegoś takiego: Intuicja podpowiada, że [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=coffeefreecode.wordpress.com&amp;blog=3346273&amp;post=64&amp;subd=coffeefreecode&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Po dłuższym czasie używania reddit.com postanowiłem zarejstrować się w tym serwisie. Po to by móc w łatwy sposób oglądać wiadomości z kilku grup (jak choćby <a href="http://www.reddit.com/r/programming">programming</a>, <a href="http://www.reddit.com/r/compsci">compsci</a>, <a href="http://www.reddit.com/r/coding">coding</a> czy <a href="http://www.reddit.com/r/python">python</a>). Niestety samo dodawanie z użytecznością (usability) nie ma za wiele wspólnego. </p>
<p>Zamiast zastosować klasyczne checkboxy czy ich proste odpowiedniki użyto czegoś takiego:<a href="http://coffeefreecode.files.wordpress.com/2011/01/reddit.png"><img src="http://coffeefreecode.files.wordpress.com/2011/01/reddit.png?w=630" alt="" title="reddit"   class="aligncenter size-full wp-image-65" /></a> </p>
<p>Intuicja podpowiada, że zielony kolor oraz znak plus oznacza &#8222;dodane&#8221; a czerwone z minusem oznacza &#8222;usunięte&#8221;. Sugestia na stronie nic nie pomaga (głosi ona: <em>click the +frontpage or -frontpage buttons to choose which reddits appear on your front page.</em>).   </p>
<p>Okazuje się, że jest odwrotnie. Zamiast &#8222;dodane&#8221; zielone oznacza &#8222;dodaj&#8221; a czerwone nie &#8222;usunięte&#8221; ale &#8222;usuń&#8221;. Jest to bardzo mylące a w efekcie frustrujące.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/coffeefreecode.wordpress.com/64/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/coffeefreecode.wordpress.com/64/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/coffeefreecode.wordpress.com/64/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/coffeefreecode.wordpress.com/64/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/coffeefreecode.wordpress.com/64/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/coffeefreecode.wordpress.com/64/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/coffeefreecode.wordpress.com/64/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/coffeefreecode.wordpress.com/64/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/coffeefreecode.wordpress.com/64/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/coffeefreecode.wordpress.com/64/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/coffeefreecode.wordpress.com/64/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/coffeefreecode.wordpress.com/64/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/coffeefreecode.wordpress.com/64/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/coffeefreecode.wordpress.com/64/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=coffeefreecode.wordpress.com&amp;blog=3346273&amp;post=64&amp;subd=coffeefreecode&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://coffeefreecode.wordpress.com/2011/01/30/reddit-albo-zdrowie-psychiczne/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/eb87bb07d5570b63195d2183d30579e6?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">kosqx</media:title>
		</media:content>

		<media:content url="http://coffeefreecode.files.wordpress.com/2011/01/reddit.png" medium="image">
			<media:title type="html">reddit</media:title>
		</media:content>
	</item>
		<item>
		<title>Yakulo &#8211; automatyzacja otwierania sesji w Yakuake</title>
		<link>http://coffeefreecode.wordpress.com/2010/10/26/yakulo-automatyzacja-otwierania-sesji-w-yakuake/</link>
		<comments>http://coffeefreecode.wordpress.com/2010/10/26/yakulo-automatyzacja-otwierania-sesji-w-yakuake/#comments</comments>
		<pubDate>Tue, 26 Oct 2010 20:26:25 +0000</pubDate>
		<dc:creator>Krzysztof Kosyl</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[kde]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[script]]></category>
		<category><![CDATA[yakuake]]></category>

		<guid isPermaLink="false">http://coffeefreecode.wordpress.com/?p=81</guid>
		<description><![CDATA[Consuetude altera natura est (Przyzwyczajenie jest drugą naturą człowieka). Jest kilka programów bez których nie wyobrażam sobie używania komputera. Wiele z nich działa w trybie tekstowym a więc by mieć do nich dostęp potrzebny jest emulator konsoli. Moim ulubionym jest Yakuake. Można o nim przeczytać w jakilinux oraz ubucentrum więc nie nie muszę go osobiście [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=coffeefreecode.wordpress.com&amp;blog=3346273&amp;post=81&amp;subd=coffeefreecode&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>
    <i>Consuetude altera natura est</i> (Przyzwyczajenie jest drugą naturą człowieka).
</p>
<p>Jest kilka programów bez których nie wyobrażam sobie używania komputera. Wiele z nich działa w trybie tekstowym a więc by mieć do nich dostęp potrzebny jest emulator konsoli. Moim ulubionym jest <b>Yakuake</b>. Można o nim przeczytać w <a href="http://jakilinux.org/aplikacje/konsola/yakuake-inny-terminal-rodem-z-quakea/">jakilinux</a>     oraz <a href="http://konqiklub.ubucentrum.net/2011/01/yakuake-czyli-konsola-quake-w-kde-sc.html">ubucentrum</a> więc nie nie muszę go osobiście zachwalać.
</p>
<p>Lubię mieć porządek w środowisku pracy. Dlatego zawsze otwierałem kilka sesji w tej samej kolejności. Na pierwszej logowałem się na konto roota, na drugiej uruchamiałem IPython&#8230; W sumie zdażało mi się dojść do kilkunastu kart (w chwili gdy piszę te słowa mam ich otwarte 15). W efekcie po starcie systemu poświęcałem na to całkiem sporo czasu.
</p>
<p>Jako, że jestem leniwy (a <a href="http://c2.com/cgi/wiki?LazinessImpatienceHubris">lenistwo jest cnotą</a>) postanowiłem coś z tym zrobić. W efekcie powstał skrypt <a href="https://github.com/kosqx/yakulo">yakulo</a> automatyzujący tą pracę.
</p>
<h2>Instalacja</h2>
<p>Skrypt działa zarówno z wersjami Yakuake przeznaczonymi dla KDE 3 oraz KDE 4.</p>
<p>Instalacja sprowadza się do wykonania następujących poleceń:</p>
<pre>
git clone git://github.com/kosqx/yakulo.git
sudo cp yakulo/yakulo /usr/bin
</pre>
<p>Jeśli nie masz w systemie gita możesz pobrać skrypt z <a href="https://github.com/kosqx/yakulo/raw/master/yakulo">https://github.com/kosqx/yakulo/raw/master/yakulo</a> i zainstalować go ręcznie.</p>
<h2>Szybki start</h2>
<p>Yakulo potrzebuje do pracy plików konfiguracyjnych zawierających informacje jak nazwać nowo tworzone sesje i jakie polecenia w nich uruchomić.  Pliki te należy twożyć w katalogu <tt>~/.yakulo/</tt>
</p>
<p>
    Przykładowy plik <tt>~/.yakulo/foo</tt> może mieć zawartość:
</p>
<pre>
# To jest komentarz
:tab Nazwa pierwszej karty
  uname -a
  ls
:tab Nazwa drugiej karty
  echo druga karta
</pre>
<p>
    Teraz można już uruchomić skrypt poleceniem:
</p>
<pre>
yakulo foo
</pre>
<p>
    Jeśli stworzy się więcej plików konfiguracyjnych (przykładowo <tt>~/.yakulo/base</tt>, <tt>~/.yakulo/project_a</tt> oraz <tt>~/.yakulo/project_b</tt>)<br />
    to można załadować wszystkie zawarte w nich karty jednym poleceniem:
</p>
<pre>
yakulo base project_a project_b
</pre>
<h2>Podsumowanie</h2>
<p>Teraz wyrobiłem sobie nowy nawyk. Zaraz po starcie KDE naciskam kolejno <b>F12</b> (wyświetla Yakuake), <b>y</b> (wpisuje w konsoli literę <tt>y</tt>), <b>PageUp</b> (w Zsh mam to zmapowane do <tt>history-beginning-search-backward</tt> czyli wyszykiwania w historii), <b>Enter</b> (uruchamia polecenie). Potem czekam kilka sekund i <i>voilà!</i></p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/coffeefreecode.wordpress.com/81/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/coffeefreecode.wordpress.com/81/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/coffeefreecode.wordpress.com/81/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/coffeefreecode.wordpress.com/81/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/coffeefreecode.wordpress.com/81/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/coffeefreecode.wordpress.com/81/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/coffeefreecode.wordpress.com/81/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/coffeefreecode.wordpress.com/81/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/coffeefreecode.wordpress.com/81/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/coffeefreecode.wordpress.com/81/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/coffeefreecode.wordpress.com/81/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/coffeefreecode.wordpress.com/81/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/coffeefreecode.wordpress.com/81/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/coffeefreecode.wordpress.com/81/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=coffeefreecode.wordpress.com&amp;blog=3346273&amp;post=81&amp;subd=coffeefreecode&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://coffeefreecode.wordpress.com/2010/10/26/yakulo-automatyzacja-otwierania-sesji-w-yakuake/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/eb87bb07d5570b63195d2183d30579e6?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">kosqx</media:title>
		</media:content>
	</item>
		<item>
		<title>Programowania funkcyjnego z Pythonem spotkania</title>
		<link>http://coffeefreecode.wordpress.com/2010/08/26/programowania-funkcyjnego-z-pythonem-spotkania/</link>
		<comments>http://coffeefreecode.wordpress.com/2010/08/26/programowania-funkcyjnego-z-pythonem-spotkania/#comments</comments>
		<pubDate>Wed, 25 Aug 2010 23:29:10 +0000</pubDate>
		<dc:creator>Krzysztof Kosyl</dc:creator>
				<category><![CDATA[Notki]]></category>
		<category><![CDATA[clojure]]></category>
		<category><![CDATA[functional]]></category>
		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://coffeefreecode.wordpress.com/?p=44</guid>
		<description><![CDATA[Jacek Laskowski opublikował wczoraj na swoim blogu ciekawą notkę Programowania funkcyjnego z Clojure początki niełatwe (szczególnie mentalnie). Myśli o programowaniu funkcyjnym siedzą mi w głowie od ponad roku, więc przeczytałem ją z zainteresowaniem. Na końcu zamieścił małe wyzwanie: by przepisać podanego przez niego jednolinijkowca do Javy. Java to nie jest mój ulubiony język, więc postanowiłem [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=coffeefreecode.wordpress.com&amp;blog=3346273&amp;post=44&amp;subd=coffeefreecode&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Jacek Laskowski opublikował wczoraj na swoim blogu ciekawą notkę <a href="http://blog.japila.pl/2010/08/programowania-funkcyjnego-z-clojure.html">Programowania funkcyjnego z Clojure początki niełatwe (szczególnie mentalnie)</a>. Myśli o programowaniu funkcyjnym siedzą mi w głowie od ponad roku, więc przeczytałem ją z zainteresowaniem. Na końcu zamieścił małe wyzwanie: by przepisać podanego przez niego jednolinijkowca do Javy. </p>
<p><pre class="brush: clojure;">
(doseq [linia (map (fn [[h s]] (str h &quot; (&quot; s &quot;)&quot;)) (partition 2 [1 2 3 4 5 6 7 8 9 0]))] (println linia))
</pre></p>
<p>Java to nie jest mój ulubiony język, więc postanowiłem sprawdzić jak by dało się to zrobić w Pythonie (który posiada lekkie wsparcie tego paradygmatu funkcyjnego). </p>
<h2>Do dzieła</h2>
<p>Na początek mały problem &#8211; Python nie posiada wbudowanej funkcji <tt>partition</tt>. Trzeba ją samemu napisać. Na przykład tak:<br />
<pre class="brush: python;">
def partition(size, seq):
    result = []
    for i in seq:
        result.append(i)
        if len(result) == size:
            yield tuple(result)
            result = []
</pre><br />
Uwaga: to jest odpowiednik <tt>clojure.core/partition(n coll)</tt>. Nie obsługuje ani <tt>step</tt> ani <tt>pad</tt>.</p>
<p>Potem już idzie z górki:<br />
<pre class="brush: python;">
print &quot;\n&quot;.join(map(lambda t: &quot;%i (%i)&quot; % t, partition(2, [1, 2, 3, 4, 5, 6, 7, 8, 9, 0])))
</pre></p>
<p>Można to napisać nawet bardziej w stylu Pythona (poprzez zastąpienie funkcji <tt>map</tt> i <tt>lambda</tt> przez <i>listę składaną</i>):<br />
<pre class="brush: python;">
print &quot;\n&quot;.join([&quot;%i (%i)&quot; % t for t in partition(2, [1, 2, 3, 4, 5, 6, 7, 8, 9, 0])])
</pre></p>
<h2>Programowanie funkcyjne?</h2>
<p>Kod, który w Clojure zajmował 106 znaków dało się skrócić do odpowiednio 92 i 87 znaków.</p>
<p>Co więcej udało się z niego usunąć efekty uboczne i sekwencyjność wprowadzoną przez <tt>doseq</tt>. Cały napis jest budowany w pamięci a dopiero następnie wyświetlany &#8211; co jest bardziej funkcyjne.</p>
<p>W Clojure dało by się to zrobić za pomocą kodu:<br />
<pre class="brush: clojure;">
(println (apply str (interpose &quot;\n&quot; (map (fn [[h s]] (str h &quot; (&quot; s &quot;)&quot;)) (partition 2 [1 2 3 4 5 6 7 8 9 0])))))
</pre><br />
Przy okazji: to jest prawdopodobnie pierwsza linijka mojego kodu w tym języku więc mogła by być lepsza.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/coffeefreecode.wordpress.com/44/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/coffeefreecode.wordpress.com/44/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/coffeefreecode.wordpress.com/44/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/coffeefreecode.wordpress.com/44/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/coffeefreecode.wordpress.com/44/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/coffeefreecode.wordpress.com/44/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/coffeefreecode.wordpress.com/44/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/coffeefreecode.wordpress.com/44/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/coffeefreecode.wordpress.com/44/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/coffeefreecode.wordpress.com/44/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/coffeefreecode.wordpress.com/44/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/coffeefreecode.wordpress.com/44/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/coffeefreecode.wordpress.com/44/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/coffeefreecode.wordpress.com/44/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=coffeefreecode.wordpress.com&amp;blog=3346273&amp;post=44&amp;subd=coffeefreecode&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://coffeefreecode.wordpress.com/2010/08/26/programowania-funkcyjnego-z-pythonem-spotkania/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/eb87bb07d5570b63195d2183d30579e6?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">kosqx</media:title>
		</media:content>
	</item>
		<item>
		<title>Obsługa HTML5 w przeglądarkach</title>
		<link>http://coffeefreecode.wordpress.com/2010/05/16/obsluga-html5-w-przegladarkach/</link>
		<comments>http://coffeefreecode.wordpress.com/2010/05/16/obsluga-html5-w-przegladarkach/#comments</comments>
		<pubDate>Sun, 16 May 2010 12:20:11 +0000</pubDate>
		<dc:creator>Krzysztof Kosyl</dc:creator>
				<category><![CDATA[WWW]]></category>
		<category><![CDATA[chrome]]></category>
		<category><![CDATA[html5]]></category>

		<guid isPermaLink="false">http://coffeefreecode.wordpress.com/?p=36</guid>
		<description><![CDATA[Na stronie html5test.com można sprawdzić jak dobrze nasza przeglądarka radzi sobie z obsługą HTML5. Przeprowadziłem małe testy, a oto ich wyniki: Przeglądarka Wersja Wynik (maksymalnie 160) Chrome 5.0.342.9 beta 137 Chrome 4.1.249.1064 118 Chromium 4.0.251.0 114 Opera 10.53 102 Opera 9.63 38 Firefox 3.6 101 Firefox 3.0.15; 3.0.19 31 Internet Explorer 8.0 19 Internet Explorer [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=coffeefreecode.wordpress.com&amp;blog=3346273&amp;post=36&amp;subd=coffeefreecode&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Na stronie <a href="http://html5test.com/">html5test.com</a> można sprawdzić jak dobrze nasza przeglądarka radzi sobie z obsługą HTML5.</p>
<p>Przeprowadziłem małe testy, a oto ich wyniki:</p>
<table border="0px">
<thead>
<tr>
<th>Przeglądarka</th>
<th>Wersja</th>
<th>Wynik (maksymalnie 160)</th>
</tr>
</thead>
<tbody>
<tr>
<td>Chrome</td>
<td>5.0.342.9 beta</td>
<td>137</td>
</tr>
<tr>
<td>Chrome</td>
<td>4.1.249.1064</td>
<td>118</td>
</tr>
<tr>
<td>Chromium</td>
<td>4.0.251.0</td>
<td>114</td>
</tr>
<tr>
<td>Opera</td>
<td>10.53</td>
<td>102</td>
</tr>
<tr>
<td>Opera</td>
<td>9.63</td>
<td>38</td>
</tr>
<tr>
<td>Firefox</td>
<td>3.6</td>
<td>101</td>
</tr>
<tr>
<td>Firefox</td>
<td>3.0.15; 3.0.19</td>
<td>31</td>
</tr>
<tr>
<td>Internet Explorer</td>
<td>8.0</td>
<td>19</td>
</tr>
<tr>
<td>Internet Explorer</td>
<td>7.0</td>
<td>11</td>
</tr>
</tbody>
</table>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/coffeefreecode.wordpress.com/36/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/coffeefreecode.wordpress.com/36/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/coffeefreecode.wordpress.com/36/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/coffeefreecode.wordpress.com/36/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/coffeefreecode.wordpress.com/36/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/coffeefreecode.wordpress.com/36/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/coffeefreecode.wordpress.com/36/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/coffeefreecode.wordpress.com/36/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/coffeefreecode.wordpress.com/36/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/coffeefreecode.wordpress.com/36/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/coffeefreecode.wordpress.com/36/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/coffeefreecode.wordpress.com/36/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/coffeefreecode.wordpress.com/36/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/coffeefreecode.wordpress.com/36/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=coffeefreecode.wordpress.com&amp;blog=3346273&amp;post=36&amp;subd=coffeefreecode&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://coffeefreecode.wordpress.com/2010/05/16/obsluga-html5-w-przegladarkach/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/eb87bb07d5570b63195d2183d30579e6?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">kosqx</media:title>
		</media:content>
	</item>
		<item>
		<title>Automatyzacja Komodo Edit/IDE &#8211; makro w Pythonie</title>
		<link>http://coffeefreecode.wordpress.com/2010/03/24/automatyzacja-komodo-editide-makro-w-pythonie/</link>
		<comments>http://coffeefreecode.wordpress.com/2010/03/24/automatyzacja-komodo-editide-makro-w-pythonie/#comments</comments>
		<pubDate>Wed, 24 Mar 2010 16:54:19 +0000</pubDate>
		<dc:creator>Krzysztof Kosyl</dc:creator>
				<category><![CDATA[Artykuły]]></category>
		<category><![CDATA[edit]]></category>
		<category><![CDATA[ide]]></category>
		<category><![CDATA[komodo]]></category>
		<category><![CDATA[macro]]></category>
		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://coffeefreecode.wordpress.com/?p=30</guid>
		<description><![CDATA[Często zdarza się, że w trakcie programowania występuje potrzeba wykonania prostej acz pracochłonnej i czasochłonnej czynności. Dobre edytor umożliwia automatyzację takich zadań. Mam ostatnio okazję pracować z Komodo Edit (darmowa wersja Komodo IDE). By nie marnować czasu napisałem proste makra, których szkielet przedstawię poniżej. Dodawanie makra Dodanie makra sprowadza się do wykonania następujących czynności: Wyświetlamy [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=coffeefreecode.wordpress.com&amp;blog=3346273&amp;post=30&amp;subd=coffeefreecode&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Często zdarza się, że w trakcie programowania występuje potrzeba wykonania prostej acz pracochłonnej i czasochłonnej czynności. Dobre edytor umożliwia automatyzację takich zadań.</p>
<p>Mam ostatnio okazję pracować z <a href="http://www.activestate.com/komodo-edit">Komodo Edit</a> (darmowa wersja <a href="http://www.activestate.com/komodo-ide">Komodo IDE</a>). By nie marnować czasu napisałem proste makra, których szkielet przedstawię poniżej.</p>
<h2>Dodawanie makra</h2>
<p>Dodanie makra sprowadza się do wykonania następujących czynności:</p>
<ol>
<li>
		Wyświetlamy <strong>Toolbox</strong> (View &gt; Tabs &amp; Sidebars &gt; Toolbox)<br />
<img src="http://coffeefreecode.files.wordpress.com/2011/05/komodo1.jpg?w=630" alt="" title="komodo1"   class="aligncenter size-full wp-image-99" />
	</li>
<li>
		W Toolbox klikamy na przycisk <strong>Add Item to Toolbox</strong> a następnie wybieramy <strong>New Macro</strong><br />
<img src="http://coffeefreecode.files.wordpress.com/2011/05/komodo2.jpg?w=630" alt="" title="komodo2"   class="aligncenter size-full wp-image-100" />
	</li>
<li>
		Zmieniamy język na <strong>Python</strong>, ustawiamy nazwę makra. Możemy też zmienić jego ikonę, ustawić dla niego skrót klawiaturowy. Dodajemy kod makra i zapisujemy je klikając OK.<br />
<img src="http://coffeefreecode.files.wordpress.com/2011/05/komodo3.jpg?w=630" alt="" title="komodo3"   class="aligncenter size-full wp-image-101" />
	</li>
<li>
		Makro uruchamia się klikając na nie w <strong>Toolbox</strong> lub korzystając z przypisanego skrótu klawiaturowego.
	</li>
</ol>
<h2>Kod makra</h2>
<p>W praktyce używam tylko dwóch rodzajów makr. Pierwsze z nich przetwarzają (&#8222;filtrują&#8221;) zaznaczony tekst. Drugie &#8222;wykonują&#8221; polecenia zawarte w aktualnej linii.</p>
<p>Poniżej zawarty jest kod realizujący oba te sposoby.</p>
<pre>
<pre class="brush: python;">
import komodo

def replace_selection(fn):
    scimoz = komodo.editor
    sel_start = min(scimoz.currentPos, scimoz.anchor)
    text = fn(scimoz.selText)
    scimoz.replaceSel(text)
    scimoz.anchor = sel_start
    scimoz.currentPos = sel_start + len(text)

def replace_line(fn):
    scimoz = komodo.editor
    scimoz.lineEnd()
    scimoz.homeExtend()
    text = fn(scimoz.selText)
    scimoz.replaceSel(text)

# Jedną z poniższych linii należy zakomentować
replace_selection(lambda t: t.replace(u' ',u'\n'))
replace_line(lambda t: u'&gt;&gt;' + t)
</pre>
</pre>
<h2>Więcej możliwości</h2>
<p>Oczywiście powyższe przykłady zastosowań są bardzo proste. W praktyce można napisać dowolną funkcję, robiącą dokładnie to czego chcemy.</p>
<p>Pełną dokumentację można znaleźć na stronie <a href="http://docs.activestate.com/komodo/5.2/macroapi.html">Macro API Reference</a>.</p>
<p>Jeśli napiszemy więcej makr lub często z nich korzystamy to możemy stworzyć dla nich własny Toolbar (<i>New Custom Toolbar</i>) lub Menu (<i>New Custom Menu</i>).</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/coffeefreecode.wordpress.com/30/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/coffeefreecode.wordpress.com/30/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/coffeefreecode.wordpress.com/30/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/coffeefreecode.wordpress.com/30/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/coffeefreecode.wordpress.com/30/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/coffeefreecode.wordpress.com/30/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/coffeefreecode.wordpress.com/30/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/coffeefreecode.wordpress.com/30/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/coffeefreecode.wordpress.com/30/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/coffeefreecode.wordpress.com/30/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/coffeefreecode.wordpress.com/30/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/coffeefreecode.wordpress.com/30/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/coffeefreecode.wordpress.com/30/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/coffeefreecode.wordpress.com/30/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=coffeefreecode.wordpress.com&amp;blog=3346273&amp;post=30&amp;subd=coffeefreecode&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://coffeefreecode.wordpress.com/2010/03/24/automatyzacja-komodo-editide-makro-w-pythonie/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/eb87bb07d5570b63195d2183d30579e6?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">kosqx</media:title>
		</media:content>

		<media:content url="http://coffeefreecode.files.wordpress.com/2011/05/komodo1.jpg" medium="image">
			<media:title type="html">komodo1</media:title>
		</media:content>

		<media:content url="http://coffeefreecode.files.wordpress.com/2011/05/komodo2.jpg" medium="image">
			<media:title type="html">komodo2</media:title>
		</media:content>

		<media:content url="http://coffeefreecode.files.wordpress.com/2011/05/komodo3.jpg" medium="image">
			<media:title type="html">komodo3</media:title>
		</media:content>
	</item>
		<item>
		<title>Curing Python&#8217;s Neglect &#8211; polemika</title>
		<link>http://coffeefreecode.wordpress.com/2009/05/31/curing-pythons-neglect-polemika/</link>
		<comments>http://coffeefreecode.wordpress.com/2009/05/31/curing-pythons-neglect-polemika/#comments</comments>
		<pubDate>Sun, 31 May 2009 20:49:23 +0000</pubDate>
		<dc:creator>Krzysztof Kosyl</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://coffeefreecode.wordpress.com/?p=18</guid>
		<description><![CDATA[Wczoraj Zed Shaw na swoim blogu napisał artykuł Curing Python&#8217;s Neglect. Jak można się domyślić z tytułu ma on na celu pokazanie niedbałości w języku. Z niektórymi uwagami muszę się zgodzić, lecz spora ich część jest nieprawdziwa lub nieaktualna. Operacje na listach Pierwszy z przykładów omawia operowanie na listach: Absolutnie nie mogę się zgodzić z [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=coffeefreecode.wordpress.com&amp;blog=3346273&amp;post=18&amp;subd=coffeefreecode&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Wczoraj <b>Zed Shaw</b> na swoim blogu napisał artykuł <a href="http://zedshaw.com/blog/2009-05-29.html">Curing Python&#8217;s Neglect</a>. Jak można się domyślić z tytułu ma on na celu pokazanie niedbałości w języku. Z niektórymi uwagami muszę się zgodzić, lecz spora ich część jest nieprawdziwa lub nieaktualna.</p>
<h1>Operacje na listach</h1>
</p>
<p>Pierwszy z przykładów omawia operowanie na listach:</p>
<p><pre class="brush: python;">
mystuff.append(mything) 
mystuff.remove(mything) 
# to ponoć jest nielogiczne
del mystuff[4]
</pre></p>
<p>Absolutnie nie mogę się zgodzić z tym zarzutem. Metody <tt>append()</tt>, <tt>remove()</tt>, <tt>index()</tt> jako parametr dostają wartość, która ma być (odpowiednio) dołączona, usunięta lub znaleziona w liście.</p>
</p>
<p>Natomiast jeśli mamy zamiar korzystać z indeksu elementu w liście należy użyć nawiasów kwadratowych i to nie zależnie czy chcemy pobrać, ustawić czy usunąć element o danym numerze.</p>
<p><pre class="brush: python;">
foo = ['ala', 'ma']
# wszystkie poniższe metody mają parametr będący wartością
foo.append('kota') 
foo.remove('ala') 
foo.index('ma')
foo.extend(('i', 'kota'))
foo.count('kota')

# natomiast tak natomiast pracuje się z indeksami
a = foo[3]
foo[3] = 'psa'
del foo[1]
</pre></p>
<h1>Obsługa argumentów linii poleceń</h1>
<p>Podobnie nie rozumiem narzekań w sprawie modułu <a href="http://docs.python.org/library/optparse.html">optparse</a>. Zasadniczo Python udostępnia dwie standardowe metody obsłużenia argumentów.</p>
<p>Pierwszym z nich jest moduł <a href="http://docs.python.org/library/getopt.html">getopt</a>, który jest wzorowany na Unixowej funkcji getopt(), jest też dostępny w Bashu czy Perlu. Dzięki temu programiści znający te języki mają o wiele łatwiej przenieść się do Pythona. Dodatkowo jest on bardzo prosty w użyciu.</p>
<p>Istnieje też moduł optparse <a href="http://docs.python.org/library/optparse.html">optparse</a>, udostępniający większe możliwości kosztem większej złożoności.</p>
<h1>Obsługa daty</h1>
<p>Rzeczywiście, obsługa dat nie należy do najprzyjemniejszych rzeczy jakie może mieć programista do zrobienia. Lecz nie widzę aby cokolwiek miało czynić ją trudniejszą w Pythonie &#8211; wręcz przeciwnie, biblioteka standardowa bardzo ułatwia sprawę. Moduł odpowiadający za nią nazywa się <a href="http://docs.python.org/library/datetime.html">datetime</a></p>
<p>Autor narzekał brak możliwości parsowania daty dziewięć la temu. Teraz jest to trywialnie proste, dla przykładu:</p>
<p><pre class="brush: python;">
from datetime import datetime

a = datetime.strptime('2009-05-31', '%Y-%m-%d')
print a   # 2009-05-31 00:00:00

b = datetime.strptime('05/31/2009 22:08:23', '%m/%d/%Y %H:%M:%S')
print b   # 2009-05-31 22:08:23
</pre></p>
<h1>Podsumowanie</h1>
<p>Nie ma idealnego języka programowania więc i w Pythonie znajdą się niedociągnięcia i błędy projektowe. Lecz niestery Zed Shaw powołuje się na język taki jakim był on w roku 2000, a to cała epoka do tyłu. Popełnia też standardowy błąd człowieka dopiero poznającego dany język, czyli: <i>ja bym to zrobił lepiej, na przykład tak jak jest w moim ulubionym języku</i>.</p>
<p>Co do sugestii o tym, że część języka i biblotek wymaga poważnych zmian &#8211; zostały one już dawno zauważone. Efektem tego jest powstanie wersji 3.0 języka, która nie jest zgodna wstecznie, ale za to naprawia wiele błędów projektowych.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/coffeefreecode.wordpress.com/18/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/coffeefreecode.wordpress.com/18/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/coffeefreecode.wordpress.com/18/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/coffeefreecode.wordpress.com/18/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/coffeefreecode.wordpress.com/18/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/coffeefreecode.wordpress.com/18/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/coffeefreecode.wordpress.com/18/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/coffeefreecode.wordpress.com/18/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/coffeefreecode.wordpress.com/18/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/coffeefreecode.wordpress.com/18/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/coffeefreecode.wordpress.com/18/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/coffeefreecode.wordpress.com/18/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/coffeefreecode.wordpress.com/18/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/coffeefreecode.wordpress.com/18/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=coffeefreecode.wordpress.com&amp;blog=3346273&amp;post=18&amp;subd=coffeefreecode&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://coffeefreecode.wordpress.com/2009/05/31/curing-pythons-neglect-polemika/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/eb87bb07d5570b63195d2183d30579e6?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">kosqx</media:title>
		</media:content>
	</item>
		<item>
		<title>Python DB API &#8211; Korzystanie z relacyjnych baz danych w Pythonie</title>
		<link>http://coffeefreecode.wordpress.com/2008/06/28/python-db-api-korzystanie-z-relacyjnych-baz-danych-w-pythonie/</link>
		<comments>http://coffeefreecode.wordpress.com/2008/06/28/python-db-api-korzystanie-z-relacyjnych-baz-danych-w-pythonie/#comments</comments>
		<pubDate>Sat, 28 Jun 2008 20:53:27 +0000</pubDate>
		<dc:creator>Krzysztof Kosyl</dc:creator>
				<category><![CDATA[Artykuły]]></category>
		<category><![CDATA[database]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[sql]]></category>

		<guid isPermaLink="false">http://coffeefreecode.wordpress.com/?p=6</guid>
		<description><![CDATA[Wprowadzenie Python posiada &#8211; jak każdy porządny język programowania &#8211; zunifikowany interfejs dostępu do relacyjnych baz danych. Koncepcyjnie jest on zbliżony do JDBC (Java DataBase Connectivity), lecz dzięki wykorzystaniu możliwości Pythona o wiele prostrzy. W chwili obecnej obowiązuje Python Database API v2.0 (znane też jako PEP 249). Jest ona rozwinięciem Python Database API v1.0 (PEP [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=coffeefreecode.wordpress.com&amp;blog=3346273&amp;post=6&amp;subd=coffeefreecode&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p><!-- Python DB API - Korzystanie z relacyjnych baz danych w Pythonie --><br />
<!-- type: Article --><br />
<!-- tags: python database sql --></p>
<h1>Wprowadzenie</h1>
<p>
    Python posiada &#8211; jak każdy porządny język programowania &#8211; zunifikowany interfejs dostępu do relacyjnych baz danych. Koncepcyjnie jest on zbliżony do JDBC (Java DataBase Connectivity), lecz dzięki wykorzystaniu możliwości Pythona o wiele prostrzy.
</p>
<p>
    W chwili obecnej obowiązuje <a href="http://www.python.org/dev/peps/pep-0249/">Python Database API v2.0</a> (znane też jako <a href="http://www.python.org/dev/peps/pep-0249/">PEP 249</a>). Jest ona rozwinięciem <a href="http://www.python.org/dev/peps/pep-0248/">Python Database API v1.0</a> (<a href="http://www.python.org/dev/peps/pep-0248/">PEP 248</a>). </p>
<h2>Prosty przykład</h2>
<p>
    Oto prosty przykład kodu wyświetlającego całą zawartość tabeli <i>test</i>. Korzystam z nim z relacyjnej bazy danych SQLite. Jej obsługa jest standardowo dostępna w Pythonie począwszy od wersji 2.5.
</p>
<p><pre class="brush: python;">
import sqlite3
db = sqlite3.connect('dbapi.db')
cur = db.cursor()

cur.execute('SELECT * FROM `test`')
print cur.rowcount
for i in cur.fetchall():
    print  i

db.close()
</pre></p>
<p>
    W pierwszej linii importujemy moduł obsługi wybranej przez nas bazy danych. Warto zwrócić uwagę, że niektóre bazy danych mają więcej niż jeden dostępny moduł (przykładowo PostgreSQL ma <tt>psycopg</tt> oraz <tt>psycopg2</tt>). Moduły dla większości baz trzeba samemu zainstalować.
</p>
<p>
    W drugiej linii nawiązujemy połączenie z bazą. Paramatry jakie przekazujemy funkcji <tt>connect</tt> zależą od wybranej bazy danych. W przypadku SQLite jest to tylko nazwa pliku w którym przechowywane są dane. Innym bazą trzeba przekazać często o wiele więcej informacji (zwykle: nazwę bazy danych, adres maszyny na której się ona znajduje, nazwę użytkownika i jego hasło).
</p>
<p>
    W trzeciej linii tworzymy kursor. Będziemy za jego pomocą wysyłali zapytania do bazy danych i odbierali z niej ich wyniki. <!--W jednym połączeniu możemy kożystać z wielu kursorów i w ten sposób np odczytywać dane z jednej tabeli i zapisywać zmodyfikowane do drugiej.-->
</p>
<p>
    W piątej linii wysyłamy do bazy zapytanie. Warto zwrócić uwagę, że treść zapytania (tzn <tt>'SELECT * FROM `test`'</tt>) nie jest ujednolicana i zależy od bazy z jakiej korzystamy.
</p>
<p>
    W szóstej linii wyświetlamy ile wierszy wyniku uzyskaliśmy.
</p>
<p>
    W siódmej i ósmej linii pobieramy wszystkie wiersze wyniku do tablicy (robi to <tt>cur.fetchall()</tt>), a następnie wyświetlamy każdy wiersz w nowej linii.
</p>
<p>
    W dziesiątej linii zamykamy połączenie z bazą danych.
</p>
<p><!--not more--></p>
<h1>Skrócony opis API</h1>
<h2>Interface modułu</h2>
<dl>
<dt><tt>connect(parametry...)</tt></dt>
<dd>
        Łączy się z bazą danych wskazana poprzez parametry. Zwraca obiekt połączenia.
    </dd>
<dt><tt>paramstyle</tt></dt>
<dd>
<p>
            Określa sposób w jaki w zapytaniach wskazuje się miejsce na parametry.
        </p>
<p>
            Nie należy tworzyć zapytania przez konkatenację fragmentów SQL i parametrów. Tak stworzony kod jest podatny na ataki <a href="http://pl.wikipedia.org/wiki/SQL_injection">SQL injection</a>.
        </p>
<dl>
<dt><tt>'qmark'</tt></dt>
<dd>
<pre class="brush: python;">
cur.execute('SELECT * FROM `test` WHERE ' +
    'name = ? AND salary &gt; ?',
    ('Adam', 1000) )
</pre>
            </dd>
<dt><tt>'numeric'</tt></dt>
<dd>
<pre class="brush: python;">
cur.execute('SELECT * FROM `test` WHERE ' +
    'name = :1 AND salary &gt; :2',
    ('Adam', 1000) )
</pre>
            </dd>
<dt><tt>'named'</tt></dt>
<dd>
<pre class="brush: python;">
cur.execute('SELECT * FROM `test` WHERE ' +
    'name = :nm AND salary &gt; :sal',
    {'nm': 'Adam', 'sal': 1000} )
</pre>
            </dd>
<dt><tt>'format'</tt></dt>
<dd>
<pre class="brush: python;">
cur.execute('SELECT * FROM `test` WHERE ' +
    'name = %s AND salary &gt; %d',
    ('Adam', 1000) )
</pre>
            </dd>
<dt><tt>'pyformat'</tt></dt>
<dd>
<pre class="brush: python;">
cur.execute('SELECT * FROM `test` WHERE ' +
    'name = %(nm)s AND salary &gt; %(sal)d',
    {'nm': 'Adam', 'sal': 1000} )
</pre>
            </dd>
</dd>
</dl>
<h2>Wyjątki</h2>
<p>Poniżej opisana jest hierarchia wyjątków.</p>
<dl>
<dt><tt>Warning</tt></dt>
<dd>Wyrzucany gdy nastąpi ważne ostrzeżenie.</dd>
<dt><tt>Error</tt></dt>
<dd>Klasa bazowa dla pozostałych błędów.</p>
<dl>
<dt><tt>InterfaceError</tt></dt>
<dd>Wyrzucany gdy błąd nastąpił w interfejsie bazy danych, a nie w samej bazie danych.</dd>
<dt><tt>DatabaseError</tt></dt>
<dd>Wyrzucany gdy błąd nastapił wewnątrz bazy danych.</p>
<dl>
<dt><tt>DataError</tt></dt>
<dd>Wyrzucany gdy pojawią się niepoprawne dane, przykładowo liczby z poza obsługiwanego zakresu, napisy dłuższe niż pole na nie przeznaczone.</dd>
<dt><tt>OperationalError</tt></dt>
<dd>Wyrzucany gdy pojawią się błędy w trakcie przetwarzania transakcji, nastąpi nagłe rozłączenie. W praktyce bywa wyrzucany również w przypadku gdzie powinien być zastosowany <tt>ProgrammingError</tt>.<!--Wyrzucany gdy pojawią się odwołania do nieistniejących tabel, próby stworzenia już istniejących, itp.--></dd>
<dt><tt>IntegrityError</tt></dt>
<dd>Wyrzucany gdy zachodzi próba przekroczenia więzów integralności, typu ustawienie już istniejącej wartości w kolumnie, której wartości powinny być unikalne lub ustawienie nieprawidłowego klucza obcego.</dd>
<dt><tt>InternalError</tt></dt>
<dd>Wywoływany gdy w bazie wystąpi wewnętrzny problem.</dd>
<dt><tt>ProgrammingError</tt></dt>
<dd>Wyrzucany gdy pojawią się odwołania do nieistniejących tabel, próby stworzenia już istniejących, itp.</dd>
<dt><tt>NotSupportedError</tt></dt>
<dd>Wyrzucany gdy nastąpi próba skorzystania z metody nie obsługiwanej przez bazę danych (przykładowo <tt>rollback()</tt> w bazie bez wsparcia transakcji).</dd>
</dl>
</dd>
</dl>
</dd>
</dl>
<h2>Obiekt połączenia (<i>connection</i>)</h2>
<dl>
<dt><tt>close()</tt></dt>
<dd>
        Zamyka połączenie z bazą danych. Automatycznie zamyka również wszystkie kursory z niego korzystające. Zamknięcie połączenia bez uprzedniego zatwierdzenia transakcji spowoduje jej wycofanie.
    </dd>
<dt><tt>commit()</tt></dt>
<dd>
        Zatwierdza transakcje. Metoda istnieje tylko w bazach obsługujących transakcje.
    </dd>
<dt><tt>rollback()</tt></dt>
<dd>
        Wycofuje transakcje. Metoda istnieje tylko w bazach obsługujących transakcje.
    </dd>
<dt><tt>cursor()</tt></dt>
<dd>
        Zwraca nowy kursor powiązany z tym połączeniem. W razie potrzeby może być emulowane.
    </dd>
</dl>
<h2>Obiekt kursora (<i>cursor</i>)</h2>
<h3>Informacje o wyniku</h3>
<dl>
<dt><tt>description</tt></dt>
<dd>
        Lista opisów poszczególnych kolumn odpowiedzi. Opis pojedynczej kolumny zawiera kolejno elementy:</p>
<dl>
<dt><tt>name</tt></dt>
<dd>nazwa kolumny &#8211; element obowiązkowy, musi być ustawiony</dd>
<dt><tt>type_code</tt></dt>
<dd>typ kolumny &#8211; element obowiązkowy, musi być ustawiony</dd>
<dt><tt>display_size</tt></dt>
<dt><tt>internal_size</tt></dt>
<dt><tt>precision</tt></dt>
<dt><tt>scale</tt></dt>
<dt><tt>null_ok</tt></dt>
<dd><tt>False</tt> jeśli kolumna ma atrybut <tt>NOT NULL</tt>, <tt>True</tt> w przeciwnym wypadku</dd>
</dl>
<p>        Elementy które nie są ustawione mają wartość <tt>None</tt>.
    </dd>
<dt><tt>rowcount</tt></dt>
<dd>
        Liczba zwróconych wierszy w przypadku <tt>SELECT</tt>, lub liczba stworzonych/zmienionych/usuniętych wierszy w przypadku <tt>INSERT</tt> / <tt>UPDATE</tt> / <tt>DELETE</tt>. Jeśli nie można ustalić ilość wierszy ostatniej instrukcji to <tt>rowcount</tt> jest ustawione na <tt>-1</tt>.
    </dd>
<h3>Wysyłanie zapytań</h3>
<dt><tt>execute(operation[, parameters])</tt></dt>
<dd>
        Tworzy i wywołuje zapytanie w bazie danych. Opcjonalny argument <tt>parameters</tt> może być (w zależności od <tt>paramstyle</tt>) sekwencją lub słownikiem. Wyniki zapytania są dostępne za pomocą kursora.
    </dd>
<dt><tt>executemany(operation, seq_of_parameters)</tt></dt>
<dd>
        Tworzy i wywołuje wielokrotne zapytanie w bazie danych. Zasadniczo działa jak poniższy kod (ale umożliwia lepszą optymalizację):<br />
<pre class="brush: python;">
for parameters in seq_of_parameters:
    execute(operation, parameters)
</pre>
    </dd>
</dl>
<h3>Pobieranie danych</h3>
<p>
    Po wykonaniu zapytania pobierającego dane z bazy należy je z niej odebrać.
</p>
<dl>
<dt><tt>fetchone()</tt></dt>
<dd>
        Pobierana następny wiersz z wyniku zapytania, zwracając pojedynczą sekwencję, lub <tt>None</tt> kiedy wszystkie dane zostały już pobrane. Przykład użycia tej metody do pobrania wszystkich elementów z kursora<br />
<pre class="brush: python;">
row = cur.fetchone()
while row is not None:
    print row
    row = cur.fetchone()
</pre></p>
</dd>
<dt><tt>fetchmany([size=cursor.arraysize])</tt></dt>
<dd>
        Pobiera pewną określoną poprzez argument <tt>size</tt> ilość elementów. Jeśli argument nie jest jawnie podany to zostaje użyta wartość <tt>cursor.arraysize</tt>.
    </dd>
<dt><tt>fetchall()</tt></dt>
<dd>
        Pobiera wszystkie (z tych, które oczekują na pobranie) wiersze z wyniku zapytania. Zwraca sekwencję sekwencji.
    </dd>
<h3>Inne</h3>
<dt><tt>callproc(procname[, parameters])</tt></dt>
<dd>
        Wywołuje procedurę składowaną. Dostępne tylko w niektórych bazach danych.
    </dd>
<dt><tt>close()</tt></dt>
<dd>
        Zamyka kursor, od tej pory nie można z niego korzystać.
    </dd>
</dl>
<br /><img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/coffeefreecode.wordpress.com/6/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/coffeefreecode.wordpress.com/6/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/coffeefreecode.wordpress.com/6/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/coffeefreecode.wordpress.com/6/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/coffeefreecode.wordpress.com/6/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/coffeefreecode.wordpress.com/6/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/coffeefreecode.wordpress.com/6/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/coffeefreecode.wordpress.com/6/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/coffeefreecode.wordpress.com/6/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/coffeefreecode.wordpress.com/6/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/coffeefreecode.wordpress.com/6/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/coffeefreecode.wordpress.com/6/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/coffeefreecode.wordpress.com/6/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/coffeefreecode.wordpress.com/6/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/coffeefreecode.wordpress.com/6/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/coffeefreecode.wordpress.com/6/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=coffeefreecode.wordpress.com&amp;blog=3346273&amp;post=6&amp;subd=coffeefreecode&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://coffeefreecode.wordpress.com/2008/06/28/python-db-api-korzystanie-z-relacyjnych-baz-danych-w-pythonie/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/eb87bb07d5570b63195d2183d30579e6?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">kosqx</media:title>
		</media:content>
	</item>
	</channel>
</rss>
