{"id":7044,"date":"2023-06-21T00:00:00","date_gmt":"2023-06-21T04:00:00","guid":{"rendered":"https:\/\/www.sisense.com\/postgres-recursive-cte\/"},"modified":"2024-10-01T11:04:29","modified_gmt":"2024-10-01T15:04:29","slug":"postgres-recursive-cte","status":"publish","type":"post","link":"https:\/\/www.sisense.com\/blog\/postgres-recursive-cte\/","title":{"rendered":"Solving the traveling salesman problem with Postgres recursive CTEs"},"content":{"rendered":"<p><\/p>\r\n<p>Many SQL implementations don\u2019t have loops, making some kinds of analysis very difficult. Postgres, SQL Server, and several others have the next best thing \u2014 recursive CTEs!<\/p>\r\n<p>We\u2019ll use them to solve the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Travelling_salesman_problem\" target=\"_blank\" rel=\"noreferrer noopener\" aria-label=\" (opens in a new tab)\">Traveling Salesman Problem<\/a> and find the shortest round-trip route through several US cities.<\/p>\r\n<figure class=\"wp-block-image fancybox\"><img decoding=\"async\" class=\"wp-image-78389\" src=\"https:\/\/cdn.sisense.com\/wp-content\/uploads\/image-01-13.png\" alt=\"Route map\" \/><\/figure>\r\n<h2>With recursive<\/h2>\r\n<p>Normal CTEs are great at helping to organize large queries. They are a simple way to make temporary tables you can access later in your query.<\/p>\r\n<p><a href=\"https:\/\/www.postgresql.org\/docs\/8.4\/queries-with.html\" target=\"_blank\" rel=\"noreferrer noopener\" aria-label=\" (opens in a new tab)\">Recursive CTEs<\/a> are more powerful &#8211; they reference themselves and allow you to explore hierarchical data. While that may sound complicated, the underlying concept is very similar to a\u00a0<em><strong>for<\/strong><\/em>\u00a0loop in other programming languages.<\/p>\r\n<p>These CTEs have two parts \u2014 an\u00a0<em><strong>anchor<\/strong><\/em>\u00a0member and a\u00a0<em><strong>recursive<\/strong><\/em>\u00a0member. The\u00a0<em><strong>anchor<\/strong><\/em>\u00a0member selects the starting rows for the recursive steps.<\/p>\r\n<p>The\u00a0<em><strong>recursive<\/strong><\/em>\u00a0member generates more rows for the CTE by first joining against the\u00a0<em><strong>anchor<\/strong><\/em>\u00a0rows, and then joining against rows created in previous recursions. The\u00a0<em><strong>recursive<\/strong><\/em>\u00a0member comes after a\u00a0<em><strong>union all<\/strong><\/em>\u00a0in the CTE definition.<\/p>\r\n<p>Here\u2019s a simple recursive CTE that generates the numbers 1 to 10. The\u00a0<em><strong>anchor<\/strong><\/em>\u00a0member selects the value 1, and the\u00a0<em><strong>recursive<\/strong><\/em>\u00a0member adds to it up to the number 10:<\/p>\r\n<pre class=\"wp-block-code\"><code>with recursive incrementer(prev_val) as (\r\n  select 1 -- anchor member\r\n  union all\r\n  select -- recursive member\r\n    incrementer.prev_val + 1\r\n  from incrementer\r\n  where prev_val <\/code><\/pre>\r\n<p>The first time the recursive CTE runs it generates a single row\u00a0<em><strong>1<\/strong><\/em>\u00a0using the\u00a0<em><strong>anchor<\/strong><\/em>\u00a0member. In the second execution, the\u00a0<em><strong>recursive<\/strong><\/em>\u00a0member joins against the\u00a0<em><strong>1<\/strong><\/em>\u00a0and outputs a second row,\u00a0<em><strong>2<\/strong><\/em>. In the third execution the\u00a0<strong>recursive<\/strong>\u00a0step joins against both rows\u00a0<em><strong>1<\/strong><\/em>\u00a0and\u00a0<em><strong>2<\/strong><\/em>\u00a0and adds the rows\u00a0<em><strong>2<\/strong><\/em>\u00a0(a duplicate) and\u00a0<em><strong>3<\/strong><\/em>.<\/p>\r\n<p>Recursive CTEs also only return distinct rows. Even though our CTE above creates many rows with the same value, only a distinct set of rows will be returned.<\/p>\r\n<p>Notice how the CTE specifies its output as the named value\u00a0<em><strong>prev_val<\/strong><\/em>. This lets us refer to the output of the previous recursive step.<\/p>\r\n<p>And at the very end there is a termination condition to halt the recursion once the sum gets to 10. Without this condition, the CTE would enter an infinite loop!<\/p>\r\n<p>Under the hood, the database is building up a table named after this recursive CTE using unions:<\/p>\r\n<figure class=\"wp-block-image fancybox\"><img decoding=\"async\" class=\"wp-image-78383\" src=\"https:\/\/cdn.sisense.com\/wp-content\/uploads\/image-02-13.png\" alt=\"Recursive union\" \/><\/figure>\r\n<p>Recursive CTEs can also have many parameters. Here\u2019s one that takes the sum, double, and square of starting values of 1, 2 and 3:<\/p>\r\n<pre class=\"wp-block-code\"><code>with recursive cruncher(inc, double, square) as (\r\n  select 1, 2.0, 3.0 -- anchor member\r\n  union all\r\n  select -- recursive member\r\n    cruncher.inc + 1,\r\n    cruncher.double * 2,\r\n    cruncher.square ^ 2\r\n  from cruncher\r\n  where inc <\/code><\/pre>\r\n<p>With recursive CTEs, we can solve the Traveling Salesman Problem.<\/p>\r\n<h2>Finding the shortest path<\/h2>\r\n<p>There are many algorithms for finding the shortest round-trip path through several cities. We\u2019ll use the simplest: brute force. Our recursive CTE will enumerate all possible routes and their total distances. We\u2019ll then sort to find the shortest.<\/p>\r\n<p>First, a list of cities with our customers, along with their latitudes and longitudes:<\/p>\r\n<pre class=\"wp-block-code\"><code>create table places as (\r\n  select\r\n    'Seattle' as name, 47.6097 as lat, 122.3331 as lon\r\n    union all select 'San Francisco', 37.7833, 122.4167\r\n    union all select 'Austin', 30.2500, 97.7500\r\n    union all select 'New York', 40.7127, 74.0059\r\n    union all select 'Boston', 42.3601, 71.0589\r\n    union all select 'Chicago', 41.8369, 87.6847\r\n    union all select 'Los Angeles', 34.0500, 118.2500\r\n    union all select 'Denver', 39.7392, 104.9903\r\n)<\/code><\/pre>\r\n<p>And we\u2019ll need a distance function to compute how far two lat\/lons are from each other (thanks to <a href=\"https:\/\/stackoverflow.com\/questions\/10034636\/postgresql-latitude-longitude-query\" target=\"_blank\" rel=\"noreferrer noopener\" aria-label=\" (opens in a new tab)\">strkol on stackoverflow.com<\/a>):<\/p>\r\n<pre class=\"wp-block-code\"><code>create or replace function lat_lon_distance(\r\n  lat1 float, lon1 float, lat2 float, lon2 float\r\n) returns float as $$\r\ndeclare\r\n  x float = 69.1 * (lat2 - lat1);\r\n  y float = 69.1 * (lon2 - lon1) * cos(lat1 \/ 57.3);\r\nbegin\r\n  return sqrt(x * x + y * y);\r\nend\r\n$$ language plpgsql<\/code><\/pre>\r\n<p>Our CTE will use San Francisco as its anchor city, and then recurse from there to every other city:<\/p>\r\n<pre class=\"wp-block-code\"><code>with recursive travel(places_chain, last_lat, last_lon,\r\n    total_distance, num_places) as (\r\n  select -- anchor member\r\n    name, lat, lon, 0::float, 1\r\n    from places\r\n    where name = 'San Francisco'\r\n  union all\r\n  select -- recursive member\r\n    -- add to the current places_chain\r\n    travel.places_chain || ' -&gt; ' || places.name,\r\n    places.lat,\r\n    places.lon,\r\n    -- add to the current total_distance\r\n    travel.total_distance + \r\n      lat_lon_distance(last_lat, last_lon, places.lat, places.lon),\r\n    travel.num_places + 1\r\n  from\r\n    places, travel\r\n  where\r\n    position(places.name in travel.places_chain) = 0\r\n)<\/code><\/pre>\r\n<p>The parameters in the CTE are:<\/p>\r\n<ul>\r\n<li><em><strong>places_chain<\/strong><\/em>: The list of places visited so far, which will be different for each instance of the recursion<\/li>\r\n<li><em><strong>last_lat and last_lon<\/strong><\/em>: The latitude and longitude of the last place in the\u00a0<em><strong>places_chain<\/strong><\/em><\/li>\r\n<li><em><strong>total_distance<\/strong><\/em>: The distance traveled going from one place to the next in the\u00a0<em><strong>places_chain<\/strong><\/em><\/li>\r\n<li><em><strong>num_places<\/strong><\/em>: The number of places in\u00a0<em><strong>places_chain<\/strong><\/em>\u00a0\u2014 we\u2019ll use this to tell which routes are complete because they visited all cities<\/li>\r\n<\/ul>\r\n<p>In the\u00a0<em><strong>recursive<\/strong><\/em>\u00a0member, the\u00a0<em><strong>where<\/strong><\/em>\u00a0clause ensures that we never repeat a place. If we\u2019ve already visited Denver,\u00a0<em><strong>position(&#8230;)<\/strong><\/em>\u00a0will return a number greater than 0, invalidating this instance of the recursion.<\/p>\r\n<p>We can see all possible routes by selecting all 8-city chains:<\/p>\r\n<pre class=\"wp-block-code\"><code>select * from travel where num_places = 8<\/code><\/pre>\r\n<p>We need to add in the distance from the last city back to San Francisco to complete the round-trip. We could hard code San Francisco\u2019s lat\/lon, but a join is more elegant. Once that\u2019s done we sort by distance and show the smallest:<\/p>\r\n<pre class=\"wp-block-code\"><code>select\r\n  travel.places_chain || ' -&gt; ' || places.name,\r\n  total_distance + lat_lon_distance(\r\n      travel.last_lat, travel.last_lon,\r\n      places.lat, places.lon) as final_dist\r\nfrom travel, places\r\nwhere\r\n  travel.num_places = 8\r\n  and places.name = 'San Francisco'\r\norder by 2 -- ascending!\r\nlimit 1<\/code><\/pre>\r\n<p>Even though this query is significantly more complicated than the\u00a0<em><strong>incrementer<\/strong><\/em>\u00a0query earlier, the database is doing the same things behind the scenes. The top branch is the creating the CTE\u2019s rows, the bottom branch is the final join and sort:<\/p>\r\n<figure class=\"wp-block-image fancybox\"><img decoding=\"async\" class=\"wp-image-78395\" src=\"https:\/\/cdn.sisense.com\/wp-content\/uploads\/image-03-12.png\" alt=\"Complicated recursive union\" \/><\/figure>\r\n<p>Run this query and you\u2019ll see the shortest route takes 6671 miles and visits the cities in this order:<\/p>\r\n<pre class=\"wp-block-preformatted\"> San Francisco -&gt; Seattle -&gt; Denver -&gt;\r\nChicago -&gt; Boston -&gt; New York -&gt; Austin -&gt;\r\nLos Angeles -&gt; San Francisco <\/pre>\r\n<p>Thanks to recursive CTEs, we can solve the Traveling Salesman Problem in SQL!<\/p>\r\n<p>\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n<\/p>","protected":false},"excerpt":{"rendered":"<p>Many SQL implementations don\u2019t have loops, making some kinds of analysis very difficult. Postgres, SQL Server, and several others have the next best thing \u2014 recursive CTEs! We\u2019ll use them to solve the Traveling Salesman Problem and find the shortest&#8230;<\/p>\n","protected":false},"author":4,"featured_media":7231,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_searchwp_excluded":"","footnotes":"","_links_to":"","_links_to_target":""},"categories":[44],"tags":[472],"application":[46,10,26],"buyer-role":[],"buyer-stage":[],"department":[],"industry":[],"topic":[],"class_list":["post-7044","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-tech-talk","tag-data-team","application-bi-analytics-teams","application-cloud-data-teams","application-product-teams"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v23.5 (Yoast SEO v23.8) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Solving the Traveling Salesman Problem with Postgres<\/title>\n<meta name=\"description\" content=\"Recursive CTEs are powerful - they reference themselves and allow you to explore hierarchical data so you can organize large queries.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.sisense.com\/blog\/postgres-recursive-cte\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Solving the traveling salesman problem with Postgres recursive CTEs\" \/>\n<meta property=\"og:description\" content=\"Many SQL implementations don\u2019t have loops, making some kinds of analysis very difficult. Postgres, SQL Server, and several others have the next best thing\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.sisense.com\/blog\/postgres-recursive-cte\/\" \/>\n<meta property=\"og:site_name\" content=\"Sisense\" \/>\n<meta property=\"article:published_time\" content=\"2023-06-21T04:00:00+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2024-10-01T15:04:29+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/cdn.sisense.com\/wp-content\/uploads\/yoast-recursive-blog-min.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"628\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Sisense Team\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:image\" content=\"https:\/\/cdn.sisense.com\/wp-content\/uploads\/yoast-recursive-blog-min.jpg\" \/>\n<meta name=\"twitter:creator\" content=\"@sisense\" \/>\n<meta name=\"twitter:site\" content=\"@sisense\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Sisense Team\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"4 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.sisense.com\/blog\/postgres-recursive-cte\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.sisense.com\/blog\/postgres-recursive-cte\/\"},\"author\":{\"name\":\"Sisense Team\",\"@id\":\"https:\/\/www.sisense.com\/#\/schema\/person\/e70aa3a7bbc471e4b7b8c5a7d2b36115\"},\"headline\":\"Solving the traveling salesman problem with Postgres recursive CTEs\",\"datePublished\":\"2023-06-21T04:00:00+00:00\",\"dateModified\":\"2024-10-01T15:04:29+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.sisense.com\/blog\/postgres-recursive-cte\/\"},\"wordCount\":735,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/www.sisense.com\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.sisense.com\/blog\/postgres-recursive-cte\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/cdn.sisense.com\/wp-content\/uploads\/2015\/09\/10161847\/featured-recursive-blog-min.jpg\",\"keywords\":[\"data team\"],\"articleSection\":[\"Tech Talk\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.sisense.com\/blog\/postgres-recursive-cte\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.sisense.com\/blog\/postgres-recursive-cte\/\",\"url\":\"https:\/\/www.sisense.com\/blog\/postgres-recursive-cte\/\",\"name\":\"Solving the Traveling Salesman Problem with Postgres\",\"isPartOf\":{\"@id\":\"https:\/\/www.sisense.com\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.sisense.com\/blog\/postgres-recursive-cte\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.sisense.com\/blog\/postgres-recursive-cte\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/cdn.sisense.com\/wp-content\/uploads\/2015\/09\/10161847\/featured-recursive-blog-min.jpg\",\"datePublished\":\"2023-06-21T04:00:00+00:00\",\"dateModified\":\"2024-10-01T15:04:29+00:00\",\"description\":\"Recursive CTEs are powerful - they reference themselves and allow you to explore hierarchical data so you can organize large queries.\",\"breadcrumb\":{\"@id\":\"https:\/\/www.sisense.com\/blog\/postgres-recursive-cte\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.sisense.com\/blog\/postgres-recursive-cte\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.sisense.com\/blog\/postgres-recursive-cte\/#primaryimage\",\"url\":\"https:\/\/cdn.sisense.com\/wp-content\/uploads\/2015\/09\/10161847\/featured-recursive-blog-min.jpg\",\"contentUrl\":\"https:\/\/cdn.sisense.com\/wp-content\/uploads\/2015\/09\/10161847\/featured-recursive-blog-min.jpg\",\"width\":1200,\"height\":628,\"caption\":\"featured recursive blog min\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.sisense.com\/blog\/postgres-recursive-cte\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.sisense.com\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Solving the traveling salesman problem with Postgres recursive CTEs\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.sisense.com\/#website\",\"url\":\"https:\/\/www.sisense.com\/\",\"name\":\"Sisense\",\"description\":\"Build your business with anywhere-analytics\",\"publisher\":{\"@id\":\"https:\/\/www.sisense.com\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.sisense.com\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.sisense.com\/#organization\",\"name\":\"Sisense\",\"url\":\"https:\/\/www.sisense.com\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.sisense.com\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/cdn.sisense.com\/wp-content\/uploads\/sisense-yoast-og.jpg\",\"contentUrl\":\"https:\/\/cdn.sisense.com\/wp-content\/uploads\/sisense-yoast-og.jpg\",\"width\":1200,\"height\":600,\"caption\":\"Sisense\"},\"image\":{\"@id\":\"https:\/\/www.sisense.com\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/x.com\/sisense\",\"https:\/\/www.linkedin.com\/company\/sisense\",\"https:\/\/github.com\/sisense\/\"],\"description\":\"Sisense accelerates product innovation through AI\/ML capabilities. Our global analytics platform lets customers drive better, faster decisions for their business and end users.\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.sisense.com\/#\/schema\/person\/e70aa3a7bbc471e4b7b8c5a7d2b36115\",\"name\":\"Sisense Team\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.sisense.com\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/213e415f47bc3c7f0155a0755b1cea8c?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/213e415f47bc3c7f0155a0755b1cea8c?s=96&d=mm&r=g\",\"caption\":\"Sisense Team\"}}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Solving the Traveling Salesman Problem with Postgres","description":"Recursive CTEs are powerful - they reference themselves and allow you to explore hierarchical data so you can organize large queries.","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:\/\/www.sisense.com\/blog\/postgres-recursive-cte\/","og_locale":"en_US","og_type":"article","og_title":"Solving the traveling salesman problem with Postgres recursive CTEs","og_description":"Many SQL implementations don\u2019t have loops, making some kinds of analysis very difficult. Postgres, SQL Server, and several others have the next best thing","og_url":"https:\/\/www.sisense.com\/blog\/postgres-recursive-cte\/","og_site_name":"Sisense","article_published_time":"2023-06-21T04:00:00+00:00","article_modified_time":"2024-10-01T15:04:29+00:00","og_image":[{"width":1200,"height":628,"url":"https:\/\/cdn.sisense.com\/wp-content\/uploads\/yoast-recursive-blog-min.jpg","type":"image\/jpeg"}],"author":"Sisense Team","twitter_card":"summary_large_image","twitter_image":"https:\/\/cdn.sisense.com\/wp-content\/uploads\/yoast-recursive-blog-min.jpg","twitter_creator":"@sisense","twitter_site":"@sisense","twitter_misc":{"Written by":"Sisense Team","Est. reading time":"4 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.sisense.com\/blog\/postgres-recursive-cte\/#article","isPartOf":{"@id":"https:\/\/www.sisense.com\/blog\/postgres-recursive-cte\/"},"author":{"name":"Sisense Team","@id":"https:\/\/www.sisense.com\/#\/schema\/person\/e70aa3a7bbc471e4b7b8c5a7d2b36115"},"headline":"Solving the traveling salesman problem with Postgres recursive CTEs","datePublished":"2023-06-21T04:00:00+00:00","dateModified":"2024-10-01T15:04:29+00:00","mainEntityOfPage":{"@id":"https:\/\/www.sisense.com\/blog\/postgres-recursive-cte\/"},"wordCount":735,"commentCount":0,"publisher":{"@id":"https:\/\/www.sisense.com\/#organization"},"image":{"@id":"https:\/\/www.sisense.com\/blog\/postgres-recursive-cte\/#primaryimage"},"thumbnailUrl":"https:\/\/cdn.sisense.com\/wp-content\/uploads\/2015\/09\/10161847\/featured-recursive-blog-min.jpg","keywords":["data team"],"articleSection":["Tech Talk"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.sisense.com\/blog\/postgres-recursive-cte\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.sisense.com\/blog\/postgres-recursive-cte\/","url":"https:\/\/www.sisense.com\/blog\/postgres-recursive-cte\/","name":"Solving the Traveling Salesman Problem with Postgres","isPartOf":{"@id":"https:\/\/www.sisense.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.sisense.com\/blog\/postgres-recursive-cte\/#primaryimage"},"image":{"@id":"https:\/\/www.sisense.com\/blog\/postgres-recursive-cte\/#primaryimage"},"thumbnailUrl":"https:\/\/cdn.sisense.com\/wp-content\/uploads\/2015\/09\/10161847\/featured-recursive-blog-min.jpg","datePublished":"2023-06-21T04:00:00+00:00","dateModified":"2024-10-01T15:04:29+00:00","description":"Recursive CTEs are powerful - they reference themselves and allow you to explore hierarchical data so you can organize large queries.","breadcrumb":{"@id":"https:\/\/www.sisense.com\/blog\/postgres-recursive-cte\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.sisense.com\/blog\/postgres-recursive-cte\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.sisense.com\/blog\/postgres-recursive-cte\/#primaryimage","url":"https:\/\/cdn.sisense.com\/wp-content\/uploads\/2015\/09\/10161847\/featured-recursive-blog-min.jpg","contentUrl":"https:\/\/cdn.sisense.com\/wp-content\/uploads\/2015\/09\/10161847\/featured-recursive-blog-min.jpg","width":1200,"height":628,"caption":"featured recursive blog min"},{"@type":"BreadcrumbList","@id":"https:\/\/www.sisense.com\/blog\/postgres-recursive-cte\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.sisense.com\/"},{"@type":"ListItem","position":2,"name":"Solving the traveling salesman problem with Postgres recursive CTEs"}]},{"@type":"WebSite","@id":"https:\/\/www.sisense.com\/#website","url":"https:\/\/www.sisense.com\/","name":"Sisense","description":"Build your business with anywhere-analytics","publisher":{"@id":"https:\/\/www.sisense.com\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.sisense.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.sisense.com\/#organization","name":"Sisense","url":"https:\/\/www.sisense.com\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.sisense.com\/#\/schema\/logo\/image\/","url":"https:\/\/cdn.sisense.com\/wp-content\/uploads\/sisense-yoast-og.jpg","contentUrl":"https:\/\/cdn.sisense.com\/wp-content\/uploads\/sisense-yoast-og.jpg","width":1200,"height":600,"caption":"Sisense"},"image":{"@id":"https:\/\/www.sisense.com\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/x.com\/sisense","https:\/\/www.linkedin.com\/company\/sisense","https:\/\/github.com\/sisense\/"],"description":"Sisense accelerates product innovation through AI\/ML capabilities. Our global analytics platform lets customers drive better, faster decisions for their business and end users."},{"@type":"Person","@id":"https:\/\/www.sisense.com\/#\/schema\/person\/e70aa3a7bbc471e4b7b8c5a7d2b36115","name":"Sisense Team","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.sisense.com\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/213e415f47bc3c7f0155a0755b1cea8c?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/213e415f47bc3c7f0155a0755b1cea8c?s=96&d=mm&r=g","caption":"Sisense Team"}}]}},"_links":{"self":[{"href":"https:\/\/www.sisense.com\/wp-json\/wp\/v2\/posts\/7044"}],"collection":[{"href":"https:\/\/www.sisense.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.sisense.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.sisense.com\/wp-json\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/www.sisense.com\/wp-json\/wp\/v2\/comments?post=7044"}],"version-history":[{"count":0,"href":"https:\/\/www.sisense.com\/wp-json\/wp\/v2\/posts\/7044\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.sisense.com\/wp-json\/wp\/v2\/media\/7231"}],"wp:attachment":[{"href":"https:\/\/www.sisense.com\/wp-json\/wp\/v2\/media?parent=7044"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.sisense.com\/wp-json\/wp\/v2\/categories?post=7044"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.sisense.com\/wp-json\/wp\/v2\/tags?post=7044"},{"taxonomy":"application","embeddable":true,"href":"https:\/\/www.sisense.com\/wp-json\/wp\/v2\/application?post=7044"},{"taxonomy":"buyer-role","embeddable":true,"href":"https:\/\/www.sisense.com\/wp-json\/wp\/v2\/buyer-role?post=7044"},{"taxonomy":"buyer-stage","embeddable":true,"href":"https:\/\/www.sisense.com\/wp-json\/wp\/v2\/buyer-stage?post=7044"},{"taxonomy":"department","embeddable":true,"href":"https:\/\/www.sisense.com\/wp-json\/wp\/v2\/department?post=7044"},{"taxonomy":"industry","embeddable":true,"href":"https:\/\/www.sisense.com\/wp-json\/wp\/v2\/industry?post=7044"},{"taxonomy":"topic","embeddable":true,"href":"https:\/\/www.sisense.com\/wp-json\/wp\/v2\/topic?post=7044"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}