<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Naga Mocha</title>
    <description>The latest articles on DEV Community by Naga Mocha (@nagamocha3000).</description>
    <link>https://dev.to/nagamocha3000</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F321514%2Fe6177534-a7e0-4d33-9e92-75b084f7b0f8.jpg</url>
      <title>DEV Community: Naga Mocha</title>
      <link>https://dev.to/nagamocha3000</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nagamocha3000"/>
    <language>en</language>
    <item>
      <title>SQL joins as reduce/folds over relations</title>
      <dc:creator>Naga Mocha</dc:creator>
      <pubDate>Fri, 29 May 2020 05:45:37 +0000</pubDate>
      <link>https://dev.to/nagamocha3000/sql-joins-as-reduce-folds-over-relations-nhn</link>
      <guid>https://dev.to/nagamocha3000/sql-joins-as-reduce-folds-over-relations-nhn</guid>
      <description>&lt;p&gt;Mental models help us navigate complex ideas. However, forming one own's mental models can be a tricky affair. On one hand, by putting the effort to come up with menetal a model by ourselves, we gain a deeper understanding of the subject at hand. We also become better and more efficient at reasoning and problem-solving. However, on the other hand, a deficient or even an entirely wrong mental model can derail arriving at a far deeper understanding and even result in huge errors and blindspots later on, despite such mental models serving us well in the beginning. This was my case when I encountered &lt;em&gt;joins&lt;/em&gt; at first when I was learning SQL.&lt;/p&gt;

&lt;p&gt;With joins, I initially visualized &lt;em&gt;foreign keys&lt;/em&gt; as sort of 'pointers' to different storage locations where the rows containing the primary keys, and &lt;em&gt;joins&lt;/em&gt; themselves as 'dereferencing' procedures. This worked well when I had to write queries for simple inner joins involving two tables. Beyond that, eg three or four tables, or if the situation called for outer queries, I was always left stumped.&lt;/p&gt;

&lt;p&gt;I then ditched the whole pointers-dereferencing model, and for a while, simply treated joins as opaque procedures, tinkering with them until the query somehow worked. I could afford such a 'strategy' when writing queries where there was always a set answer for comparison (such as in online exercises) but I knew at some point I'd have to write 'greenfield' queries without crutches to hold on to. A full understanding of joins was necessitated.&lt;/p&gt;

&lt;p&gt;My strategy then was to go back to the basics. When you're at an intermediate level, going back to the basics always feels like a chore - there's that impatience coming from hey, I already know this why should I go through it over again. Furthermore, every minute spent on 'going back to the basics' could be spent on becoming more 'advanced', doing more projects and all that kind of stuff. Well, revisiting fundamentals can be made into an engaging exercise. It's the best time to reevaluate and challenge one's mental models and assumptions. Which is how I ended up getting a better understanding of how to use sql &lt;em&gt;joins&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Note, there's a part 2 of this post that actually does go back to the basics -the fundamental ideas on which joins are based on. This post is more of an alternative per se: an attempt at using another high-level concept so as to make sense of &lt;em&gt;joins&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Let's start with a simple SQL query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;column1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;column2&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="n"&gt;columnN&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;relationA&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If we think about the 'order' of evaluation: the &lt;em&gt;from&lt;/em&gt; gets 'evaluated' before the &lt;em&gt;select&lt;/em&gt;. The &lt;em&gt;from&lt;/em&gt; is where the &lt;code&gt;join&lt;/code&gt; clauses are placed. The word 'evaluated' is in quotation marks because technically, SQL engines aren't required to, nor have to evaluate a query in some given order. In fact, data-retrieval sql queries themselves aren't dictating some imperative order of evaluation in the same way 'line number' dictates order of evaluation in synchronous code. Instead, such queries describe the shape of the data we want back, which is why SQL is said to be declarative. The 'declarativess' of SQL is yet another concept I struggled with initally. And before going further, I'd like to link to a particular Julia Evans' blog &lt;a href="https://jvns.ca/blog/2019/10/03/sql-queries-don-t-start-with-select/"&gt;post&lt;/a&gt;, which gives a more in-depth treatment of the evaluation order of SQL queries.&lt;/p&gt;

&lt;p&gt;Back to the SQL code we have above. As mentioned, let's suspend the technicalities and assume an 'order of evaluation': By the time &lt;strong&gt;select&lt;/strong&gt; is evaluated, all it has to work with is a single table from which it picks the required columns, i.e. the projection part in relational algebra. Therefore, if there are any &lt;strong&gt;joins&lt;/strong&gt;,these &lt;em&gt;joins&lt;/em&gt; can be conceptualized as procedures or operations that build a huge single table from many related tables, using the join clauses to connect rows. Another way of seeing it is that the evaluation of a series of joins is in fact a &lt;strong&gt;reduce&lt;/strong&gt; operation.&lt;/p&gt;

&lt;p&gt;Now, &lt;a href="https://en.wikipedia.org/wiki/Fold_higher-order_function"&gt;reduce&lt;/a&gt; itself is a high-level concept, being one of the fundamental higher-order functions in functional programming; &lt;em&gt;reduce&lt;/em&gt; abstracts a common iteration pattern as we shall see. However, the manner in which &lt;em&gt;reduce&lt;/em&gt; is normally introduced to novices waters down its essence. The standard example used usually entails a collection of numbers and calculating a value such as a sum. For example, the MDN javascript docs provide the following sample:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;array1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reducer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;accumulator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;currentValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;accumulator&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;currentValue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// 1 + 2 + 3 + 4&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;array1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;reducer&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="c1"&gt;// expected output: 10&lt;/span&gt;

&lt;span class="c1"&gt;// 5 + 1 + 2 + 3 + 4&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;array1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;reducer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="c1"&gt;// expected output: 15&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;I kinda get why such examples are used to demonstrate &lt;em&gt;reduce&lt;/em&gt;: rather than presenting the abstracted version - it's easier for learners to at least be familiar with it and know that it exists. Generally, all beginner material has to balance between ease of understanding, clarity, correctness and thoroughness. Most choose ease-of-understanding in the hope that once learners progress to the intermediate level, they can take on the technicalities.&lt;/p&gt;

&lt;p&gt;And so, for quite a while as a novice, I simply thought of reduce as a fancy way to perform calculations over an array of numbers, in which case, I'd rather use good old-fashioned for-loops. It's not until I was working through Daniel Higgibotham's &lt;em&gt;'Clojure for the Brave and True'&lt;/em&gt; that I saw &lt;em&gt;reduce&lt;/em&gt; in a new light. Here's how Daniel introduces reduce:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The pattern of &lt;em&gt;process each element in a sequence and build a result&lt;/em&gt; is so common that there’s a built-in function for it called reduce....&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This was a mini-moment of enlightenment for me! My understanding of reduce became more generalized and abstract:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;For one, the sequence can consist of anything, not just numbers: a sequence of cats, dogs, json, other sequences, whatever.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Moreover, the sequence itself doesn't even have to be an array, it can be a tree, a map, any sequence-like/iterable data-structure.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;And finally, the value we are building up using reduce doesn't even have to be of the same type as the elements in the sequence - just because the array consists of numbers doesn't mean &lt;em&gt;reduce&lt;/em&gt; has to return a number.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the sake of repetition, here's yet another definition of reduce that I got from Eric Elliot's post, &lt;em&gt;10 Tips for Better Redux Architecture&lt;/em&gt; , &lt;a href="https://medium.com/javascript-scene/10-tips-for-better-redux-architecture-69250425af44"&gt;link&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In functional programming, the common utility &lt;em&gt;&lt;code&gt;reduce()&lt;/code&gt;&lt;/em&gt; or &lt;em&gt;&lt;code&gt;fold()&lt;/code&gt;&lt;/em&gt; is used to apply a reducer function to each value in a list of values in order to accumulate a single output value.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And now back to SQL. Do keep in mind that I might just be shoehorning one concept into another out of sheer excitement. As already mentioned, no matter how many tables are listed in the &lt;strong&gt;from&lt;/strong&gt; clause, at the end of the day &lt;strong&gt;select&lt;/strong&gt; expects only a single table from which it can pick out the specified collumns. Thus, one might visualize the &lt;em&gt;join&lt;/em&gt; 'operator' as a reducer function. And just like a reducer, it takes in two arguments: the first argument is the table accumulated so far, and the second argument is the next table in line. Within this reducer function, join then builds, or rather accumulates both tables into a much larger table. The accumulation is across two dimensions, rows and collumns. On columns, since this reduction takes place before the &lt;strong&gt;select&lt;/strong&gt; clause, all the columns from both tables are included. However, if we use the keyword &lt;strong&gt;using&lt;/strong&gt; in the join clause instead of the more common &lt;strong&gt;on&lt;/strong&gt; clause, the two columns that are being compared to are collapsed into a single column. As for the rows, it all depends on the type of &lt;em&gt;join&lt;/em&gt; we are using. For example, when we are using a &lt;strong&gt;right-outer join&lt;/strong&gt;, if a row in the left accumulated table cannot be partnered up with a row in the right table, it is discarded; once all the rows are partnered up, if there were any rows in the right table that didn't get a partner, nulls are used to fill up the gaps. This was kind of the idea in my head on how joins work.&lt;/p&gt;

&lt;p&gt;Still, I did not know how to conceptualize foreign-key columns in a '&lt;em&gt;joins-as-reduce&lt;/em&gt;' context. All join-clauses I had come across or worked on at that point related primary keys with foreign keys therefore I somehow presumed both concepts are tightly related. The whole pointer-dereferencing thing felt even more iffy whenever I tried to combine it with &lt;em&gt;joins-as-reduce&lt;/em&gt; so for the sake of sanity, I couldn not resort to it again. Toying with a couple of queries though, I found out that the columns used in a join clause don't even have to have some primary-key - secondary-key sort of relationship. All this time, I was intertwining joins and keys, when they weren't even dependent on each other. As such, faced with gaping holes in my understanding, I had to go back to the basics... which is exactly what I explore in the second part of this article. See you there!&lt;/p&gt;

</description>
      <category>sql</category>
      <category>postgres</category>
    </item>
    <item>
      <title>Speeding up unique constraint checks in Postgres... or not</title>
      <dc:creator>Naga Mocha</dc:creator>
      <pubDate>Thu, 28 May 2020 19:50:57 +0000</pubDate>
      <link>https://dev.to/nagamocha3000/speeding-up-unique-constraint-checks-in-postgres-or-not-2c59</link>
      <guid>https://dev.to/nagamocha3000/speeding-up-unique-constraint-checks-in-postgres-or-not-2c59</guid>
      <description>&lt;p&gt;Intuitively, or rather theoretically (since this doesn't even require intuition), using hash indexes over Btree indexes for equality lookups should be faster: O(1) vs O(log n). And as is expected, various benchmarks confirm this. However, when it comes to enforcing uniqueness in a given column, hash indexes don't perform so well. &lt;/p&gt;

&lt;p&gt;The standard way of adding a uniqueness constraint to a column, is by declaring it when creating the table. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="k"&gt;unique&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;On my computer, it takes 5.563 seconds to insert a million sequentially ordered IDs. It takes 7.533 seconds to add a million randomly ordered IDs. Without any constraint on the &lt;code&gt;id&lt;/code&gt; column, it takes roughly 2 seconds to insert a million items regardless of whether they are ordered sequentially or randomly.&lt;/p&gt;

&lt;p&gt;Postgres provides another round-about way of adding a uniqueness constraint to a column - &lt;em&gt;exclusion constraints&lt;/em&gt;. Postgres' &lt;a href="https://www.postgresql.org/docs/12/ddl-constraints.html#DDL-CONSTRAINTS-EXCLUSION"&gt;documentation&lt;/a&gt; defines exclusion constraints as follows:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Exclusion constraints ensure that if any two rows are compared on the specified columns or expressions using the specified operators, at least one of these operator comparisons will return false or null.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For our case, enforcing uniqueness, the above statement could be restated in this way: Suppose we only consider a single column and provide one operator for the exclusion constraint (such as the equals operator '=' for uniqueness). Then, when inserting or updating a row, the comparison with all other pre-existing rows should result in false, (or null since sql has three-valued logic). Otherwise, it will be excluded. This is easier demonstrated with actual sql:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;exclude&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Better yet, Postgres allows us to speed up the exclusion check using an index as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;exclude&lt;/span&gt; &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="n"&gt;hash&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now, when using a unique constraint, Postgres' documentation states that it "will automatically create a unique B-tree index on the column or group of columns listed in the constraint". Given that the exclusion constraint above is using hash index, I expected it to be faster. However, inserting a million sequential ID's took 9.192 seconds, which is almost twice as slow as relying on the plain old unique constraint. Inserting randomly ordered IDs took 8.443 seconds.&lt;/p&gt;

&lt;p&gt;At first, I presumed it has something to do with the way the underlying hash indexes are structured, but even when using btree, it took roughly the same amount of time as the hash index exclusion constraint. The btree though was way much slower when inserting randomly ordered IDs, taking 12.058 seconds. My current presumption is that Postgres developers have put a lot of work into optimizing the standard unique constraint, since it's expected that the users will opt for it over exclusion constraints, which are better left for more interesting stuff, like overlapping intervals. &lt;/p&gt;

&lt;p&gt;Other than being slower, by relying on exclusion constraints to enforce uniqueness, we also lose the ability to have the column be referenced by foreign keys in other tables. This is because in Postgres, only unique columns and primary key columns can be referenced. For example, the second table definition below fails:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;constraint&lt;/span&gt; &lt;span class="n"&gt;unique_book_id_hash&lt;/span&gt; &lt;span class="n"&gt;exclude&lt;/span&gt; &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="n"&gt;hash&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;item_id&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="k"&gt;references&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;-- ❌&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;So far though, if you've noticed, I've been using integers. Before making any further conclusions and dismissing hash indexes entirely, it's only fair that they're measured up in the one area where they excel quite well, comparing lengthy strings. And as expected, they do truly shine here. I began with UUID's since I didn't have to write an extra function for generating random strings. With plain old &lt;code&gt;unique&lt;/code&gt;, it takes 32.96 seconds to insert a million UUIDs. It gets worse when inserting the next million UUIDS, 50.557 seconds. On the other hand, when using the hash-index based exclusion check, it takes 12.537 seconds to insert the first set of a million UUIDs, 12.764 to insert the next set and finally 16.24 seconds to insert the third set - quite impressive. I'll be sure to try comparing both with random strings of different lengths but I expect similar results. And yeah, that's definitely one way to speed up uniqueness constraint checks if the column's type is a string, rather than an integer and it won't be referenced elsewhere in the database. &lt;/p&gt;

</description>
      <category>postgres</category>
      <category>sql</category>
    </item>
  </channel>
</rss>
