<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="cs">
	<id>http://postgres.cz/index.php?action=history&amp;feed=atom&amp;title=PostgreSQL_SQL_Tricks_I</id>
	<title>PostgreSQL SQL Tricks I - Historie editací</title>
	<link rel="self" type="application/atom+xml" href="http://postgres.cz/index.php?action=history&amp;feed=atom&amp;title=PostgreSQL_SQL_Tricks_I"/>
	<link rel="alternate" type="text/html" href="http://postgres.cz/index.php?title=PostgreSQL_SQL_Tricks_I&amp;action=history"/>
	<updated>2026-05-12T22:40:22Z</updated>
	<subtitle>Historie editací této stránky</subtitle>
	<generator>MediaWiki 1.43.3</generator>
	<entry>
		<id>http://postgres.cz/index.php?title=PostgreSQL_SQL_Tricks_I&amp;diff=1391&amp;oldid=prev</id>
		<title>PavelStehule v 23. 7. 2025, 11:37</title>
		<link rel="alternate" type="text/html" href="http://postgres.cz/index.php?title=PostgreSQL_SQL_Tricks_I&amp;diff=1391&amp;oldid=prev"/>
		<updated>2025-07-23T11:37:01Z</updated>

		<summary type="html">&lt;p&gt;&lt;/p&gt;
&lt;table style=&quot;background-color: #fff; color: #202122;&quot; data-mw=&quot;interface&quot;&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;cs&quot;&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Starší verze&lt;/td&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Verze z 23. 7. 2025, 11:37&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l654&quot;&gt;Řádek 654:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Řádek 654:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;[[PostgreSQL SQL Tricks II|Newer tricks]]&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;[[PostgreSQL SQL Tricks II|Newer tricks]]&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;-----&lt;/del&gt;&lt;/div&gt;&lt;/td&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-added&quot;&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&amp;lt;center&amp;gt;&lt;/del&gt;&lt;/div&gt;&lt;/td&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-added&quot;&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&amp;lt;htmlet&amp;gt;wide-banner&amp;lt;/htmlet&amp;gt;&lt;/del&gt;&lt;/div&gt;&lt;/td&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-added&quot;&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&amp;lt;/center&amp;gt;&lt;/del&gt;&lt;/div&gt;&lt;/td&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-added&quot;&gt;&lt;/td&gt;&lt;/tr&gt;

&lt;!-- diff cache key pgwiki2-mediawiki-:diff:1.41:old-1385:rev-1391:php=table --&gt;
&lt;/table&gt;</summary>
		<author><name>PavelStehule</name></author>
	</entry>
	<entry>
		<id>http://postgres.cz/index.php?title=PostgreSQL_SQL_Tricks_I&amp;diff=1385&amp;oldid=prev</id>
		<title>PavelStehule v 23. 7. 2025, 11:34</title>
		<link rel="alternate" type="text/html" href="http://postgres.cz/index.php?title=PostgreSQL_SQL_Tricks_I&amp;diff=1385&amp;oldid=prev"/>
		<updated>2025-07-23T11:34:40Z</updated>

		<summary type="html">&lt;p&gt;&lt;/p&gt;
&lt;table style=&quot;background-color: #fff; color: #202122;&quot; data-mw=&quot;interface&quot;&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;cs&quot;&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Starší verze&lt;/td&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Verze z 23. 7. 2025, 11:34&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l1&quot;&gt;Řádek 1:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Řádek 1:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;SQL is a language where one task can be solved multiple ways with different efficiency. You can see effective use of PostgreSQL&amp;#039;s possibilities on this page.&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;SQL is a language where one task can be solved multiple ways with different efficiency. You can see effective use of PostgreSQL&amp;#039;s possibilities on this page.&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&lt;/del&gt;&lt;/div&gt;&lt;/td&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-added&quot;&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&amp;lt;div style=&quot;padding:20px; float: right;&quot;&amp;gt;&lt;/del&gt;&lt;/div&gt;&lt;/td&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-added&quot;&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&amp;lt;div style=&quot; border: solid thin lightgrey; padding:10px;&quot;&amp;gt;&lt;/del&gt;&lt;/div&gt;&lt;/td&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-added&quot;&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&amp;lt;htmlet&amp;gt;reklama&amp;lt;/htmlet&amp;gt;&lt;/del&gt;&lt;/div&gt;&lt;/td&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-added&quot;&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&amp;lt;/div&amp;gt;&lt;/del&gt;&lt;/div&gt;&lt;/td&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-added&quot;&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&amp;lt;/div&amp;gt;&lt;/del&gt;&lt;/div&gt;&lt;/td&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-added&quot;&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;[[PostgreSQL SQL Tricks II|Newer tricks]]&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;[[PostgreSQL SQL Tricks II|Newer tricks]]&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;</summary>
		<author><name>PavelStehule</name></author>
	</entry>
	<entry>
		<id>http://postgres.cz/index.php?title=PostgreSQL_SQL_Tricks_I&amp;diff=540&amp;oldid=prev</id>
		<title>imported&gt;Pavel v 21. 12. 2016, 20:28</title>
		<link rel="alternate" type="text/html" href="http://postgres.cz/index.php?title=PostgreSQL_SQL_Tricks_I&amp;diff=540&amp;oldid=prev"/>
		<updated>2016-12-21T20:28:31Z</updated>

		<summary type="html">&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Nová stránka&lt;/b&gt;&lt;/p&gt;&lt;div&gt;SQL is a language where one task can be solved multiple ways with different efficiency. You can see effective use of PostgreSQL&amp;#039;s possibilities on this page.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding:20px; float: right;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot; border: solid thin lightgrey; padding:10px;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;htmlet&amp;gt;reklama&amp;lt;/htmlet&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[PostgreSQL SQL Tricks II|Newer tricks]]&lt;br /&gt;
== Slow queries in stored procedures ==&lt;br /&gt;
Sometimes some users reports slow queries in stored procedure. Direct execution of same query is fast. Reason is simple. Stored procedures uses prepared statements and optimalization of prepared statements runs without knowledge of real parameters. This behave ensure safety against sql injection, but carry some problems, planner is blind. This problem isn&amp;#039;t too often and solution is simply - we have to use dynamic queries. But we cannot to forget carefully check against sql injection. This kind of problems we can find with analyzing of prepared statements.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
t2=# PREPARE pp(integer) AS SELECT count(*) FROM foo WHERE a BETWEEN $1 + 100 AND $1 + 200; &lt;br /&gt;
t2=# EXPLAIN ANALYZE execute pp(100);&lt;br /&gt;
                                                      QUERY PLAN                                                       &lt;br /&gt;
-----------------------------------------------------------------------------------------------------------------------&lt;br /&gt;
 Aggregate  (cost=438.60..438.61 rows=1 width=0) (actual time=16.149..16.150 rows=1 loops=1)&lt;br /&gt;
   -&amp;gt;  Bitmap Heap Scan on foo  (cost=13.39..437.35 rows=500 width=0) (actual time=1.391..5.458 rows=1033 loops=1)&lt;br /&gt;
         Recheck Cond: ((a &amp;gt;= ($1 + 100)) AND (a &amp;lt;= ($1 + 200)))&lt;br /&gt;
         -&amp;gt;  Bitmap Index Scan on fx  (cost=0.00..13.26 rows=500 width=0) (actual time=1.131..1.131 rows=1033 loops=1)&lt;br /&gt;
               Index Cond: ((a &amp;gt;= ($1 + 100)) AND (a &amp;lt;= ($1 + 200)))&lt;br /&gt;
 Total runtime: 16.340 ms&lt;br /&gt;
(6 rows)&lt;br /&gt;
t2=# EXPLAIN ANALYZE SELECT count(*) FROM foo WHERE a BETWEEN 100 + 100 AND 100 + 200; &lt;br /&gt;
                                                      QUERY PLAN                                                       &lt;br /&gt;
-----------------------------------------------------------------------------------------------------------------------&lt;br /&gt;
 Aggregate  (cost=432.21..432.22 rows=1 width=0) (actual time=15.930..15.932 rows=1 loops=1)&lt;br /&gt;
   -&amp;gt;  Bitmap Heap Scan on foo  (cost=22.21..429.78 rows=971 width=0) (actual time=1.179..13.338 rows=1033 loops=1)&lt;br /&gt;
         Recheck Cond: ((a &amp;gt;= 200) AND (a &amp;lt;= 300))&lt;br /&gt;
         -&amp;gt;  Bitmap Index Scan on fx  (cost=0.00..21.97 rows=971 width=0) (actual time=0.917..0.917 rows=1033 loops=1)&lt;br /&gt;
               Index Cond: ((a &amp;gt;= 200) AND (a &amp;lt;= 300))&lt;br /&gt;
 Total runtime: 16.058 ms&lt;br /&gt;
(6 rows)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Execution plans are nearly identical, so we can use prepared statements.&lt;br /&gt;
&lt;br /&gt;
PostgreSQL 9.2 has redesigned implementation of prepared statements - described performance issues should be history. &lt;br /&gt;
&lt;br /&gt;
== Slow DISTINCT ==&lt;br /&gt;
Current implementation of DISTINCT needs sort, that can be slow. We should to use GROUP BY clause - this clause can to use hash aggregate - and it&amp;#039;s significantly faster than DISTINCT.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
postgres=# select count(*) from (select distinct i from g) a;&lt;br /&gt;
 count &lt;br /&gt;
-------&lt;br /&gt;
 10001&lt;br /&gt;
(1 row)&lt;br /&gt;
&lt;br /&gt;
Time: 1563,109 ms&lt;br /&gt;
&lt;br /&gt;
postgres=# select count(*) from (select i from g group by i) a;&lt;br /&gt;
 count &lt;br /&gt;
-------&lt;br /&gt;
 10001&lt;br /&gt;
(1 row)&lt;br /&gt;
&lt;br /&gt;
Time: 594,481 ms&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This topis is related to PostgreSQL 8.2 and older. PostgreSQL 8.3 and higher can to use a hash aggregation for distinct.&lt;br /&gt;
&lt;br /&gt;
== Taking quarter from interval ==&lt;br /&gt;
Interval arithmetic is little bit alchemy. Internally type interval carries days, months and years. Input value isn&amp;#039;t normalized, it stays in entered units (i.e. 200 days are stored as 0 years, 0 months and 200 days). Some functions can&amp;#039;t to work well with similar entered values (etc. EXTRACT QUARTER returns first quarter). We have to do normalization first with justify_interval function (so interval 200 days will be stored as 0 years, 6 months and 20 days). From this value we take correct information about quarter:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
postgres=# SELECT interval &amp;#039;200 days&amp;#039;, EXTRACT(QUARTER FROM interval &amp;#039;300 days&amp;#039;);&lt;br /&gt;
 interval | date_part &lt;br /&gt;
----------+-----------&lt;br /&gt;
 200 days |         1&lt;br /&gt;
(1 row)&lt;br /&gt;
postgres=# SELECT justify_interval(interval &amp;#039;200 days&amp;#039;),&lt;br /&gt;
                  EXTRACT(QUARTER FROM justify_interval(&amp;#039;200 days&amp;#039;));&lt;br /&gt;
 justify_interval | date_part &lt;br /&gt;
------------------+-----------&lt;br /&gt;
 6 mons 20 days   |         3&lt;br /&gt;
(1 row)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Author: Tom Lane from pgsql-bugs&lt;br /&gt;
&lt;br /&gt;
==Select random row==&lt;br /&gt;
Hubert Lubaczewski showed in his [[http://www.depesz.com/index.php/2007/09/16/my-thoughts-on-getting-random-row/ blog]] problem with select random row from table. When I did one database audit I found typical but slow solution (from FAQ) ORDER BY random() LIMIT 1. This method never uses index and every similar query do: complete scan of table and sort of table. Next possible method WHERE id &amp;gt;= (random()*C+1)::int LIMIT 1 is much faster, but when series has some gaps then prefer first rows after gaps. I used with success method based on using set of random numbers. Disadvantage of this method is risk of returning zero rows (then we have to call statement again):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
SELECT id, ...&lt;br /&gt;
   FROM data&lt;br /&gt;
  WHERE id = ANY(ARRAY(&lt;br /&gt;
                       SELECT (random()*max_id)::int &lt;br /&gt;
                          FROM generate_series(1,20)))&lt;br /&gt;
  LIMIT 1;&lt;br /&gt;
-- max_id is constant equal max(id) from table + 20% reserve&lt;br /&gt;
-- id is primary key of table data&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Use exception carefully ==&lt;br /&gt;
Trapping of exceptions has some overhead. So don&amp;#039;t use it too often. In majority of cases this overhead is insignificant, but that can change when you are using cycles. Next two procedures add new item into table of unique records. If item exists, it returns false, else true.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE OR REPLACE FUNCTION addnew1(integer) &lt;br /&gt;
RETURNS boolean AS $$&lt;br /&gt;
BEGIN&lt;br /&gt;
  -- test(a) .. primary key&lt;br /&gt;
  INSERT INTO test(a) VALUES($1);&lt;br /&gt;
  RETURN true;&lt;br /&gt;
EXCEPTION WHEN OTHERS&lt;br /&gt;
  RETURN false&lt;br /&gt;
END; $$ LANGUAGE plpgsql;&lt;br /&gt;
&lt;br /&gt;
CREATE OR REPLACE FUNCTION addnew2(integer)&lt;br /&gt;
RETURNS boolean AS $$&lt;br /&gt;
BEGIN&lt;br /&gt;
  IF NOT EXISTS(SELECT a FROM test WHERE a = $1) THEN&lt;br /&gt;
    INSERT INTO test(a) VALUES($1);&lt;br /&gt;
    RETURN true;&lt;br /&gt;
  END IF;&lt;br /&gt;
  RETURN false;&lt;br /&gt;
END; $$ LANGUAGE plpgsql;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Procedure addnew1 is about 2 times slower than addnew2, but addnew2 is more robust. Every protected block in plpgsql creates new subtransaction. And in this case time for creating new subtrans + INSERT &amp;gt; SELECT + INSERT.&lt;br /&gt;
&lt;br /&gt;
== Fast compare variables NEW and OLD in trigger&amp;#039;s body ==&lt;br /&gt;
We can run trigger effectively if trigger&amp;#039;s body is executed only when any columns are changed. This test can be simple and fast with new (from 8.2) row operator &amp;#039;&amp;#039;IS DISTINCT FROM&amp;#039;&amp;#039;.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
IF ROW(NEW.*) IS DISTINCT FROM ROW(OLD.*) THEN&lt;br /&gt;
  -- continue only if some is changed&lt;br /&gt;
  IF NEW.a1 IS DISTINCT FROM OLD.a1 &lt;br /&gt;
     OR NEW.a2 IS DISTINCT FROM OLD.a2 THEN&lt;br /&gt;
     -- continue only if important columns are changed&lt;br /&gt;
     ...&lt;br /&gt;
  END IF;&lt;br /&gt;
END IF;&lt;br /&gt;
RETURN NEW;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
or effectively:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
IF ROW(NEW.a1, NEW.a2) IS DISTINCT FROM ROW(OLD.a1, OLD.a2) THEN&lt;br /&gt;
  -- important columns are changed&lt;br /&gt;
  ...&lt;br /&gt;
END IF;&lt;br /&gt;
RETURN NEW;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
From 8.4 you can compare record values simply:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
IF NEW IS DISTINCT FROM OLD THEN&lt;br /&gt;
  -- continue only if some is changed&lt;br /&gt;
  ...&lt;br /&gt;
END IF;&lt;br /&gt;
RETURN NEW;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Fast first n rows removing ==&lt;br /&gt;
PostgreSQL doesn&amp;#039;t support LIMIT for UPDATE or DELETE statements. Sometimes we would to use it (for queue emulation, ...). Classic solution with IN and subquery isn&amp;#039;t too effective:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
postgres=# explain analyze delete from del where ctid  in (select ctid from del limit 10 offset 0);&lt;br /&gt;
                                                      QUERY PLAN                                                      &lt;br /&gt;
----------------------------------------------------------------------------------------------------------------------&lt;br /&gt;
 Merge IN Join  (cost=804.79..854.94 rows=10 width=6) (actual time=63.392..63.464 rows=10 loops=1)&lt;br /&gt;
   Merge Cond: (public.del.ctid = &amp;quot;IN_subquery&amp;quot;.ctid)&lt;br /&gt;
   -&amp;gt;  Sort  (cost=804.39..829.39 rows=10000 width=6) (actual time=63.205..63.217 rows=11 loops=1)&lt;br /&gt;
         Sort Key: public.del.ctid&lt;br /&gt;
         Sort Method:  quicksort  Memory: 646kB&lt;br /&gt;
         -&amp;gt;  Seq Scan on del  (cost=0.00..140.00 rows=10000 width=6) (actual time=0.045..27.366 rows=9970 loops=1)&lt;br /&gt;
   -&amp;gt;  Sort  (cost=0.41..0.43 rows=10 width=6) (actual time=0.172..0.187 rows=10 loops=1)&lt;br /&gt;
         Sort Key: &amp;quot;IN_subquery&amp;quot;.ctid&lt;br /&gt;
         Sort Method:  quicksort  Memory: 17kB&lt;br /&gt;
         -&amp;gt;  Limit  (cost=0.00..0.14 rows=10 width=6) (actual time=0.021..0.127 rows=10 loops=1)&lt;br /&gt;
               -&amp;gt;  Seq Scan on del  (cost=0.00..140.00 rows=10000 width=6) (actual time=0.016..0.030 rows=10 loops=1)&lt;br /&gt;
 Total runtime: 63.661 ms&lt;br /&gt;
(12 rows)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Tom Lane&amp;#039;s proposal is based on using of array:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
postgres=# explain analyze delete from del where ctid  = any (array(select ctid from del limit 10));&lt;br /&gt;
                                                    QUERY PLAN                                                    &lt;br /&gt;
------------------------------------------------------------------------------------------------------------------&lt;br /&gt;
 Tid Scan on del  (cost=0.14..40.37 rows=10 width=6) (actual time=0.200..0.237 rows=10 loops=1)&lt;br /&gt;
   TID Cond: (ctid = ANY ($0))&lt;br /&gt;
   InitPlan&lt;br /&gt;
     -&amp;gt;  Limit  (cost=0.00..0.14 rows=10 width=6) (actual time=0.046..0.139 rows=10 loops=1)&lt;br /&gt;
           -&amp;gt;  Seq Scan on del  (cost=0.00..140.00 rows=10000 width=6) (actual time=0.040..0.065 rows=10 loops=1)&lt;br /&gt;
 Total runtime: 0.376 ms&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The core of this trick is protection from JOIN, what is not (in this case) effective as seq. scan with filter. This trick is usable in other cases. Generally we should to say, so for small N (N &amp;lt; 100) is predicate = ANY better than predicate IN (for PostgreSQL 8.2 and 8.3). But real efficiency depends on indexes.&lt;br /&gt;
&lt;br /&gt;
== Getting list of comma separated items ==&lt;br /&gt;
D. Dante Lorenso sent note about getting list from content of table:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
SELECT a.id, a.name,&lt;br /&gt;
    array_to_string(ARRAY(&lt;br /&gt;
      SELECT b.name&lt;br /&gt;
         FROM b&lt;br /&gt;
        WHERE b.id = a.id&lt;br /&gt;
        ORDER BY b.name ASC&lt;br /&gt;
    ), &amp;#039;,&amp;#039;) AS b_names&lt;br /&gt;
   FROM a&lt;br /&gt;
  ORDER BY a.id ASC;&lt;br /&gt;
&lt;br /&gt;
[table a]&lt;br /&gt;
 id | name&lt;br /&gt;
----+------&lt;br /&gt;
1   | one&lt;br /&gt;
2   | two&lt;br /&gt;
3   | three&lt;br /&gt;
4   | four&lt;br /&gt;
&lt;br /&gt;
[table b]&lt;br /&gt;
 id | name&lt;br /&gt;
----+------&lt;br /&gt;
1   | pizza&lt;br /&gt;
1   | hot dog&lt;br /&gt;
2   | gorilla&lt;br /&gt;
2   | monkey&lt;br /&gt;
3   | apple&lt;br /&gt;
4   | cheese&lt;br /&gt;
4   | milk&lt;br /&gt;
4   | eggs&lt;br /&gt;
&lt;br /&gt;
--result&lt;br /&gt;
 id | name  | b_names&lt;br /&gt;
----+-------+---------&lt;br /&gt;
1   | one   | pizza,hot dog&lt;br /&gt;
2   | two   | gorilla,monkey&lt;br /&gt;
3   | three | apple&lt;br /&gt;
4   | four  | cheese,milk,eggs&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Using functions pgstattuple and pgstatindex ==&lt;br /&gt;
You can find nice contrib package pgstattuple in PostgreSQL 8.1 and higher. This contrib module contains some diagnostics functions for detection of fraction of dead tuples and for detection of index&amp;#039;s fragmentation. Using of these functions is more practical with next two table generating functions:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE OR REPLACE FUNCTION print_table_dead_tp(OUT table_name varchar, OUT tuple_count int8, OUT table_len varchar, OUT free_space varchar,&lt;br /&gt;
                                               OUT dead_tuple_percent numeric(5,2), OUT dead_tuple_count integer) &lt;br /&gt;
RETURNS SETOF RECORD AS $$&lt;br /&gt;
DECLARE r record; s record; &lt;br /&gt;
BEGIN &lt;br /&gt;
  FOR r IN SELECT c.oid, n.nspname || &amp;#039;.&amp;#039; || c.relname as &amp;quot;tablename&amp;quot;&lt;br /&gt;
              FROM pg_catalog.pg_class c&lt;br /&gt;
                   LEFT JOIN &lt;br /&gt;
                   pg_catalog.pg_namespace n &lt;br /&gt;
                   ON n.oid = c.relnamespace&lt;br /&gt;
             WHERE c.relkind = &amp;#039;r&amp;#039;&lt;br /&gt;
               AND n.nspname NOT IN (&amp;#039;pg_catalog&amp;#039;, &amp;#039;pg_toast&amp;#039;)&lt;br /&gt;
               AND pg_catalog.pg_table_is_visible(c.oid)&lt;br /&gt;
             ORDER BY 2&lt;br /&gt;
  LOOP&lt;br /&gt;
    s := pgstattuple(r.oid);&lt;br /&gt;
    table_name := r.tablename;&lt;br /&gt;
    tuple_count := s.tuple_count; dead_tuple_percent :=  s.dead_tuple_percent; &lt;br /&gt;
    dead_tuple_count := s.dead_tuple_count;&lt;br /&gt;
    table_len :=  pg_size_pretty(s.table_len);&lt;br /&gt;
    free_space := pg_size_pretty(s.free_space);&lt;br /&gt;
    RETURN NEXT; &lt;br /&gt;
  END LOOP; &lt;br /&gt;
  RETURN;&lt;br /&gt;
END; $$ LANGUAGE plpgsql;&lt;br /&gt;
&lt;br /&gt;
CREATE OR REPLACE FUNCTION print_indexes_info(OUT index_name varchar, OUT indexsize varchar, &lt;br /&gt;
                                              OUT leaf_pages integer, OUT deleted_pages integer, &lt;br /&gt;
                                              OUT leaf_fragmentation double precision, OUT avg_leaf_density double precision) &lt;br /&gt;
RETURNS SETOF RECORD AS $$&lt;br /&gt;
DECLARE r record; o pgstatindex_type;&lt;br /&gt;
BEGIN&lt;br /&gt;
  FOR r IN SELECT schemaname || &amp;#039;.&amp;#039; || indexname AS indexname FROM pg_indexes LOOP&lt;br /&gt;
    BEGIN&lt;br /&gt;
      o := pgstatindex(r.indexname);&lt;br /&gt;
      index_name := r.indexname; &lt;br /&gt;
      indexsize := pg_size_pretty(o.index_size); deleted_pages := o.deleted_pages;&lt;br /&gt;
      leaf_pages := o.leaf_pages; avg_leaf_density := o.avg_leaf_density;&lt;br /&gt;
      leaf_fragmentation := o.leaf_fragmentation; &lt;br /&gt;
  &lt;br /&gt;
      RETURN NEXT;&lt;br /&gt;
    EXCEPTION WHEN OTHERS THEN&lt;br /&gt;
      indexsize := NULL; deleted_pages := NULL; leaf_fragmentation := NULL;&lt;br /&gt;
      index_name := r.indexname;&lt;br /&gt;
      RETURN NEXT;&lt;br /&gt;
    END;&lt;br /&gt;
  END LOOP;&lt;br /&gt;
  RETURN;&lt;br /&gt;
END; $$ LANGUAGE plpgsql;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Diacritic removing ==&lt;br /&gt;
We can simply remove diacritic with calling &amp;#039;&amp;#039;to_ascii&amp;#039;&amp;#039; function. There is small problem. This function doesn&amp;#039;t support UTF8 encoding, so we have to use &amp;#039;&amp;#039;convert&amp;#039;&amp;#039; function. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
postgres=# select to_ascii(convert(&amp;#039;Příliš žlutý kůň&amp;#039; using utf8_to_iso_8859_2),&amp;#039;latin2&amp;#039;);&lt;br /&gt;
     to_ascii     &lt;br /&gt;
------------------&lt;br /&gt;
 Prilis zluty kun&lt;br /&gt;
(1 row)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Note for 7.4, name of conversion is &amp;quot;utf_8_to_iso_8859_2&amp;quot;. For 8.3 and 9.0 we have to use small workaround:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE FUNCTION to_ascii(bytea, name) &lt;br /&gt;
RETURNS text AS &amp;#039;to_ascii_encname&amp;#039; LANGUAGE internal;&lt;br /&gt;
&lt;br /&gt;
SELECT to_ascii(convert_to(&amp;#039;Příliš žlutý kůň&amp;#039;, &amp;#039;latin2&amp;#039;),&amp;#039;latin2&amp;#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Predicate IN optimalization ==&lt;br /&gt;
I found note [http://archives.postgresql.org/pgsql-performance/ pg_performance] about optimalization possibility of IN predicate when list of values is longer than eighty numbers. For longer list is better create constant subqueries with using multi values:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
SELECT * &lt;br /&gt;
   FROM tab &lt;br /&gt;
  WHERE x IN (1,2,3,..n); -- n &amp;gt; 70&lt;br /&gt;
&lt;br /&gt;
-- faster case&lt;br /&gt;
SELECT * &lt;br /&gt;
   FROM tab&lt;br /&gt;
  WHERE x IN (VALUES(10),(20));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Using VALUES is faster for bigger number of items, so don&amp;#039;t use it for small set of values.&lt;br /&gt;
&lt;br /&gt;
== BAR code validation ==&lt;br /&gt;
SQL Guru Joe Celko described  [http://www.dbazine.com/ofinterest/oi-articles/celko36 DBAzinu] interesting technique for BAR code validation with multi values. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE FUNCTION barcode_sum(text)&lt;br /&gt;
RETURNS bigint AS &lt;br /&gt;
$$&lt;br /&gt;
  SELECT ABS(SUM(CAST(SUBSTRING($1 FROM Weights.seq FOR 1) AS INTEGER) * Weights.wgt))&lt;br /&gt;
     FROM (VALUES (CAST(1 AS INTEGER), CAST(-1 AS INTEGER)),&lt;br /&gt;
                  (2, +1), (3, -1), (4, +1), (5, -1), &lt;br /&gt;
                  (6, +1), (7, -1), (8, +1), (9, -1), (10, +1), &lt;br /&gt;
                  (11,-1), (12, +1)) AS weights(seq, wgt)&lt;br /&gt;
$$ LANGUAGE sql;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Our function can be used like:&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE TABLE products(&lt;br /&gt;
  barcode char(13) NOT NULL&lt;br /&gt;
  CONSTRAINT valid_checkdigit&lt;br /&gt;
    check (MOD(barcode_sum(barcode),10) = CAST(substring(barcode from 13 for 1) AS integer))&lt;br /&gt;
  CONSTRAINT all_numeric_checkdigit CHECK(barcode NOT SIMILAR TO &amp;#039;%[^0-9]%&amp;#039;)&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
INSERT INTO products VALUES(&amp;#039;2837232811227&amp;#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This trick is based on behave of char type. Shorted text than 13 chars is filled with spaces, so test NOT SIMILAR ensure correct number of digits.&lt;br /&gt;
&lt;br /&gt;
== Using hash functions for ensuring uniqueness of texts ==&lt;br /&gt;
Classic unique index isn&amp;#039;t well solution for longer texts. It&amp;#039;s natural using some of hash functions. Output of these functions is 32 char long text. This is hexadecimal number, so we can cut it by casting to bytea type. Attention: hash doesn&amp;#039;t ensure 100% uniqueness.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
root=# CREATE TABLE test(a text);&lt;br /&gt;
CREATE TABLE&lt;br /&gt;
&lt;br /&gt;
root=# CREATE UNIQUE INDEX uidx ON test((decode(md5(a),&amp;#039;hex&amp;#039;)));&lt;br /&gt;
CREATE INDEX&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
root=# CREATE UNIQUE INDEX uidx ON test((decode(md5(lower(replace(a,&amp;#039; &amp;#039;,&amp;#039;&amp;#039;))),&amp;#039;hex&amp;#039;)));&lt;br /&gt;
CREATE INDEX&lt;br /&gt;
root=# INSERT INTO test VALUES(&amp;#039;příliš žluťoučký kůň&amp;#039;);&lt;br /&gt;
INSERT 0 1&lt;br /&gt;
root=# INSERT INTO test VALUES(&amp;#039;příliš žluťoučký kůň &amp;#039;);&lt;br /&gt;
ERROR:  duplicate key violates unique constraint &amp;quot;uidx&amp;quot;&lt;br /&gt;
root=# INSERT INTO test VALUES(&amp;#039;Příliš žluťoučký kůň&amp;#039;);&lt;br /&gt;
ERROR:  duplicate key violates unique constraint &amp;quot;uidx&amp;quot;&lt;br /&gt;
&lt;br /&gt;
-- less risk, using two hash functions&lt;br /&gt;
-- CREATE UNIQUE INDEX uidx ON TEST((decode(md5(a),&amp;#039;hex&amp;#039;)),(hashtext(a)));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Getting OID of function ==&lt;br /&gt;
Every database object in PostgreSQL has Oid - unique identifier. We can find it in database system&amp;#039;s tables or we can get it with casting:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE OR REPLACE FUNCTION a(integer, varchar) RETURNS void AS &lt;br /&gt;
$$&lt;br /&gt;
  BEGIN&lt;br /&gt;
  END;&lt;br /&gt;
$$ LANGUAGE plpgsql;&lt;br /&gt;
&lt;br /&gt;
root=# SELECT &amp;#039;a&amp;#039;::regproc::int;&lt;br /&gt;
ERROR:  more than one function named &amp;quot;a&amp;quot;&lt;br /&gt;
root=# SELECT &amp;#039;a(integer, varchar)&amp;#039;::regprocedure::int;&lt;br /&gt;
 int4  &lt;br /&gt;
-------&lt;br /&gt;
 57507&lt;br /&gt;
(1 row)&lt;br /&gt;
&lt;br /&gt;
root=# SELECT 57507::regprocedure;&lt;br /&gt;
         regprocedure         &lt;br /&gt;
------------------------------&lt;br /&gt;
 a(integer,character varying)&lt;br /&gt;
(1 row)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Table&amp;#039;s Oid we could to get with cast to regclass type:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
SELECT &amp;#039;oo&amp;#039;::regclass::int;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== General array sort ==&lt;br /&gt;
Very nice exhibition of PostgreSQL&amp;#039;s features is general procedure for sorting by David Fetter.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE OR REPLACE FUNCTION array_sort (ANYARRAY)&lt;br /&gt;
RETURNS ANYARRAY LANGUAGE SQL&lt;br /&gt;
AS $$&lt;br /&gt;
SELECT ARRAY(&lt;br /&gt;
    SELECT $1[s.i] AS &amp;quot;foo&amp;quot;&lt;br /&gt;
    FROM&lt;br /&gt;
        generate_series(array_lower($1,1), array_upper($1,1)) AS s(i)&lt;br /&gt;
    ORDER BY foo&lt;br /&gt;
);&lt;br /&gt;
$$;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
little bit faster shorter modernised variant:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE OR REPLACE FUNCTION array_sort (ANYARRAY)&lt;br /&gt;
RETURNS ANYARRAY LANGUAGE SQL&lt;br /&gt;
AS $$&lt;br /&gt;
SELECT ARRAY(SELECT unnest($1) ORDER BY 1)&lt;br /&gt;
$$&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
or &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
-- by Craig Ringer&lt;br /&gt;
CREATE OR REPLACE FUNCTION array_sort (ANYARRAY)&lt;br /&gt;
RETURNS ANYARRAY LANGUAGE SQL&lt;br /&gt;
AS $$&lt;br /&gt;
SELECT array_agg(x ORDER BY x) FROM unnest($1) x;&lt;br /&gt;
$$&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
If you need really fast sort of int arrays, then you have to look on  intarray contrib module.&lt;br /&gt;
&lt;br /&gt;
== Array transformation in PL/Perl ==&lt;br /&gt;
PL/Perl doesn&amp;#039;t support OUT arrays. We can use undocumented procedure _plperl_to_pg_array:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE OR REPLACE FUNCTION foo(OUT a integer[], OUT b integer[]) AS $$&lt;br /&gt;
  return { a=&amp;gt; _plperl_to_pg_array([1,2,3]),&lt;br /&gt;
           b=&amp;gt; _plperl_to_pg_array([4,5,6]) };&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== UPC code validation ==&lt;br /&gt;
This is sample of validation of code with control number in Perl (PL/perl). I used this function for compare with PL/pgSQL and C languages. Perl is about 20% faster, but C is 10 times faster than Perl.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE OR REPLACE FUNCTION ean_perl (&lt;br /&gt;
    TEXT&lt;br /&gt;
) RETURNS boolean AS $_$&lt;br /&gt;
    my $ean = length $_[0] == 12 ? &amp;quot;0$_[0]&amp;quot; : $_[0];&lt;br /&gt;
    # Make sure we really have an EAN.&lt;br /&gt;
    return &amp;#039;false&amp;#039; unless $ean =~ /^\d{13}$/;&lt;br /&gt;
    my @nums = split &amp;#039;&amp;#039;, $ean;&lt;br /&gt;
    return 10 - (&lt;br /&gt;
        # Sum even numerals.&lt;br /&gt;
        (   (   $nums[1] + $nums[3] + $nums[5] + $nums[7] + $nums[9]&lt;br /&gt;
                    + $nums[11]&lt;br /&gt;
            ) * 3 # Multiply total by 3.&lt;br /&gt;
        # Add odd numerals except for checksum (12).&lt;br /&gt;
        ) + $nums[0] + $nums[2] + $nums[4] + $nums[6] + $nums[8] + $nums[10]&lt;br /&gt;
    # Compare to the checksum.&lt;br /&gt;
    ) % 10 == $nums[12] ? &amp;#039;true&amp;#039; : &amp;#039;false&amp;#039;;&lt;br /&gt;
$_$ LANGUAGE plperl immutable;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
I found interesting solution by Joe Celko in [http://www.dbazine.com/ofinterest/oi-articles/celko36 article]. After rewriting to PostgreSQL:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
SELECT (&lt;br /&gt;
   SELECT mod(&lt;br /&gt;
      sum(&lt;br /&gt;
         CAST(substring(&amp;#039;2837232811227&amp;#039; from s.seq for 1) AS integer) * &lt;br /&gt;
         CASE mod(s.seq,2) WHEN 0 THEN 1 ELSE -1 END),&lt;br /&gt;
      10) FROM generate_series(1,12) as s(seq)) =&lt;br /&gt;
   CAST(substring(&amp;#039;2837232811227&amp;#039; FROM 13 FOR 1) AS integer);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Show only every n row ==&lt;br /&gt;
When we would to skip some rows from table, we should to use temporary sequences and modulo function:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  CREATE TEMP SEQUENCE number; &lt;br /&gt;
  SELECT * FROM ( SELECT *, nextval(&amp;#039;number&amp;#039;) AS number FROM datatable ) foo &lt;br /&gt;
    WHERE foo.number % 5 = 0;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With 8.4 this can be done without the use of a temporary sequence&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  SELECT * FROM ( SELECT *, row_number() over () AS number FROM datatable ) foo &lt;br /&gt;
    WHERE foo.number % 5 = 0;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Array to table ==&lt;br /&gt;
We can simply unpack array with generate_series function:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 SELECT (ARRAY[1,3,4,6,7])[idx.n] FROM generate_series(1,7) idx(n);&lt;br /&gt;
&lt;br /&gt;
CREATE OR REPLACE FUNCTION unpack(anyarray)&lt;br /&gt;
RETURNS SETOF anyelement AS $$ &lt;br /&gt;
SELECT $1[i] &lt;br /&gt;
   FROM generate_series(array_lower($1,1), &lt;br /&gt;
                        array_upper($1,1)) g(i);&lt;br /&gt;
$$ LANGUAGE sql STRICT IMMUTABLE;&lt;br /&gt;
&lt;br /&gt;
postgres=# select unpack(array[&amp;#039;a&amp;#039;,&amp;#039;b&amp;#039;,&amp;#039;c&amp;#039;]);&lt;br /&gt;
 unpack &lt;br /&gt;
--------&lt;br /&gt;
 a&lt;br /&gt;
 b&lt;br /&gt;
 c&lt;br /&gt;
(3 rows)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
With 8.4, we can use the unnest()-function:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
test=*# select unnest(array[&amp;#039;a&amp;#039;,&amp;#039;b&amp;#039;,&amp;#039;c&amp;#039;]);&lt;br /&gt;
 unnest&lt;br /&gt;
--------&lt;br /&gt;
 a&lt;br /&gt;
 b&lt;br /&gt;
 c&lt;br /&gt;
(3 rows)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Additionally, we can extend the unnest() function to return one row with a default value if the array is empty or null:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
create or replace function unnest(anyarray, anyelement)&lt;br /&gt;
returns setof anyelement as $$&lt;br /&gt;
select unnest(case when array_upper($1, 1) &amp;gt; 0 then $1 else array[$2] end);&lt;br /&gt;
$$ language sql immutable;&lt;br /&gt;
&lt;br /&gt;
gp=&amp;gt; create temp table foo (x text[], y int) distributed by (y);&lt;br /&gt;
CREATE TABLE&lt;br /&gt;
gp=&amp;gt; insert into foo values&lt;br /&gt;
(array[&amp;#039;a&amp;#039;,&amp;#039;b&amp;#039;,&amp;#039;c&amp;#039;], 1),&lt;br /&gt;
(null, 2),&lt;br /&gt;
(&amp;#039;{}&amp;#039;::text[],3);&lt;br /&gt;
INSERT 0 3&lt;br /&gt;
&lt;br /&gt;
gp=&amp;gt; select *,array_upper(x,1) as size from foo;&lt;br /&gt;
    x    | y | size&lt;br /&gt;
---------+---+------&lt;br /&gt;
 {}      | 3 |&lt;br /&gt;
         | 2 |&lt;br /&gt;
 {a,b,c} | 1 |    3&lt;br /&gt;
(3 rows)&lt;br /&gt;
&lt;br /&gt;
gp=&amp;gt; select unnest(x),y from foo;&lt;br /&gt;
 unnest | y&lt;br /&gt;
--------+---&lt;br /&gt;
 a      | 1&lt;br /&gt;
 b      | 1&lt;br /&gt;
 c      | 1&lt;br /&gt;
(3 rows)&lt;br /&gt;
&lt;br /&gt;
gp=&amp;gt; select unnest(x,&amp;#039;empty&amp;#039;),y from foo;&lt;br /&gt;
 unnest | y&lt;br /&gt;
--------+---&lt;br /&gt;
 empty  | 3&lt;br /&gt;
 empty  | 2&lt;br /&gt;
 a      | 1&lt;br /&gt;
 b      | 1&lt;br /&gt;
 c      | 1&lt;br /&gt;
(5 rows)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== LIKE optimalization ==&lt;br /&gt;
PostgreSQL use index for LIKE only when:&lt;br /&gt;
* pattern doesn&amp;#039;t start with % and _ symbols,&lt;br /&gt;
* database cluster is initialized with C locale.&lt;br /&gt;
&lt;br /&gt;
Last constraint we can pass by special index:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE INDEX like_index ON people(surname varchar_pattern_ops);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
When we work wit pattern like %some (for internet domain searching) we should to use trick:&lt;br /&gt;
* create functional index on mirrored values.&lt;br /&gt;
* search with mirrored pattern&lt;br /&gt;
&lt;br /&gt;
PostgreSQL doesn&amp;#039;t allow reverse (mirror) function. We can use rvrs function from [[Oracle_functionality_%28en%29#PLVstr string and PLVchr char functions|Orafunc]] package or we can use PL/Perl.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE OR REPLACE FUNCTION reverse(varchar) RETURNS varchar AS $$ &lt;br /&gt;
  $reversed = reverse $_[0]; &lt;br /&gt;
  return $reversed; &lt;br /&gt;
$$ LANGUAGE plperlu IMMUTABLE;&lt;br /&gt;
&lt;br /&gt;
CREATE INDEX rev_email ON users( (reverse(email) ) varchar_pattern_ops );&lt;br /&gt;
SELECT * FROM _users WHERE reverse(email) LIKE reverse (&amp;#039;%.cz&amp;#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Without plperl:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE OR REPLACE FUNCTION reverse(TEXT) RETURNS TEXT AS $$&lt;br /&gt;
 SELECT &lt;br /&gt;
    array_to_string( &lt;br /&gt;
      ARRAY&lt;br /&gt;
        ( SELECT substring($1, s.i,1) FROM generate_series(length($1), 1, -1) AS s(i) ), &lt;br /&gt;
      &amp;#039;&amp;#039;);&lt;br /&gt;
$$ LANGUAGE SQL IMMUTABLE;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: PostgreSQL 9.1 has buildin function for reverse string.&lt;br /&gt;
&lt;br /&gt;
== Deferred constraints ==&lt;br /&gt;
PostgreSQL run all constrains immediately. SQL know deferred constraints, that allows delay tests to time closely to commit. PostgreSQL hasn&amp;#039;t full support. You can deferred only referential integrity constraints. But PostgreSQL supports undocumented deferred triggers, so we should use it. Syntax is similar to trigger definition:&lt;br /&gt;
&lt;br /&gt;
   CREATE CONSTRAINT TRIGGER sc&lt;br /&gt;
   AFTER INSERT &lt;br /&gt;
   ON fieldtrip_students&lt;br /&gt;
   INITIALLY DEFERRED&lt;br /&gt;
   FOR EACH ROW EXECUTE PROCEDURE field_trip();&lt;br /&gt;
&lt;br /&gt;
== Removing of duplicate rows ==&lt;br /&gt;
Any table created without parameter &amp;#039;&amp;#039;WITHOUT OIDS&amp;#039;&amp;#039; has row unique identifier OID. We can use it for removing duplicate rows:&lt;br /&gt;
&lt;br /&gt;
  DELETE FROM people WHERE &lt;br /&gt;
    NOT oid IN (SELECT MIN(oid) FROM people GROUP BY name, surname);&lt;br /&gt;
&lt;br /&gt;
But new tables are created with this parameter usually. So we can&amp;#039;t use OID. Every PostgreSQL row has another unique identifier - ctid. For ctid type we have not defined any aggregate functions, we have to use correlated subquery. I found this trick in Greg Mullane presentation.&lt;br /&gt;
&lt;br /&gt;
  DELETE FROM lidi WHERE&lt;br /&gt;
    NOT (ctid, tableoid) = (SELECT (ctid, tableoid) FROM lidi l WHERE &lt;br /&gt;
      prijmeni=l.prijmeni AND jmeno=l.jmeno LIMIT 1);&lt;br /&gt;
&lt;br /&gt;
== Select first n rows from group ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   SELECT * FROM people WHERE id IN (&lt;br /&gt;
     SELECT id FROM people s &lt;br /&gt;
       WHERE people.category = s.category &lt;br /&gt;
       ORDER BY age LIMIT 2) &lt;br /&gt;
     ORDER BY category, age;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Other solution without correlated subquery is   [http://www.xaprb.com/blog/2007/03/14/how-to-find-the-max-row-per-group-in-sql-without-subqueries/ SELF JOIN]. I have to look on problem from different perspective. I have to ask on persons for which only one person in group is older.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
SELECT s1.* &lt;br /&gt;
   FROM people s1&lt;br /&gt;
        LEFT JOIN&lt;br /&gt;
        people s2&lt;br /&gt;
        ON s1.category = s2.category AND s1.age &amp;lt; s2.age&lt;br /&gt;
  GROUP BY s1.id, s1.category&lt;br /&gt;
  HAVING COUNT(s2.id) &amp;lt;= 1&lt;br /&gt;
  ORDER BY s1.category, COUNT(s2.id);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Cast to varchar ==&lt;br /&gt;
Every data type in PostreSQL has input and output functions. We can use these functions for support casting to varchar (if we miss it). Don&amp;#039;t use this trick on PostgreSQL 8.3 and higher&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  testdb011=# SELECT &amp;#039;(1,1)&amp;#039;::point::varchar;&lt;br /&gt;
  ERROR:  cannot cast type point to character varying&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
but&lt;br /&gt;
&lt;br /&gt;
  testdb011=# SELECT textin(point_out(&amp;#039;(1,1)&amp;#039;::point))::varchar;&lt;br /&gt;
   textin&lt;br /&gt;
  --------&lt;br /&gt;
   (1,1)&lt;br /&gt;
  (1 row)&lt;br /&gt;
&lt;br /&gt;
== Create Aggregates ==&lt;br /&gt;
I needed to be able to do a fast &lt;br /&gt;
&amp;lt;pre&amp;gt;SELECT count(*) FROM sometable WHERE field=&amp;#039;x&amp;#039;&amp;lt;/pre&amp;gt; &lt;br /&gt;
instead of a simple &amp;lt;pre&amp;gt;SELECT count (*) FROM sometable&amp;lt;/pre&amp;gt;  &lt;br /&gt;
Both of the above queries initiate sequential scans on a table (which takes too much time).  Here is what I created:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;CREATE TABLE aggregate AS &lt;br /&gt;
     (SELECT result,count(result) AS count FROM original_table &lt;br /&gt;
      GROUP BY result ORDER BY count DESC);&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Further, my data contains many one-offs which I don&amp;#039;t want to put into my aggregate table (I assume that if a value isn&amp;#039;t in my aggregate table that it&amp;#039;s too small to be useful)...  here is what I do to leave those values out:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;CREATE TABLE aggregate AS &lt;br /&gt;
     (SELECT * FROM &lt;br /&gt;
          (SELECT result,count(result) AS count FROM original_table &lt;br /&gt;
           GROUP BY result ORDER BY count DESC) &lt;br /&gt;
     AS countquery WHERE count &amp;gt; 3);&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method is fast enough.  Because I have an index on the &amp;quot;result&amp;quot; field, It runs in under a minute against 3 million records.  It takes approximately the same amount of time to generate counts for all the result values as it does to count the total number of records.&lt;br /&gt;
&lt;br /&gt;
Now I can do a simple (very fast) query to get a count:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;SELECT * FROM aggregate where result=&amp;#039;xxxxx&amp;#039;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[PostgreSQL SQL Tricks II|Newer tricks]]&lt;br /&gt;
-----&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
&amp;lt;htmlet&amp;gt;wide-banner&amp;lt;/htmlet&amp;gt;&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;/div&gt;</summary>
		<author><name>imported&gt;Pavel</name></author>
	</entry>
</feed>