{"id":73157,"date":"2015-07-07T16:41:41","date_gmt":"2015-07-07T16:41:41","guid":{"rendered":"https:\/\/www.red-gate.com\/simple-talk\/uncategorized\/anatomy-of-a-select-statement-part-3-recursive-subquery-factoring\/"},"modified":"2021-07-14T13:07:23","modified_gmt":"2021-07-14T13:07:23","slug":"anatomy-of-a-select-statement-part-3-recursive-subquery-factoring","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/databases\/oracle-databases\/anatomy-of-a-select-statement-part-3-recursive-subquery-factoring\/","title":{"rendered":"Anatomy Of A SELECT Statement \u2013 Part 3: Recursive Subquery Factoring"},"content":{"rendered":"<p>We need a baritone voiceover man. You know how those huge TV shows &#8211; 24, Prison Break, Jane the Virgin &#8211; always start with a recap sequence to bring you up-to-date in case you&#8217;d missed the last episode. And it&#8217;s always a deep-voiced male narrator: &#8220;Previously on The Walking Dead,&#8221; he&#8217;ll say.<\/p>\n<p>We need that guy.<\/p>\n<p><em>Previously on\u00a0Anatomy of a SELECT Statement<\/em>, we&#8217;ve discussed <a href=\"https:\/\/allthingsoracle.com\/anatomy-of-a-select-statement-part-1-the-with-clause\/\">the subquery factoring clause (aka the With Clause)<\/a>, and we&#8217;ve discussed <a href=\"https:\/\/allthingsoracle.com\/anatomy-of-a-select-statement-part-2-the-hierarchical-query-clause\/\">the hierarchical query clause (aka connect by)<\/a>. In this episode we&#8217;ll be marrying these two topics and talking about recursive subquery factoring.<\/p>\n<p>Recursive subquery factoring. Ah, they love their scary, long names, don&#8217;t they? Basically, it is the ability to carry out hierarchical queries using with clauses (which is why it&#8217;s sometimes called the recursive with clause). And there&#8217;s a good reason for it too. Cos as simple and useful as the connect by syntax is, it is an Oracle-specific solution. However, recursive subquery factoring is ANSI SQL, which means it should pretty much work in any database that speaks the SQL lingua franca. It&#8217;s been part of Oracle since 11gR2 and, while it might take a little getting used to, it provides all the same functionality as connect by.<\/p>\n<p>Let&#8217;s take a look.<\/p>\n<h5>The Basics<\/h5>\n<p>Let&#8217;s revisit the EMP table and run a query that&#8217;ll return the management structure.<\/p>\n<pre>SELECT ename \"Employee\", empno \"Employee ID\", mgr \"Manager ID\"\r\nFROM emp\r\nCONNECT BY PRIOR empno = mgr\r\nSTART WITH mgr IS NULL;\r\n<\/pre>\n<p>Using the recursive with clause syntax we can obtain the same result. I&#8217;ll show you how, and afterwards we&#8217;ll study the query in a little detail.<\/p>\n<pre>WITH m (empno, \"Employee\", mgr) AS\r\n (SELECT empno, ename, mgr\r\n  FROM emp\r\n  WHERE mgr IS NULL\r\n  UNION ALL\r\n  SELECT e.empno, e.ename, m.empno\r\n  FROM emp e, m\r\n  WHERE e.mgr = m.empno)\r\nSELECT * FROM m;\r\n<\/pre>\n<p>Okay, so it&#8217;s a little more verbose, which might seem like a bad thing if you&#8217;re used to terse connect by queries. But let&#8217;s put that to one side for now, and point out a few things \u00a0that we need to know and note.<\/p>\n<p>The with clause must be followed by a list of column or column aliases (in our example it is <em>empno, &#8220;Employee&#8221;, mgr<\/em>). This isn&#8217;t a requirement for ordinary with clauses, but you&#8217;ll get an ORA-32039 error if you omit it when running a recursive query.<\/p>\n<p>The first block is called the anchor member. This is the part that roots your query; it is the equivalent of your START WITH. However, unlike your START WITH, the anchor member is not optional. You must have one.<\/p>\n<p>The second block, which must be cemented to the first by a UNION ALL, is the recursive member. What makes it particularly interesting is that it is joined not to the anchor member, but to the query alias, <em>m<\/em> in our example. It must always do this (reference the query alias); whereas the anchor member must <em>never<\/em> reference the query alias. Additionally, the recursive member must reference the query alias no more than once.<\/p>\n<p>There are a few other restrictions, many of them are obvious. The anchor member must always precede the recursive member; they must always have the same number of columns; the recursive member cannot contain a DISTINCT or a GROUP BY.<\/p>\n<h5>The Search Clause<\/h5>\n<p>The connect by syntax allows for a specialist order by clause, ORDER SIBLINGS BY. What this does, if you don&#8217;t remember, is sort returned rows of the same level. The recursive subquery factoring equivalent for this is the search clause. It comes in two flavours: SEARCH DEPTH FIRST and SEARCH BREADTH FIRST.\u00a0Search Depth First will return the children before the siblings; Search Breadth First does the reverse.<\/p>\n<p>This is how you use it.<\/p>\n<pre>WITH m (empno, Employee, mgr) AS\r\n  (SELECT empno, ename, mgr\r\n   FROM emp\r\n   WHERE mgr IS NULL\r\n   UNION ALL\r\n   SELECT e.empno, e.ename, m.empno\r\n   FROM emp e, m\r\n   WHERE e.mgr = m.empno)\r\nSEARCH DEPTH FIRST by empno ASC SET ordseq\r\nSELECT Employee, empno \"Employee ID\", mgr \"Manager ID\"\r\nFROM m\r\nORDER BY ordseq;\r\n<\/pre>\n<p>In this example, <em>ordseq <\/em>is a pseudocolumn defined, in this case, as empno ASC. It is later used to order our resultset.<\/p>\n<p>Running this query, as opposed to the earlier unsorted one, will show the results in a more organised version. To make that fact clearer, it would be useful if we had the equivalent of the LEVEL pseudocolumn; fortunately, we can construct one.<\/p>\n<pre>WITH m (empno, Employee, mgr, lvl) AS\r\n (SELECT empno, ename, mgr, 1 lvl\r\n  FROM emp\r\n  WHERE mgr IS NULL\r\n  UNION ALL\r\n  SELECT e.empno, e.ename, m.empno, lvl + 1\r\n  FROM emp e, m\r\n  WHERE e.mgr = m.empno)\r\nSEARCH DEPTH FIRST by empno ASC SET ordseq\r\nSELECT Employee, empno \"Employee ID\", mgr \"Manager ID\", lvl \"Level\"\r\nFROM m\r\nORDER BY ordseq;\r\n<\/pre>\n<h5>CYCLE PATH<\/h5>\n<p>If we have circular data (King is Scott&#8217;s boss while Scott is simultaneously King&#8217;s boss), we can save our connect by query from endlessly chasing its tail like an overexcited puppy by including the NOCYCLE keyword. (The truth is a little more prosaic; if we have circular data, Oracle would spit out the ORA-01436 error.)<\/p>\n<p>With recursive subquery factoring, things are a little different. To start off, the error we&#8217;d get is different: <em>ORA-32044: cycle detected while executing recursive with query.<\/em> The solution, unsurprisingly, is a little different too. Using the CYCLE keyword we can untangle this knot and, at the same time, create an equivalent for connect by&#8217;s connect_by_iscycle.<\/p>\n<pre>WITH m (empno, Employee, mgr, lvl) AS\r\n  (SELECT empno, ename, mgr, 1 lvl\r\n   FROM emp\r\n   WHERE mgr IS NULL\r\n   UNION ALL\r\n   SELECT e.empno, e.ename, m.empno, lvl + 1\r\n   FROM emp e, m\r\n   WHERE e.mgr = m.empno)\r\nSEARCH DEPTH FIRST by empno ASC SET ordseq\r\nCYCLE empno SET is_cycle TO 1 DEFAULT 0\r\nSELECT Employee, empno \"Employee ID\", mgr \"Manager ID\", lvl \"Level\", is_cycle\r\nFROM m\r\nORDER BY ordseq;\r\n<\/pre>\n<p>And since we&#8217;re on a roll, coming up with equivalents for connect by features, we might as well construct one for connect_by_path which, if your memory needs refreshing, maps the path from the root to the current record.<\/p>\n<pre>WITH m (empno, Employee, mgr, path) AS\r\n  (SELECT empno, ename, mgr, ename as path\r\n   FROM emp\r\n   WHERE mgr IS NULL\r\n   UNION ALL\r\n   SELECT e.empno, e.ename, m.empno, m.path||' \/ '||e.ename as path\r\n   FROM emp e, m\r\n   WHERE e.mgr = m.empno)\r\nSEARCH DEPTH FIRST by empno ASC SET ordseq\r\nCYCLE empno SET is_cycle TO 1 DEFAULT 0\r\nSELECT Employee, empno \"Employee ID\", mgr \"Manager ID\", is_cycle, path\r\nFROM m\r\nORDER BY ordseq;\r\n<\/pre>\n<h5>Conclusion<\/h5>\n<p>The Pacific island nation of Vanuatu has the most beautiful Pidgin English. It is wittily verbose in places where English is dry and terse. So instead of &#8220;son&#8221;, they say \u00a0<em>boe blong mi<\/em>\u00a0(boy that belongs to me); to say &#8220;daughter&#8221; they simply substitute the &#8216;<em>boe<\/em>&#8216; for &#8216;<em>gel<\/em>&#8216;. That&#8217;s the beauty of verbosity; it gives you the opportunity to make minor tweaks that may have major effects.<\/p>\n<p>Whenever I need to write a quick, simple query, I still use connect by. (Chances of it being deprecated and phased out any time soon are, I think, slim.) However, when the requirement is more complex, I rely on the more verbose recursive with clause. Understanding how to use both of them will give you the opportunity to do the same.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>We need a baritone voiceover man. You know how those huge TV shows &#8211; 24, Prison Break, Jane the Virgin &#8211; always start with a recap sequence to bring you up-to-date in case you&#8217;d missed the last episode. And it&#8217;s always a deep-voiced male narrator: &#8220;Previously on The Walking Dead,&#8221; he&#8217;ll say. We need that guy. Previously on\u00a0Anatomy of a&hellip;<\/p>\n","protected":false},"author":221907,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[143533],"tags":[],"coauthors":[],"class_list":["post-73157","post","type-post","status-publish","format-standard","hentry","category-oracle-databases"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/73157","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/221907"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=73157"}],"version-history":[{"count":1,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/73157\/revisions"}],"predecessor-version":[{"id":91650,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/73157\/revisions\/91650"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=73157"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=73157"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=73157"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=73157"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}