<?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/"
	>

<channel>
	<title>Разработка &#187; PostgreSQL</title>
	<atom:link href="http://www.job-blog.bullgare.ru/tag/postgresql/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.job-blog.bullgare.ru</link>
	<description>о программировании и работе</description>
	<lastBuildDate>Fri, 03 Feb 2012 09:42:30 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.4</generator>
		<item>
		<title>PostgreSQL &#8211; генерирование повторяющихся значений для неупорядоченных данных</title>
		<link>http://www.job-blog.bullgare.ru/2011/03/postgresql-%d0%b3%d0%b5%d0%bd%d0%b5%d1%80%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d0%b5-%d0%bf%d0%be%d0%b2%d1%82%d0%be%d1%80%d1%8f%d1%8e%d1%89%d0%b8%d1%85%d1%81%d1%8f-%d0%b7%d0%bd%d0%b0%d1%87/</link>
		<comments>http://www.job-blog.bullgare.ru/2011/03/postgresql-%d0%b3%d0%b5%d0%bd%d0%b5%d1%80%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d0%b5-%d0%bf%d0%be%d0%b2%d1%82%d0%be%d1%80%d1%8f%d1%8e%d1%89%d0%b8%d1%85%d1%81%d1%8f-%d0%b7%d0%bd%d0%b0%d1%87/#comments</comments>
		<pubDate>Thu, 17 Mar 2011 19:52:15 +0000</pubDate>
		<dc:creator>bullgare</dc:creator>
				<category><![CDATA[Базы данных]]></category>
		<category><![CDATA[INNER JOIN]]></category>
		<category><![CDATA[JOIN]]></category>
		<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[self-join]]></category>
		<category><![CDATA[sql]]></category>

		<guid isPermaLink="false">http://www.job-blog.bullgare.ru/?p=983</guid>
		<description><![CDATA[Была задача: есть некоторое количество заказов, которым оказались не назначены менеджеры, есть 3 менеджера, между которыми нужно распределить эти заказы равномерно. Легче всё сделать на php или python, но задача была сделать всё при помощи SQL-запроса без хранимых процедур и желательно одним запросом. Пришлось немного подумать &#8211; в итоге сделал следующее (пусть id менеджеров 111, [...]]]></description>
			<content:encoded><![CDATA[<p>Была задача: есть некоторое количество заказов, которым оказались не назначены менеджеры, есть 3 менеджера, между которыми нужно распределить эти заказы равномерно. Легче всё сделать на php или python, но задача была сделать всё при помощи SQL-запроса без хранимых процедур и желательно одним запросом.<br />
<span id="more-983"></span><br />
Пришлось немного подумать &#8211; в итоге сделал следующее (пусть id менеджеров 111, 222 и 333):</p>
<pre class="code">
SELECT
	main.*,
	CASE
		WHEN mod(number, 3) = 1 THEN 111
		WHEN mod(number, 3) = 2 THEN 222
		ELSE 333
	END AS manager
FROM
(
	SELECT
		m1.id,
		COUNT(m2.id) AS number
	FROM
		main_training AS m1
	INNER JOIN
		main_training AS m2
	ON
		m1.id >= m2.id
	WHERE
		m1.id IN (1, 2, 3, 5, 15, 20, 40) AND
		m2.id IN (1, 2, 3, 5, 15, 20, 40)
	GROUP BY
		m1.id
	ORDER by
		number
) AS main;
</pre>
<p>Основная проблема &#8211; пронумеровать последовательно все подпадающие под выборку строки. Делается это при помощи <strong>INNER JOIN</strong> на ту же таблицу. Условие <strong>ON m1.id >= m2.id</strong> и группировка по <strong>m1.id</strong> дают в <strong>COUNT(m2.id)</strong> порядковый номер (таблица <strong>m2</strong> джойнится к <strong>m1</strong> столько раз, сколько в <strong>m2</strong> есть удовлетворяющих условию строк с <strong>id</strong> меньше <strong>m1.id</strong>).<br />
После этого на основе модуля от деления порядкового номера на 3 определяем менеджера, которого назначим для сделки.</p>
<p><strong>UPD:</strong><br />
Нашёл (вспомнил) формулу для аггрегирования значений:</p>
<blockquote><p>formula for running aggregates</p></blockquote>
<pre class="code">
SELECT
	x1.key,
	x1.some_column,
	AGGREGATE_FN(x2.some_column) AS running_aggregate
FROM x AS x1
INNER JOIN x AS x2
ON x1.key >= x2.key
GROUP BY x1.key;
</pre>
<p>Формула взята из доклада <em>Jay Pipes</em>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.job-blog.bullgare.ru/2011/03/postgresql-%d0%b3%d0%b5%d0%bd%d0%b5%d1%80%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d0%b5-%d0%bf%d0%be%d0%b2%d1%82%d0%be%d1%80%d1%8f%d1%8e%d1%89%d0%b8%d1%85%d1%81%d1%8f-%d0%b7%d0%bd%d0%b0%d1%87/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Postgresql-аналоги myslq-евской &#171;ON DUPLICATE KEY UPDATE&#187;</title>
		<link>http://www.job-blog.bullgare.ru/2010/08/postgresql-%d0%b0%d0%bd%d0%b0%d0%bb%d0%be%d0%b3%d0%b8-myslq-%d0%b5%d0%b2%d1%81%d0%ba%d0%be%d0%b9-on-duplicate-key-update/</link>
		<comments>http://www.job-blog.bullgare.ru/2010/08/postgresql-%d0%b0%d0%bd%d0%b0%d0%bb%d0%be%d0%b3%d0%b8-myslq-%d0%b5%d0%b2%d1%81%d0%ba%d0%be%d0%b9-on-duplicate-key-update/#comments</comments>
		<pubDate>Thu, 05 Aug 2010 20:46:17 +0000</pubDate>
		<dc:creator>bullgare</dc:creator>
				<category><![CDATA[Базы данных]]></category>
		<category><![CDATA[ON DUPLICATE KEY UPDATE]]></category>
		<category><![CDATA[PostgreSQL]]></category>

		<guid isPermaLink="false">http://www.job-blog.bullgare.ru/?p=750</guid>
		<description><![CDATA[В mysql есть очень удобная конструкция INSERT INTO table (columns) VALUES (values) ON DUPLICATE KEY UPDATE column1=value1, column2=value2 Это очень удобно тогда, когда нужно вставить только те данные, которых в таблице нет (при этом в апдейте указывается уже существующее значение поля). В postgresql такого, к сожалению, нет. Но подобное поведение можно сэмулировать несколькими способами. Два [...]]]></description>
			<content:encoded><![CDATA[<p>В mysql есть очень удобная конструкция</p>
<pre class="code">
INSERT INTO table (columns) VALUES (values) ON DUPLICATE KEY UPDATE column1=value1, column2=value2
</pre>
<p>Это очень удобно тогда, когда нужно вставить только те данные, которых в таблице нет (при этом в апдейте указывается уже существующее значение поля).<br />
В postgresql такого, к сожалению, нет.<br />
Но подобное поведение можно сэмулировать несколькими способами. Два самых интересных:<br />
<span id="more-750"></span><br />
1. Создать правило</p>
<pre class="code">
 CREATE OR REPLACE RULE "insert_ignore_mytable" AS
     ON INSERT TO
          "mytable"
     WHERE EXISTS
          ( SELECT 1 FROM "mytable" WHERE id = NEW.id )
     DO INSTEAD NOTHING;
</pre>
<p>Этот способ удобен тем, что его нужно написать только один раз, и при любой вставке данных с уже существующим ключом выполнение работы продолжится, а самой вставки не произойдёт. Неудобен тем, что выполняется селект, да и вообще это по сути триггер, делающий лишнюю работу до вставки.<br />
2. Обработать исключение</p>
<pre class="code">
BEGIN
     INSERT INTO db(a,b) VALUES (key, data);
EXCEPTION WHEN unique_violation THEN
      -- смотрим, улыбаемся
END;
</pre>
<p>Удобен тем, что не надо затрагивать таблицу, т.е. другие запросы пойдут как обычно. Неудобен тем, что его приходится использовать внутри хранимых процедур.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.job-blog.bullgare.ru/2010/08/postgresql-%d0%b0%d0%bd%d0%b0%d0%bb%d0%be%d0%b3%d0%b8-myslq-%d0%b5%d0%b2%d1%81%d0%ba%d0%be%d0%b9-on-duplicate-key-update/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Как сдампить базу данных PostgreSql в локальную базу через командную строку в Windows</title>
		<link>http://www.job-blog.bullgare.ru/2010/05/%d0%ba%d0%b0%d0%ba-%d1%81%d0%b4%d0%b0%d0%bc%d0%bf%d0%b8%d1%82%d1%8c-%d0%b1%d0%b0%d0%b7%d1%83-%d0%b4%d0%b0%d0%bd%d0%bd%d1%8b%d1%85-postgresql-%d0%b2-%d0%bb%d0%be%d0%ba%d0%b0%d0%bb%d1%8c%d0%bd%d1%83/</link>
		<comments>http://www.job-blog.bullgare.ru/2010/05/%d0%ba%d0%b0%d0%ba-%d1%81%d0%b4%d0%b0%d0%bc%d0%bf%d0%b8%d1%82%d1%8c-%d0%b1%d0%b0%d0%b7%d1%83-%d0%b4%d0%b0%d0%bd%d0%bd%d1%8b%d1%85-postgresql-%d0%b2-%d0%bb%d0%be%d0%ba%d0%b0%d0%bb%d1%8c%d0%bd%d1%83/#comments</comments>
		<pubDate>Thu, 20 May 2010 12:06:45 +0000</pubDate>
		<dc:creator>bullgare</dc:creator>
				<category><![CDATA[Без рубрики]]></category>
		<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[windows]]></category>
		<category><![CDATA[командная строка]]></category>

		<guid isPermaLink="false">http://www.job-blog.bullgare.ru/?p=730</guid>
		<description><![CDATA[pg_dump.exe -U [user_remote] -h [host_remote] [database_name] &#124; psql -U [user_local] -d [local_db_name]]]></description>
			<content:encoded><![CDATA[<pre class="code">
pg_dump.exe -U [user_remote] -h [host_remote] [database_name] | psql -U [user_local] -d [local_db_name]
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.job-blog.bullgare.ru/2010/05/%d0%ba%d0%b0%d0%ba-%d1%81%d0%b4%d0%b0%d0%bc%d0%bf%d0%b8%d1%82%d1%8c-%d0%b1%d0%b0%d0%b7%d1%83-%d0%b4%d0%b0%d0%bd%d0%bd%d1%8b%d1%85-postgresql-%d0%b2-%d0%bb%d0%be%d0%ba%d0%b0%d0%bb%d1%8c%d0%bd%d1%83/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PostgreSQL EXPLAIN</title>
		<link>http://www.job-blog.bullgare.ru/2010/05/postgresql-explain/</link>
		<comments>http://www.job-blog.bullgare.ru/2010/05/postgresql-explain/#comments</comments>
		<pubDate>Sun, 16 May 2010 16:22:39 +0000</pubDate>
		<dc:creator>bullgare</dc:creator>
				<category><![CDATA[Базы данных]]></category>
		<category><![CDATA[Программирование]]></category>
		<category><![CDATA[EXPLAIN]]></category>
		<category><![CDATA[PostgreSQL]]></category>

		<guid isPermaLink="false">http://www.job-blog.bullgare.ru/?p=691</guid>
		<description><![CDATA[При работе с MySql&#8217;овским EXPLAIN&#8217;ОМ всё просто (а может просто привычно). Postgres предоставляет больше информации, но с ним сложнее разобраться. Далее &#8211; вольный перевод статьи про PostgreSQL EXPLAIN (ссылка на оригинал в конце статьи). Разберём небольшой пример: EXPLAIN SELECT * FROM CUSTOMER; QUERY PLAN -------------------------------------------------------------- Seq Scan on customer (cost=0.00..60.48 rows=2048 width=107) (1 row) Это [...]]]></description>
			<content:encoded><![CDATA[<p>При работе с MySql&#8217;овским EXPLAIN&#8217;ОМ всё просто (а может просто привычно).<br />
Postgres предоставляет больше информации, но с ним сложнее разобраться.<br />
Далее &#8211; вольный перевод статьи про PostgreSQL EXPLAIN (ссылка на оригинал в конце статьи).<br />
<span id="more-691"></span><br />
Разберём небольшой пример:</p>
<pre class="code">
EXPLAIN SELECT * FROM CUSTOMER;
                          QUERY PLAN
--------------------------------------------------------------
 Seq Scan on customer  (cost=0.00..60.48 rows=2048 width=107)
(1 row)
</pre>
<p>Это означает, что оптимизатор решил произвести последовательное чтение записей из таблицы. Он также оценил,что первая строка будет выведена через 0.00, а все &#8211; через 60.48. При этом будет возвращено 2048 строк средней длиной 107 байт.<br />
EXPLAIN не запускает сам запрос, это лишь оценка.  Для получения точных результатов нужно запустить EXPLAIN ANALYZE:</p>
<pre class="code">
EXPLAIN ANALYZE SELECT * FROM CUSTOMER;
                                                 QUERY PLAN
------------------------------------------------------------------------------------------------------------
 Seq Scan on customer  (cost=0.00..60.48 rows=2048 width=107) (actual time=0.033..6.577 rows=2048 loops=1)
 Total runtime: 7.924 ms
(2 rows)
</pre>
<p>Теперь появился второй набор данных: реальное время, затраченное на выполнение последовательного чтения, реальное число строк и количество повторов чтения этих строк (про повторы позже). Также появилась информация о полном времени выполнения запроса.<br />
Можно заметить, что числа в реальном времени не очень то соответствуют предсказанным. Это потому, что оценка измеряется не во времени, а в специальных единицах. Одна единица равна времени, затраченному на последовательное чтение одной страницы с диска.<br />
Но это слишком скучный запрос, перейдём к более интересным.</p>
<pre class="code">
EXPLAIN SELECT * FROM customer ORDER BY city;
                             QUERY PLAN
--------------------------------------------------------------------
 Sort  (cost=173.12..178.24 rows=2048 width=107)
   Sort Key: city
   ->  Seq Scan on customer  (cost=0.00..60.48 rows=2048 width=107)
</pre>
<p>Теперь план запроса включает 2 шага: сортировку и последовательное чтение. Хотя это и кажется нелогичным, запрос выполняется снизу вверх (если смотреть на план), т.е. результат последовательного чтения подаётся на вход оператору сортировки (оператор сортировки использует результаты последовательного чтения как входные данные).<br />
Если взглянуть на шаг сортировки, можно заметить, что оптимизатор говорит, по какому полю проводится сортировка (&laquo;Sort Key&raquo;). Разные виды обработки могут выводить дополнительную информацию вроде этой. Также стоит обратить внимание на то, что затраты на вывод первой строки оператором сортировки очень велики, они почти равны затратам на вывод всех столбцов. Это из-за того, что сначала он должен отсортировать все строки, что и занимает большую часть времени.<br />
Если шагов выполнения запроса несколько, то показываемые затраты на шаг содержат не только затраты на выполнение этого шага, но и всех шагов ниже. В нашем примере затраты на шаг сортировки равны 173.12-60.48 для вывода первого результата, и 178.24-60.48 для вывода всех результатов. 60.48 вычитается из-за того, что сортировке необходимо получить все данные от последовательного чтения, прежде чем вернуть хоть что-то. В общем, когда видите очень похожие числа в затратах вывода первого и всех результатов, это означает, что операции требуются все данные предыдущих операций.</p>
<p>Теперь рассмотрим ещё более интересный пример:</p>
<pre class="code">
EXPLAIN ANALYZE SELECT * FROM customer JOIN contact USING (last_name);
                                                    QUERY PLAN
------------------------------------------------------------------------------------------------------------
 Hash Join  (cost=1.02..92.23 rows=2048 width=351) (actual time=1.366..58.684 rows=4096 loops=1)
   Hash Cond: (("outer".last_name)::text = ("inner".last_name)::text)
   ->  Seq Scan on customer  (cost=0.00..60.48 rows=2048 width=107) (actual time=0.079..21.658 rows=2048 loops=1)
   ->  Hash  (cost=1.02..1.02 rows=2 width=287) (actual time=0.146..0.146 rows=2 loops=1)
         ->  Seq Scan on contact  (cost=0.00..1.02 rows=2 width=287) (actual time=0.074..0.088 rows=2 loops=1)
 Total runtime: 62.233 ms
</pre>
<p>Наконец-то что-то похожее на настоящий запрос. Обратите внимание на отступы. Они используются, чтобы показать, что результаты одних шагов используются в качестве входных данных для других. В нашем примере можно увидеть, что hash join принимает на вход результаты выполнения последовательного чтения и hash-операции. Последняя, в свою очередь, сама работает с результатами последовательного чтения. Заметьте, что hash-операция имеет одинаковые затраты на вывод первого и всех результатов &#8211; ей нужны все результаты до того, как вернуть что-либо. В этом плане интересно то, что затраты на вывод первого результата hash join зависит от суммарных затрат hash-операции, но начинается одновременно с последовательным чтением таблицы customer. Это потому, что hash join может возвращать результаты сразу, как только получит первый результат от обеих операций, дающих ему данные на вход.</p>
<p>Вернёмся к объяснению того, что такое повторы или циклы (loops), для этого рассмотрим пример:</p>
<pre class="code">
->  Nested Loop  (cost=5.64..14.71 rows=1 width=140) (actual time=18.983..19.481 rows=4 loops=1)
               ->  Hash Join  (cost=5.64..8.82 rows=1 width=72) (actual time=18.876..19.212 rows=4 loops=1)
               ->  Index Scan using pg_class_oid_index on pg_class i  (cost=0.00..5.88 rows=1 width=72) (actual time=0.051..0.055 rows=1 loops=4)
</pre>
<p>Вложенный цикл &#8211; это то, что хорошо знакомо программистам, пишущим на процедурных языках; это что-то вроде:</p>
<pre class="code">
for each row in input_a
    for each row in input_b
        do something
    next
next
</pre>
<p>Если в input_a 4 элемента, то внутренний цикл ( по input_b) будет выполнен 4 раза. Это именно то, о чём нам говорит оптимизатор в примере выше. Если умножить 0.055 на 4, то получится чуть меньше, чем разница между общим временем выполнения hash join&#8217;а и общим временем выполнения вложенного цикла (остаток времени скорее всего пошёл на накладные расходы, связанные с вычислением всего этого).</p>
<p>Что же это означает на практике? Обычно запрос запускают с EXPLAIN, пытаясь улучшить производительность. Ключом к этому является поиск шага, который занимает большую часть времени (а потом надо попытаться переписать медленную часть или весь запрос). Давайте взглянем на следующий пример и попытаемся найти проблемный шаг. (Этот запрос может запустить каждый и получит такой же результат).</p>
<pre class="code">
EXPLAIN ANALYZE SELECT * FROM pg_indexes WHERE tablename='pg_constraint';
                                                 QUERY PLAN
---------------------------------------------------------------------------------------------------------------
Nested Loop Left Join  (cost=5.64..16.89 rows=1 width=260) (actual time=19.552..20.530 rows=4 loops=1)
  Join Filter: ("inner".oid = "outer".reltablespace)
  ->  Nested Loop Left Join  (cost=5.64..15.84 rows=1 width=200) (actual time=19.313..20.035 rows=4 loops=1)
        Join Filter: ("inner".oid = "outer".relnamespace)
        ->  Nested Loop  (cost=5.64..14.71 rows=1 width=140) (actual time=18.983..19.481 rows=4 loops=1)
              ->  Hash Join  (cost=5.64..8.82 rows=1 width=72) (actual time=18.876..19.212 rows=4 loops=1)
                    Hash Cond: ("outer".indrelid = "inner".oid)
                    ->  Seq Scan on pg_index x  (cost=0.00..2.78 rows=78 width=8) (actual time=0.037..0.296 rows=80 loops=1)
                    ->  Hash  (cost=5.63..5.63 rows=1 width=72) (actual time=18.577..18.577 rows=1 loops=1)
                          ->  Index Scan using pg_class_relname_nsp_index on pg_class c (cost=0.00..5.63 rows=1 width=72) (actual time=18.391..18.464 rows=1 loops=1)
                                Index Cond: (relname = 'pg_constraint'::name)
                                Filter: (relkind = 'r'::"char")
              ->  Index Scan using pg_class_oid_index on pg_class i  (cost=0.00..5.88 rows=1 width=72) (actual time=0.051..0.055 rows=1 loops=4)
                    Index Cond: (i.oid = "outer".indexrelid)
                    Filter: (relkind = 'i'::"char")
        ->  Seq Scan on pg_namespace n  (cost=0.00..1.06 rows=6 width=68) (actual time=0.014..0.045 rows=6 loops=4)
  ->  Seq Scan on pg_tablespace t  (cost=0.00..1.02 rows=2 width=68) (actual time=0.010..0.018 rows=2 loops=4)
Total runtime: 65.294 ms
</pre>
<p>Вложенный цикл имеет самое большое время выполнения &#8211; 20.035мс. Он, в свою очередь, использует результаты другого вложенного цикла и последовательного чтения, и уже этот вложенный цикл &#8211; самый долгий (19.481 мс).<br />
Следовательно, сейчас &laquo;путь&raquo; к &laquo;дорогому&raquo; шагу следующий:</p>
<pre class="code">
Nested Loop Left Join  (cost=5.64..16.89 rows=1 width=260) (actual time=19.552..20.530 rows=4 loops=1)
   Join Filter: ("inner".oid = "outer".reltablespace)
   ->  Nested Loop Left Join  (cost=5.64..15.84 rows=1 width=200) (actual time=19.313..20.035 rows=4 loops=1)
         Join Filter: ("inner".oid = "outer".relnamespace)
         ->  Nested Loop  (cost=5.64..14.71 rows=1 width=140) (actual time=18.983..19.481 rows=4 loops=1)
</pre>
<p>В этом примере эти шаги расположены последовательно, но это не всегда так.<br />
Нижний вложенный цикл берёт данные отсюда:</p>
<pre class="code">
               ->  Hash Join  (cost=5.64..8.82 rows=1 width=72) (actual time=18.876..19.212 rows=4 loops=1)
                     Hash Cond: ("outer".indrelid = "inner".oid)
                     ->  Seq Scan on pg_index x  (cost=0.00..2.78 rows=78 width=8) (actual time=0.037..0.296 rows=80 loops=1)
                     ->  Hash  (cost=5.63..5.63 rows=1 width=72) (actual time=18.577..18.577 rows=1 loops=1)
                           ->  Index Scan using pg_class_relname_nsp_index on pg_class (cost=0.00..5.63 rows=1 width=72) (actual time=18.391..18.464 rows=1 loops=1)
                                 Index Cond: (relname = 'pg_constraint'::name)
                                 Filter: (relkind = 'r'::"char")
               ->  Index Scan using pg_class_oid_index on pg_class i  (cost=0.00..5.88 rows=1 width=72) (actual time=0.051..0.055 rows=1 loops=4)
                     Index Cond: (i.oid = "outer".indexrelid)
                     Filter: (relkind = 'i'::"char")
</pre>
<p>Заметим, что большую часть времени занимает hash join. Он берёт данные из последовательного чтения и hash-операции. Эта hash-операция занимает большую часть времени:</p>
<pre class="code">
                     ->  Hash  (cost=5.63..5.63 rows=1 width=72) (actual time=18.577..18.577 rows=1 loops=1)
                           ->  Index Scan using pg_class_relname_nsp_index on pg_class c
                               (cost=0.00..5.63 rows=1 width=72) (actual time=18.391..18.464 rows=1 loops=1)
                                 Index Cond: (relname = 'pg_constraint'::name)
                                 Filter: (relkind = 'r'::"char")
</pre>
<p>Мы наконец нашли самую дорогую часть запроса: сканирование индекса pg_class_relname_nsp_index. К сожалению, практически невозможно ускорить сканирование индекса для поиска одной строки. Но и занимает оно всего 18.464 мс, вряд ли когда-нибудь понадобится настолько улучшать производительность.<br />
Ещё одно замечание: затраты на EXPLAIN ANALYZE достаточно значительны. Они могут занимать до 30% времени выполнения запроса. Поэтому EXPLAIN &#8211; это инструмент для измерения относительной производительности, а не абсолютной.</p>
<p>Оригинал статьи &#8211; <a href="http://wiki.postgresql.org/wiki/Introduction_to_VACUUM,_ANALYZE,_EXPLAIN,_and_COUNT#PostgreSQL_EXPLAIN">Introduction to VACUUM, ANALYZE, EXPLAIN, and COUNT</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.job-blog.bullgare.ru/2010/05/postgresql-explain/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Генерирование данных в PostgreSQL</title>
		<link>http://www.job-blog.bullgare.ru/2010/03/%d1%80%d0%b5%d0%ba%d1%83%d1%80%d1%81%d0%b8%d0%b2%d0%bd%d0%be-%d0%bf%d0%be%d0%b4%d0%bd%d0%b8%d0%bc%d0%b0%d0%b5%d0%bc-%d1%81%d1%82%d0%b0%d1%82%d0%b8%d1%81%d1%82%d0%b8%d0%ba%d1%83-%d0%bf%d0%be-%d0%ba/</link>
		<comments>http://www.job-blog.bullgare.ru/2010/03/%d1%80%d0%b5%d0%ba%d1%83%d1%80%d1%81%d0%b8%d0%b2%d0%bd%d0%be-%d0%bf%d0%be%d0%b4%d0%bd%d0%b8%d0%bc%d0%b0%d0%b5%d0%bc-%d1%81%d1%82%d0%b0%d1%82%d0%b8%d1%81%d1%82%d0%b8%d0%ba%d1%83-%d0%bf%d0%be-%d0%ba/#comments</comments>
		<pubDate>Thu, 11 Mar 2010 09:34:20 +0000</pubDate>
		<dc:creator>bullgare</dc:creator>
				<category><![CDATA[Базы данных]]></category>
		<category><![CDATA[generate_series]]></category>
		<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[sql]]></category>
		<category><![CDATA[WITH RECURSIVE]]></category>

		<guid isPermaLink="false">http://www.job-blog.bullgare.ru/?p=645</guid>
		<description><![CDATA[Задача: Рекурсивно поднимаем статистику по количеству аккаунтов на указанную дату. При этом у аккаунта есть только период с-по. Основная задача &#8211; сгенерировать даты. Сложный способ &#8211; рекурсия: WITH RECURSIVE stat_( inputdate ) AS ( SELECT COUNT(*) AS total, curdate FROM account WHERE expired > inputdate AND inputdate = NOW() - INTERVAL '1 MONTH' UNION ALL [...]]]></description>
			<content:encoded><![CDATA[<p>Задача: Рекурсивно поднимаем статистику по количеству аккаунтов на указанную дату.<br />
При этом у аккаунта есть только период с-по.<br />
Основная задача &#8211; сгенерировать даты.<br />
<span id="more-645"></span><br />
Сложный способ &#8211; рекурсия:</p>
<pre class="code">
WITH RECURSIVE stat_( inputdate ) AS (
		SELECT COUNT(*) AS total, curdate
		FROM account
		WHERE expired > inputdate AND inputdate = NOW() - INTERVAL '1 MONTH'
	UNION ALL
		SELECT COUNT(*) AS total, ( inputdate + INTERVAL '1 DAY' ) AS curdate
		FROM stat_, account
		WHERE expired > curdate AND curdate = NOW() + INTERVAL '1 MONTH'

)
SELECT * FROM stat_
</pre>
<p>Очень простой способ &#8211; создаём виртуалную таблицу:</p>
<pre class="code">
 SELECT 'now'::text::date + s.a AS day, account.id, count(account.id) AS count
   FROM generate_series((-120), 0) s(a)
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.job-blog.bullgare.ru/2010/03/%d1%80%d0%b5%d0%ba%d1%83%d1%80%d1%81%d0%b8%d0%b2%d0%bd%d0%be-%d0%bf%d0%be%d0%b4%d0%bd%d0%b8%d0%bc%d0%b0%d0%b5%d0%bc-%d1%81%d1%82%d0%b0%d1%82%d0%b8%d1%81%d1%82%d0%b8%d0%ba%d1%83-%d0%bf%d0%be-%d0%ba/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

