{"id":203,"date":"2009-02-06T00:41:42","date_gmt":"2009-02-05T22:41:42","guid":{"rendered":"https:\/\/www.sqlinthewild.co.za\/?p=203"},"modified":"2010-10-09T12:26:27","modified_gmt":"2010-10-09T10:26:27","slug":"index-columns-selectivity-and-inequality-predicates","status":"publish","type":"post","link":"https:\/\/www.sqlinthewild.co.za\/index.php\/2009\/02\/06\/index-columns-selectivity-and-inequality-predicates\/","title":{"rendered":"Index columns, selectivity and inequality predicates"},"content":{"rendered":"<p>So, following on from my <a href=\"https:\/\/www.sqlinthewild.co.za\/index.php\/2009\/01\/19\/index-columns-selectivity-and-equality-predicates\/\">post last week<\/a>, I&#8217;m going to take a look at how selectivity and index column order affect inequality predicates.<\/p>\n<p>One thing to note straight off is that the selectivity of a column is much less important for inequality predicates than it was for equality. For equality predicates, the selectivity alone can give a reasonable idea of the number of rows a particular predicate will return. That&#8217;s not the case with inequalities. Also, with inequality predicates, the order of columns in the index becomes very important.<\/p>\n<p>One of the most important considerations with inequality predicates is the number of rows that the predicate will return. An identity column may be highly selective, but if the filter is for all rows &gt; 0 and the identity values start t one, then an index on that column is not going to be very useful.<\/p>\n<p>The other consideration when there are inequality predicates is that only that column and columns to the left of it in the index key can be used for index seeks. Any columns to the right of the column with the inequality is no longer eligible for seeking.<\/p>\n<p>To explain with an example, consider our hypothetical table from the previous post (with one small change):<\/p>\n<pre class=\"brush: sql; title: ; notranslate\" title=\"\">CREATE TABLE ConsideringIndexOrder (\r\nID INT,\r\nSomeString VARCHAR (100),\r\nSomeDate DATETIME DEFAULT GETDATE()\r\n);\u00a0 <\/pre>\n<p>The same as previously, there&#8217;s a single nonclustered index on all three columns, in the order ID, SomeDate, SomeString.<\/p>\n<p>If there&#8217;s an inequality predicate, then then the index is only fully seekable for the following queries<br \/>\n\u2026\u00a0 WHERE ID = @ID AND SomeDate = @dt AND SomeString &gt; @str<br \/>\n\u2026\u00a0 WHERE ID = @ID AND SomeDate &gt; @dt<br \/>\n\u2026\u00a0 WHERE ID &gt; @ID<\/p>\n<p><!--more-->If there&#8217;s another predicate, equality or inequality, on a column further to the right in the index, that cannot be executed as part of the index seek, and will be done as a second step, just as happened with equalities when the predicates were not left-based subsets of the index columns.<\/p>\n<p>So, what does that mean for index columns order? Quite simply, if queries are always going to filter with one or more equality predicates and one or more inequality predicates, the columns used for the inequalities must appear further to the right in the index than the equalities.<\/p>\n<p>That&#8217;s great when there&#8217;s only one inequality predicate, but what happens when there&#8217;s more than one? If there are going to be more than one inequality predicate, the one that is likely to return fewer rows should go earlier in the index. This is not to say the most selective one, but the one that will be queried with a more selective range.<\/p>\n<p>Using the above table as an example, if a typical query will run with an inequality on the ID column that on average will return 1000 rows and with an inequality on the date column that will on average return 100 rows, then the date column should go before the ID in the index (assuming that&#8217;s the only query)<\/p>\n<p>Let\u2019s take a look at some query scenarios based on the hypothetical table above to see how that index will be used with some inequality predicates.<\/p>\n<p><strong>Scenario 1: Inequality predicate on the ID column<\/strong><\/p>\n<p>This is probably the simplest of the inequalities. Since ID is the leading column of the index, SQL does a seek to find the beginning of the range (or the first row in the table if applicable) and then reads along the leaf pages of the index until it reaches the end of the range. Those rows are then returned.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-206\" style=\"border: 1px solid black;\" title=\"seek1\" src=\"https:\/\/www.sqlinthewild.co.za\/wp-content\/uploads\/2009\/02\/seek1.png\" alt=\"\" width=\"309\" height=\"174\" \/><\/p>\n<p><strong>Scenario 2: Equality match on the ID column and inequality on the Date column<\/strong><\/p>\n<p>This one&#8217;s also fairly easy. SQL seeks to find a matching ID and the start of the range and then reads along hte index to find the rest of the rows.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-207\" style=\"border: 1px solid black;\" title=\"seek2\" src=\"https:\/\/www.sqlinthewild.co.za\/wp-content\/uploads\/2009\/02\/seek2.png\" alt=\"\" width=\"308\" height=\"197\" \/><\/p>\n<p><strong>Scenario 3: Inequality match on both the ID and Date columns<\/strong><\/p>\n<p>In this case, only one of the predicates can be used as a seek predicate, the other will be executed as a predicate, meaning that each row that the seek retrieves has to be compared against that predicate. Since the index starts with ID, it&#8217;s the inequality on ID that will be picked for the seek. If there was a second index that started with date, that one might be picked instead.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-208\" style=\"border: 1px solid black;\" title=\"seek-3\" src=\"https:\/\/www.sqlinthewild.co.za\/wp-content\/uploads\/2009\/02\/seek-3.png\" alt=\"\" width=\"309\" height=\"296\" \/><\/p>\n<p>While both columns are mentioned in the seek predicate, note that there&#8217;s also a predicate on the SomeDate column, which is not present in the simple index seeks.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>So, following on from my post last week, I&#8217;m going to take a look at how selectivity and index column order affect inequality predicates. One thing to note straight off is that the selectivity of a column is much less&#8230; <a class=\"read-more-button\" href=\"https:\/\/www.sqlinthewild.co.za\/index.php\/2009\/02\/06\/index-columns-selectivity-and-inequality-predicates\/\">(Read more)<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[24,15,16],"tags":[],"class_list":["post-203","post","type-post","status-publish","format-standard","hentry","category-indexes","category-sql-server","category-syndication"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p7h6n-3h","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/www.sqlinthewild.co.za\/index.php\/wp-json\/wp\/v2\/posts\/203","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.sqlinthewild.co.za\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.sqlinthewild.co.za\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.sqlinthewild.co.za\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.sqlinthewild.co.za\/index.php\/wp-json\/wp\/v2\/comments?post=203"}],"version-history":[{"count":0,"href":"https:\/\/www.sqlinthewild.co.za\/index.php\/wp-json\/wp\/v2\/posts\/203\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.sqlinthewild.co.za\/index.php\/wp-json\/wp\/v2\/media?parent=203"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.sqlinthewild.co.za\/index.php\/wp-json\/wp\/v2\/categories?post=203"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.sqlinthewild.co.za\/index.php\/wp-json\/wp\/v2\/tags?post=203"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}