<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Postgres, PostgreSQL on CRAIG KERSTIENS</title><link>/tags/Postgres-PostgreSQL/</link><description>CRAIG KERSTIENS (Postgres, PostgreSQL)</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Mon, 18 Nov 2013 12:55:56 -0800</lastBuildDate><atom:link href="/tags/Postgres-PostgreSQL/index.xml" rel="self" type="application/rss+xml"/><item><title>The best Postgres feature you're not using – CTEs aka WITH clauses</title><link>/2013/11/18/best-postgres-feature-youre-not-using/</link><pubDate>Mon, 18 Nov 2013 12:55:56 -0800</pubDate><guid>/2013/11/18/best-postgres-feature-youre-not-using/</guid><description>&lt;p>SQL by default isn&amp;rsquo;t typically friendly to dive into, and especially so if you&amp;rsquo;re reading someone else&amp;rsquo;s already created queries. For some reason most people throw out principles we follow in other languages &lt;a href="http://www.craigkerstiens.com/2013/07/29/documenting-your-postgres-database/">such as commenting&lt;/a> and composability just for SQL. I was recently reminded of a key feature in Postgres that most don&amp;rsquo;t use by &lt;a href="http://www.twitter.com/timonk">@timonk&lt;/a> highlighting it in his AWS Re:Invent Redshift talk. The simple feature actually makes SQL both readable and composable, and even for my own queries capable of coming back to them months later and understanding them, where previously they would not be.&lt;/p>
&lt;p>The feature itself is known as CTEs or common table expressions, you may also here it referred to as &lt;code>WITH&lt;/code> clauses. The general idea is that it allows you to create something somewhat equivilant to a view that only exists during that transaction. You can create multiple of these which then allow for clear building blocks and make it simple to follow what you&amp;rsquo;re doing.&lt;/p>
&lt;p>Lets take a look at a nice simple one:&lt;/p>
&lt;pre>&lt;code>WITH users_tasks AS (
SELECT
users.email,
array_agg(tasks.name) as task_list,
projects.title
FROM
users,
tasks,
project
WHERE
users.id = tasks.user_id
projects.title = tasks.project_id
GROUP BY
users.email,
projects.title
)
&lt;/code>&lt;/pre>
&lt;p>Using this I could now just append some basic other query on to the end that references this CTE &lt;code>users_tasks&lt;/code>. Something akin to:&lt;/p>
&lt;pre>&lt;code>SELECT *
FROM users_tasks;
&lt;/code>&lt;/pre>
&lt;p>But where it becomes more interesting is chaining these together. So while I have all tasks assigned to each user here, perhaps I want to then find which users are responsible for more than 50% of the tasks on a given project, thus being the bottleneck. To oversimplify this we could do it a couple of ways, total up the tasks for each project, and then total up the tasks for each user per project:&lt;/p>
&lt;pre>&lt;code>total_tasks_per_project AS (
SELECT
project_id,
count(*) as task_count
FROM tasks
GROUP BY project_id
),
tasks_per_project_per_user AS (
SELECT
user_id,
project_id,
count(*) as task_count
FROM tasks
GROUP BY user_id, project_id
),
&lt;/code>&lt;/pre>
&lt;p>Then we would want to combine and find the users that are now over that 50%:&lt;/p>
&lt;pre>&lt;code>overloaded_users AS (
SELECT tasks_per_project_per_user.user_id,
FROM tasks_per_project_per_user,
total_tasks_per_project
WHERE tasks_per_project_per_user.task_count &amp;gt; (total_tasks_per_project / 2)
)
&lt;/code>&lt;/pre>
&lt;p>Now as a final goal I&amp;rsquo;d want to get a comma separated list of tasks of the overloaded users. So we&amp;rsquo;re simply giong to join against that &lt;code>overloaded_users&lt;/code> and our initial list of &lt;code>users_tasks&lt;/code>. Putting it all together it looks somewhat long, but becomes much more readable. And as a bonus I layered in some comments.&lt;/p>
&lt;pre>&lt;code>--- Created by Craig Kerstiens 11/18/2013
--- Query highlights users that have over 50% of tasks on a given project
--- Gives comma separated list of their tasks and the project
--- Initial query to grab project title and tasks per user
WITH users_tasks AS (
SELECT
users.id as user_id,
users.email,
array_agg(tasks.name) as task_list,
projects.title
FROM
users,
tasks,
project
WHERE
users.id = tasks.user_id
projects.title = tasks.project_id
GROUP BY
users.email,
projects.title
),
--- Calculates the total tasks per each project
total_tasks_per_project AS (
SELECT
project_id,
count(*) as task_count
FROM tasks
GROUP BY project_id
),
--- Calculates the projects per each user
tasks_per_project_per_user AS (
SELECT
user_id,
project_id,
count(*) as task_count
FROM tasks
GROUP BY user_id, project_id
),
--- Gets user ids that have over 50% of tasks assigned
overloaded_users AS (
SELECT tasks_per_project_per_user.user_id,
FROM tasks_per_project_per_user,
total_tasks_per_project
WHERE tasks_per_project_per_user.task_count &amp;gt; (total_tasks_per_project / 2)
)
SELECT
email,
task_list,
title
FROM
users_tasks,
overloaded_users
WHERE
users_tasks.user_id = overloaded_users.user_id
&lt;/code>&lt;/pre>
&lt;p>CTEs won&amp;rsquo;t always be quite as performant as optimizing your SQL to be as concise as possible. In most cases I have seen performance differences smaller than a 2X difference, this tradeoff for readability is a nobrainer as far as I&amp;rsquo;m concerned. And with time the Postgres optimizer should continue to get better about such performance.&lt;/p>
&lt;p>As for the verbosity, yes I could have done this query in probably 10-15 lines of very concise SQL. Yet, most may not be able to understand it quickly if at all. Readability is huge when it comes to SQL to ensure its doing the right thing. SQL will almost always tell you an answer, it just may not be to the question you think you&amp;rsquo;re asking. Ensuring your queries can be reasoned about is critical to ensuring accuracy and CTEs are one great way of accomplishing that.&lt;/p>
&lt;p>&lt;em>If you&amp;rsquo;re looking for a deeper resource on Postgres I recommend the book &lt;a href="https://theartofpostgresql.com/?affiliate=cek">The Art of PostgreSQL&lt;/a>. It is by a personal friend that has aimed to create the definitive guide to Postgres, from a developer perspective. If you use code CRAIG15 you&amp;rsquo;ll receive 15% off as well.&lt;/em>&lt;/p></description></item><item><title>Diving into Postgres JSON operators and functions</title><link>/2013/09/11/Diving-into-Postgres-JSON-operators-and-functions/</link><pubDate>Wed, 11 Sep 2013 12:55:56 -0800</pubDate><guid>/2013/09/11/Diving-into-Postgres-JSON-operators-and-functions/</guid><description>&lt;p>Just as &lt;a href="https://postgres.heroku.com/blog/past/2013/9/9/postgres_93_now_available/">PostgreSQL 9.3&lt;/a> was coming out I had a need to take advantage of the JSON datatype and some of the &lt;a href="http://www.postgresql.org/docs/9.3/static/functions-json.html">operators and functions&lt;/a> within it. The use case was pretty simple, run a query across a variety of databases, then take the results and store them. We explored doing something more elaborate with the columns/values, but in the end just opted to save the entire result set as JSON then I could use the operators to explore it as desired.&lt;/p>
&lt;p>Here&amp;rsquo;s the general idea in code (using sequel):&lt;/p>
&lt;pre>&lt;code>result = r.connection { |c| c.fetch(self.query).all }
mymodel.results = result.to_json
&lt;/code>&lt;/pre>
&lt;p>As the entire dataset was stored as some compressed JSON I needed to do a bit of manipulation to get it back into a form that was workable. Fortunately all the steps were fairly straightforward.&lt;/p>
&lt;p>First you want to unnest each result from the json array, in my case this looked like:&lt;/p>
&lt;pre>&lt;code>SELECT json_array_elements(result)
&lt;/code>&lt;/pre>
&lt;p>The above will unnest all of the array elements so I have an individual result as JSON. A real world example would look something similar to:&lt;/p>
&lt;pre>&lt;code>SELECT json_array_elements(result)
FROM query_results
LIMIT 2;
json_array_elements
&lt;/code>&lt;/pre>
&lt;hr>
&lt;p>{&amp;ldquo;column_name&amp;rdquo;:&amp;ldquo;data_in_here&amp;rdquo;}
{&amp;ldquo;column_name_2&amp;rdquo;:&amp;ldquo;other_data_in_here&amp;rdquo;}
(2 rows)&lt;/p>
&lt;p>From here based on the query I would want to get some specific value. In this case I&amp;rsquo;m going to search for the text key column_name_2:&lt;/p>
&lt;pre>&lt;code>SELECT json_array_elements(result)-&amp;gt;'column_name_2'
FROM query_results
LIMIT 1;
json_array_elements
-----------------------
&amp;quot;other_data_in_here&amp;quot;
(1 rows)
&lt;/code>&lt;/pre>
&lt;p>&lt;em>One gotcha I encountered was when I wanted to search for some value or exclude some value&amp;hellip; Expecting I could just compare the result of the above in a where statement I was sadly mistaken because the equals operator didn&amp;rsquo;t translate.&lt;/em> My first attempt at fixing this was to cast in this form:&lt;/p>
&lt;pre>&lt;code>SELECT json_array_elements(result)-&amp;gt;'column_name_2'::text
&lt;/code>&lt;/pre>
&lt;p>The sad part is because of the operator the cast doesn&amp;rsquo;t get applied as I&amp;rsquo;d expect. Instead you&amp;rsquo;ll want to do:&lt;/p>
&lt;pre>&lt;code>SELECT (json_array_elements(result)-&amp;gt;'column_name_2')::text
&lt;/code>&lt;/pre>
&lt;p>Of course theres plenty more you can do with the &lt;a href="http://www.postgresql.org/docs/9.3/static/functions-json.html">JSON operators in the new Postgres 9.3&lt;/a>. If you&amp;rsquo;ve already got JSON in your application give them a look today. And while slightly worse, if you&amp;rsquo;ve got JSON stored in a text field simply cast it with &lt;code>::json&lt;/code> to begin using the operators.&lt;/p>
&lt;p>&lt;em>If you&amp;rsquo;re looking for a deeper resource on Postgres I recommend the book &lt;a href="https://theartofpostgresql.com/?affiliate=cek">The Art of PostgreSQL&lt;/a>. It is by a personal friend that has aimed to create the definitive guide to Postgres, from a developer perspective. If you use code CRAIG15 you&amp;rsquo;ll receive 15% off as well.&lt;/em>&lt;/p></description></item></channel></rss>