{"id":944,"date":"2016-09-03T20:51:29","date_gmt":"2016-09-03T20:51:29","guid":{"rendered":"http:\/\/nenadnoveljic.com\/blog\/?p=944"},"modified":"2017-04-01T22:10:09","modified_gmt":"2017-04-01T22:10:09","slug":"nested-loop-join-heuristics","status":"publish","type":"post","link":"https:\/\/nenadnoveljic.com\/blog\/nested-loop-join-heuristics\/","title":{"rendered":"Nested Loop Join Heuristics"},"content":{"rendered":"<h1>Discarded Nested Loop Join<\/h1>\n<p>While testing the optimizer transformations on disjunctive subqueries, I created by chance boundary conditions where I observed that the optimizer had not chosen\u00a0the plan with the lowest cost. More precisely, the optimizer discarded the cheaper Nested Loop join and opted for the Hash Join which had a higher cost. In particular, the phenomena happens when the index on the join column of the inner table is missing, and the inner table contains only 1 or 0 records. Although these conditions are less likely on a live system and would have only a minor impact on the query response time, I decided to find out the reason for such behaviour. In fact, elaborating the conditions under which the optimizer starts to change its behavioral patterns almost always provides interesting insights into its internal functioning. I tested the behaviour on SQL Server and Oracle.<\/p>\n<h1>SQL Server<\/h1>\n<p>First, I&#8217;ll create one big and one small table :<\/p>\n<pre><code>SELECT TOP (1000000) \r\n  id = CONVERT(INT, ROW_NUMBER() OVER (ORDER BY s1.[object_id])),\r\n  a = CONVERT(INT, ROW_NUMBER() OVER (ORDER BY s1.[object_id]))\r\nINTO t_large\r\nFROM sys.all_objects AS s1 CROSS JOIN sys.all_objects AS s2\r\nOPTION (MAXDOP 1);\r\n\r\ncreate table t_small (b integer,c integer) ;\r\n\r\ninsert into t_mall values (1,1) ;<\/code><\/pre>\n<p>The <a href=\"http:\/\/sqlperformance.com\/2013\/01\/t-sql-queries\/generate-a-set-1\" target=\"_blank\">query to populate the large table with numbers<\/a> is used by courtesy of Aaron Bertrand.<br \/>\nNext, I&#8217;ll execute the\u00a0 query which joins both of the tables and observe the execution plan. The following output is produced by running the queries with <em>set showplan_all on<\/em>. For the sake of\u00a0simplicity, I removed the irrelevant information leaving only the operation and its cost.<\/p>\n<pre><code>select a from t_large l join t_small s on l.id = s.b ;\r\n\r\nselect a from t_large l join t_small s on l.id = s.b \t<span style=\"color: #ff0000;\">10.49783<\/span>\t\r\n  |--<span style=\"color: #ff0000;\">Hash Match<\/span>(Inner Join, HASH:([s].[b])=([l].[id]), RESIDUAL:([master].[dbo].[t_small].[b] as [s].[b]=[master].[dbo].[t_large].[id] as [l].[id]))\t10.49783\t\r\n       |--Table Scan(OBJECT:([master].[dbo].[t_small] AS [s]))\t0.0032831\t\r\n       |--Table Scan(OBJECT:([master].[dbo].[t_large] AS [l]))\t3.94106\t<\/code><\/pre>\n<p>The optimizer has chosen Hash Join and estimated the total cost to <span style=\"color: #ff0000;\">10.49783<\/span>:<\/p>\n<p>Let&#8217;s take a look at the plan cost for Nested Loop Join which can be enforced by hint:<\/p>\n<pre><code>select a from t_large l join t_small s on l.id = s.b option (<span style=\"color: #ff0000;\">loop join<\/span>);\r\n\r\nselect a from t_large l join t_small s on l.id = s.b option (loop join);\t<span style=\"color: #ff0000;\">8.424343<\/span>\r\n  |--<span style=\"color: #ff0000;\">Nested Loops<\/span>(Inner Join, WHERE:([master].[dbo].[t_small].[b] as [s].[b]=[master].[dbo].[t_large].[id] as [l].[id]))\t8.424343\t\r\n       |--Table Scan(OBJECT:([master].[dbo].[t_small] AS [s]))\t0.0032831\t\r\n       |--Table Scan(OBJECT:([master].[dbo].[t_large] AS [l]))\t3.94106\t<\/code><\/pre>\n<p>In this case, as expected, the cost of the Nested Loop is slightly lower than the one of the Hash Join; the Hash Join namely has an overhead of preparing the hash table. This can be confirmed by the runtime statistics, which were switched on by the following directives:<\/p>\n<pre><code>SET STATISTICS IO ON\r\nSET STATISTICS TIME ON<\/code><\/pre>\n<p>Nested Loop:<\/p>\n<pre><code>Table 't_large'. Scan count 1, <span style=\"color: #ff0000;\">logical reads 3832<\/span>, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.\r\nTable 't_small'. Scan count 1, logical reads 1, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.\r\n\r\n SQL Server Execution Times:\r\n   CPU time = <span style=\"color: #ff0000;\">125 ms<\/span>,  elapsed time = 119 ms.\r\n<\/code><\/pre>\n<p>Hash Join:<\/p>\n<pre><code>Table 't_large'. Scan count 1, <span style=\"color: #ff0000;\">logical reads 3832<\/span>, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.\r\nTable 't_small'. Scan count 1, logical reads 1, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.\r\n\r\n SQL Server Execution Times:\r\n   CPU time = <span style=\"color: #ff0000;\">172 ms<\/span>,  elapsed time = 159 ms.\r\n<\/code><\/pre>\n<p>We can make two observations. One is, that the inner table <em>t_large<\/em> was scanned only once in both cases. The other is, that the Hash Join spent slightly more time on CPU for, as already mentioned, building the hash table. In conclusion, the calculated costs correspond well to the reality, but at the end, the Nested Loop was not considered, unless it was enforced by the hint.<\/p>\n<p>So, the question is, why did the optimizer ignore the nested loop?<\/p>\n<h1>Heuristics<\/h1>\n<p>To answer this question, we&#8217;ll trace the transformation rules which were applied by the optimizer during the query optimization. This can be done by comparing the content of the <em>dm_exec_query_transformation_stats<\/em> DMV just before and immediately after the query compilation. The query transformations for which the <em>succeeded<\/em> column increased, have been considered as alternatives. The exact technique for getting this information for a particular query is described in the book <a href=\"http:\/\/www.red-gate.com\/products\/sql-development\/sql-prompt\/entrypage\/sql-query-optimizer-ebook\" target=\"_blank\">Inside the SQL Server Query Optimizer<\/a> by Benjamin Nevarez. I publish the queries by courtesy of the author:<\/p>\n<pre><code>GO\r\nSELECT *\r\nINTO before_query_transformation_stats\r\nFROM sys.dm_exec_query_transformation_stats\r\nGO\r\nSELECT *\r\nINTO after_query_transformation_stats\r\nFROM sys.dm_exec_query_transformation_stats\r\nGO\r\nDROP TABLE after_query_transformation_stats\r\nDROP TABLE before_query_transformation_stats\r\nGO\r\nSELECT *\r\nINTO before_query_transformation_stats\r\nFROM sys.dm_exec_query_transformation_stats\r\nGO\r\nselect a from t_large l join t_small s on l.id = s.b \r\nOPTION (RECOMPILE)\r\nGO\r\nSELECT *\r\nINTO after_query_transformation_stats\r\nFROM sys.dm_exec_query_transformation_stats\r\nGO\r\nSELECT a.name, (a.promised - b.promised) as promised\r\nFROM before_query_transformation_stats b\r\nJOIN after_query_transformation_stats a\r\nON b.name = a.name\r\nWHERE b.succeeded &lt;&gt; a.succeeded\r\nDROP TABLE before_query_transformation_stats\r\nDROP TABLE after_query_transformation_stats\r\n<\/code><\/pre>\n<p>Here are the joins that are considered when we don&#8217;t specify the hint:<\/p>\n<pre><code>name\tpromised\r\n...\r\nJNtoHS\t2\r\nJNtoSM\t2\r\n...\r\n<\/code><\/pre>\n<p>It can be seen, that only <em>JNtoSM<\/em> (Join to Sort Merge Join) and <em>JNtoHS<\/em> (Join to Hash) have been considered. In contrast, <em>JNtoNL<\/em> (Join to Nested Loop) was discarded. Thus, we can deduce, that some heuristics kicked in. I couldn&#8217;t find any explanation for it, but we can guess that the heuristics is there to pre-empt a disastrous scenario, where we can end up with lot of table scans when there are more than one record in the small outer table. To demonstrate this point, I&#8217;ll add just one more record into the small table and collect the IO statistics:<\/p>\n<pre><code>insert into t_small values (2,2) ;<\/code><\/pre>\n<p>Prior to rerunning the query I&#8217;ll disable the <em>Table Spool (Lazy Spool)<\/em> operations, which would otherwise be introduced and change the execution plan:<\/p>\n<pre><code>DBCC RULEOFF('BuildSpool')\r\nSET STATISTICS IO ON<\/code><\/pre>\n<p>Nested Loop:<\/p>\n<pre><code>Table 't_large'. Scan count 1, <span style=\"color: #ff0000;\">logical reads 7664<\/span>, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.\r\nTable 't_small'. Scan count 1, logical reads 1, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.\r\n<\/code><\/pre>\n<p>Hash Join:<\/p>\n<pre><code>Table 't_large'. Scan count 1, <span style=\"color: #ff0000;\">logical reads 3832<\/span>, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.\r\nTable 't_small'. Scan count 1, logical reads 1, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.\r\n<\/code><\/pre>\n<p>As expected, the large\u00a0inner table in the Nested Loop has to be scanned once again for an additional row in the outer table, while the number of logical reads remained the same for Hash Join. In conclusion, the number of logical reads in the Nested Loop join grows proportionally to the number of records in the inner table. Therefore, there is indeed a compelling reason for the heuristic rule for avoiding nested loops when the index on the join predicate column in the inner table is missing.<\/p>\n<h1>Oracle<\/h1>\n<p>I also checked how the Oracle optimizer behaves under the same conditions. Here&#8217;s the test case:<\/p>\n<pre><code>create user u identified by Temp_1234 ;\r\n\r\ngrant dba to u ;\r\n\r\nconnect u\/Temp_1234\r\n\r\ncreate table t_large ( id number , a number  ) ;\r\ncreate table t_small ( b number , c number ) ;\r\n\r\ninsert into t_large\r\nSELECT level,level\r\nFROM   dual\r\nCONNECT BY level &lt;= 1000000;\r\n\r\ninsert into t_small values (1,1) ;\r\n\r\ncommit ;\r\n\r\nexec dbms_stats.gather_schema_stats('U');\r\n\r\nalter session set tracefile_identifier = 'NL' ;\r\nALTER SESSION SET EVENTS='10053 trace name context forever, level 1' ;\r\nexplain plan for select a from t_large l join t_small s on l.id = s.b ;<\/code><\/pre>\n<p>Unlike in SQL Server, in Oracle, we don&#8217;t need to run the queries with hints to obtain the costing information for different join methods. We can rather extract all the information we need from the optimizer 10053 trace.<br \/>\nLikewise, Oracle opted for Hash Join, although\u00a0Nested Loop was cheaper.<\/p>\n<pre><code>--------------------------------------+-----------------------------------+\r\n| Id  | Operation           | Name    | Rows  | Bytes | Cost  | Time      |\r\n--------------------------------------+-----------------------------------+\r\n| 0   | SELECT STATEMENT    |         |       |       |   284 |           |\r\n| 1   |  <span style=\"color: #ff0000;\">HASH JOIN         <\/span> |         |     1 |    13 |   284 |  00:00:06 |\r\n| 2   |   TABLE ACCESS FULL | T_SMALL |     1 |     3 |     4 |  00:00:01 |\r\n| 3   |   TABLE ACCESS FULL | T_LARGE |  977K | 9766K |   278 |  00:00:06 |\r\n--------------------------------------+-----------------------------------+\r\n\r\n<span style=\"color: #ff0000;\">Best NL cost: 282.775582<\/span>\r\n...\r\nHA Join\r\n  <span style=\"color: #ff0000;\">HA cost: 283.618402<\/span>\r\n...\r\nBest:: JoinMethod: Hash\r\n<\/code><\/pre>\n<p>If you run the test case on a 12c database, you&#8217;ll find a hint that there is indeed some Nested Loop Join heuristics involved in the optimization process:<\/p>\n<pre><code>DP: skipping adaptive plan due to NLJ heuristics<\/code><\/pre>\n<h1>Conclusion<\/h1>\n<p>Both of the observed database optimizers employ heuristics to pre-empt expensive full table scans of the inner table, because the disadvantage of full table scan &#8211; when there are multiple records in the outer table &#8211; far outweighs the overhead of Hash Join compared to Nested Loop, when we have just 0 or 1 records in the outer table.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Not always the cheapest execution plan gets chosen. This blog post describes such an edge case in SQL Server and Oracle. <a href=\"https:\/\/nenadnoveljic.com\/blog\/nested-loop-join-heuristics\/\" class=\"more-link\">Continue Reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"inline_featured_image":false,"footnotes":""},"categories":[21,11,5,17],"tags":[],"class_list":["post-944","post","type-post","status-publish","format-standard","hentry","category-comparison-oracle-sql-server","category-cost-based-optimizer","category-oracle","category-sql-server"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.4 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Nested Loop Join Heuristics - All-round Database Topics<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/nenadnoveljic.com\/blog\/nested-loop-join-heuristics\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Nested Loop Join Heuristics - All-round Database Topics\" \/>\n<meta property=\"og:description\" content=\"Not always the cheapest execution plan gets chosen. This blog post describes such an edge case in SQL Server and Oracle. Continue Reading &rarr;\" \/>\n<meta property=\"og:url\" content=\"https:\/\/nenadnoveljic.com\/blog\/nested-loop-join-heuristics\/\" \/>\n<meta property=\"og:site_name\" content=\"All-round Database Topics\" \/>\n<meta property=\"article:published_time\" content=\"2016-09-03T20:51:29+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2017-04-01T22:10:09+00:00\" \/>\n<meta name=\"author\" content=\"Nenad Noveljic\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@NenadNoveljic\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Nenad Noveljic\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"8 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/nenadnoveljic.com\\\/blog\\\/nested-loop-join-heuristics\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/nenadnoveljic.com\\\/blog\\\/nested-loop-join-heuristics\\\/\"},\"author\":{\"name\":\"Nenad Noveljic\",\"@id\":\"https:\\\/\\\/nenadnoveljic.com\\\/blog\\\/#\\\/schema\\\/person\\\/51458d9dd86dbbdd19f5add451d44efa\"},\"headline\":\"Nested Loop Join Heuristics\",\"datePublished\":\"2016-09-03T20:51:29+00:00\",\"dateModified\":\"2017-04-01T22:10:09+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/nenadnoveljic.com\\\/blog\\\/nested-loop-join-heuristics\\\/\"},\"wordCount\":868,\"commentCount\":0,\"articleSection\":[\"Comparison Oracle-SQL Server\",\"cost based optimizer\",\"Oracle\",\"SQL Server\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/nenadnoveljic.com\\\/blog\\\/nested-loop-join-heuristics\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/nenadnoveljic.com\\\/blog\\\/nested-loop-join-heuristics\\\/\",\"url\":\"https:\\\/\\\/nenadnoveljic.com\\\/blog\\\/nested-loop-join-heuristics\\\/\",\"name\":\"Nested Loop Join Heuristics - All-round Database Topics\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/nenadnoveljic.com\\\/blog\\\/#website\"},\"datePublished\":\"2016-09-03T20:51:29+00:00\",\"dateModified\":\"2017-04-01T22:10:09+00:00\",\"author\":{\"@id\":\"https:\\\/\\\/nenadnoveljic.com\\\/blog\\\/#\\\/schema\\\/person\\\/51458d9dd86dbbdd19f5add451d44efa\"},\"breadcrumb\":{\"@id\":\"https:\\\/\\\/nenadnoveljic.com\\\/blog\\\/nested-loop-join-heuristics\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/nenadnoveljic.com\\\/blog\\\/nested-loop-join-heuristics\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/nenadnoveljic.com\\\/blog\\\/nested-loop-join-heuristics\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/nenadnoveljic.com\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Nested Loop Join Heuristics\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/nenadnoveljic.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/nenadnoveljic.com\\\/blog\\\/\",\"name\":\"All-round Database Topics\",\"description\":\"Nenad Noveljic\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/nenadnoveljic.com\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/nenadnoveljic.com\\\/blog\\\/#\\\/schema\\\/person\\\/51458d9dd86dbbdd19f5add451d44efa\",\"name\":\"Nenad Noveljic\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/a97b796613ea48ec8a7b79c8ffe1c685dcffc920c68121f6238d5caab5070670?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/a97b796613ea48ec8a7b79c8ffe1c685dcffc920c68121f6238d5caab5070670?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/a97b796613ea48ec8a7b79c8ffe1c685dcffc920c68121f6238d5caab5070670?s=96&d=mm&r=g\",\"caption\":\"Nenad Noveljic\"},\"sameAs\":[\"nenad-noveljic-9b746a6\",\"https:\\\/\\\/x.com\\\/NenadNoveljic\"],\"url\":\"https:\\\/\\\/nenadnoveljic.com\\\/blog\\\/author\\\/nenad\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Nested Loop Join Heuristics - All-round Database Topics","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/nenadnoveljic.com\/blog\/nested-loop-join-heuristics\/","og_locale":"en_US","og_type":"article","og_title":"Nested Loop Join Heuristics - All-round Database Topics","og_description":"Not always the cheapest execution plan gets chosen. This blog post describes such an edge case in SQL Server and Oracle. Continue Reading &rarr;","og_url":"https:\/\/nenadnoveljic.com\/blog\/nested-loop-join-heuristics\/","og_site_name":"All-round Database Topics","article_published_time":"2016-09-03T20:51:29+00:00","article_modified_time":"2017-04-01T22:10:09+00:00","author":"Nenad Noveljic","twitter_card":"summary_large_image","twitter_creator":"@NenadNoveljic","twitter_misc":{"Written by":"Nenad Noveljic","Est. reading time":"8 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/nenadnoveljic.com\/blog\/nested-loop-join-heuristics\/#article","isPartOf":{"@id":"https:\/\/nenadnoveljic.com\/blog\/nested-loop-join-heuristics\/"},"author":{"name":"Nenad Noveljic","@id":"https:\/\/nenadnoveljic.com\/blog\/#\/schema\/person\/51458d9dd86dbbdd19f5add451d44efa"},"headline":"Nested Loop Join Heuristics","datePublished":"2016-09-03T20:51:29+00:00","dateModified":"2017-04-01T22:10:09+00:00","mainEntityOfPage":{"@id":"https:\/\/nenadnoveljic.com\/blog\/nested-loop-join-heuristics\/"},"wordCount":868,"commentCount":0,"articleSection":["Comparison Oracle-SQL Server","cost based optimizer","Oracle","SQL Server"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/nenadnoveljic.com\/blog\/nested-loop-join-heuristics\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/nenadnoveljic.com\/blog\/nested-loop-join-heuristics\/","url":"https:\/\/nenadnoveljic.com\/blog\/nested-loop-join-heuristics\/","name":"Nested Loop Join Heuristics - All-round Database Topics","isPartOf":{"@id":"https:\/\/nenadnoveljic.com\/blog\/#website"},"datePublished":"2016-09-03T20:51:29+00:00","dateModified":"2017-04-01T22:10:09+00:00","author":{"@id":"https:\/\/nenadnoveljic.com\/blog\/#\/schema\/person\/51458d9dd86dbbdd19f5add451d44efa"},"breadcrumb":{"@id":"https:\/\/nenadnoveljic.com\/blog\/nested-loop-join-heuristics\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/nenadnoveljic.com\/blog\/nested-loop-join-heuristics\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/nenadnoveljic.com\/blog\/nested-loop-join-heuristics\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/nenadnoveljic.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Nested Loop Join Heuristics"}]},{"@type":"WebSite","@id":"https:\/\/nenadnoveljic.com\/blog\/#website","url":"https:\/\/nenadnoveljic.com\/blog\/","name":"All-round Database Topics","description":"Nenad Noveljic","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/nenadnoveljic.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/nenadnoveljic.com\/blog\/#\/schema\/person\/51458d9dd86dbbdd19f5add451d44efa","name":"Nenad Noveljic","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/a97b796613ea48ec8a7b79c8ffe1c685dcffc920c68121f6238d5caab5070670?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/a97b796613ea48ec8a7b79c8ffe1c685dcffc920c68121f6238d5caab5070670?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/a97b796613ea48ec8a7b79c8ffe1c685dcffc920c68121f6238d5caab5070670?s=96&d=mm&r=g","caption":"Nenad Noveljic"},"sameAs":["nenad-noveljic-9b746a6","https:\/\/x.com\/NenadNoveljic"],"url":"https:\/\/nenadnoveljic.com\/blog\/author\/nenad\/"}]}},"_links":{"self":[{"href":"https:\/\/nenadnoveljic.com\/blog\/wp-json\/wp\/v2\/posts\/944","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/nenadnoveljic.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/nenadnoveljic.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/nenadnoveljic.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/nenadnoveljic.com\/blog\/wp-json\/wp\/v2\/comments?post=944"}],"version-history":[{"count":1,"href":"https:\/\/nenadnoveljic.com\/blog\/wp-json\/wp\/v2\/posts\/944\/revisions"}],"predecessor-version":[{"id":961,"href":"https:\/\/nenadnoveljic.com\/blog\/wp-json\/wp\/v2\/posts\/944\/revisions\/961"}],"wp:attachment":[{"href":"https:\/\/nenadnoveljic.com\/blog\/wp-json\/wp\/v2\/media?parent=944"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/nenadnoveljic.com\/blog\/wp-json\/wp\/v2\/categories?post=944"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/nenadnoveljic.com\/blog\/wp-json\/wp\/v2\/tags?post=944"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}