{"id":15705,"date":"2024-05-09T10:03:54","date_gmt":"2024-05-09T17:03:54","guid":{"rendered":"https:\/\/www.couchbase.com\/blog\/?p=15705"},"modified":"2024-05-22T13:47:54","modified_gmt":"2024-05-22T20:47:54","slug":"query-graph-recursive-cte","status":"publish","type":"post","link":"https:\/\/www.couchbase.com\/blog\/query-graph-recursive-cte\/","title":{"rendered":"Query Graph Models With Couchbase Recursive CTE"},"content":{"rendered":"<p><span style=\"font-weight: 400;\">Recursive Common Table Expressions (CTEs) and Oracle&#8217;s CONNECT BY are well-known SQL constructs among RDBMS users, facilitating the delegation of complex, interdependent data structure exploration to the database layer for enhanced processing efficiency.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">These constructs are crucial for querying interdependent data structures, a common requirement across various industries, including finance, supply chain management, customer relationship management (CRM), travel booking, and more recently, social networks. Recognizing their importance, all major relational database management systems (RDBMS) such as PostgreSQL, MySQL (from version 8.0), SQL Server, Oracle, and SQLite offer support for Recursive CTEs.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">In contrast, NoSQL databases, which are designed to manage a diverse array of data models like documents, key-value, wide-column, and graph data, prioritize scalability, high availability, flexibility, and performance in distributed systems. In these environments, the CTE concept, recursive or not, isn&#8217;t directly addressed. Users often turn to specialized solutions such as graph databases\u2014Cypher for Neo4J and AQL for ArangoDB, for instance\u2014to handle complex data structures.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Couchbase sets itself apart with SQL for JSON, offering a unique approach to Recursive CTEs that also extend its multi model support. This blog will delve into three primary topics:<\/span><\/p>\n<ol>\n<li style=\"list-style-type: none;\">\n<ol>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">How you can leverage a single DBMS as Couchbase for complex data structures for a number of use cases, in the same way as RDBMS could, but without the need for a more dedicated database.\u00a0<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">The use of Couchase SQL++ construct to query, transform and project these complex relationship using a SQL construct that are familiar to RDBMS users.<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Best practices to manage resource consumption with Recursive CTE.<\/span><\/li>\n<\/ol>\n<\/li>\n<\/ol>\n<h2><span style=\"font-weight: 400;\">Bill of Materials use case<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">The BOM is a critical component in manufacturing and engineering, detailing the raw materials, parts, and components needed to manufacture a product. It often has a hierarchical structure, where parts are made up of other parts or materials.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">This example will include basic components and sub-components of a desktop computer.<\/span><\/p>\n<table>\n<tbody>\n<tr>\n<td><b>Component ID<\/b><\/td>\n<td><b>ComponentName<\/b><\/td>\n<td><b>ParentComponentID<\/b><\/td>\n<td><b>Quantity<\/b><\/td>\n<\/tr>\n<tr>\n<td><span>1<\/span><\/td>\n<td><span>Desktop Computer<\/span><\/td>\n<td><span>null<\/span><\/td>\n<td><span>1<\/span><\/td>\n<\/tr>\n<tr>\n<td><span>2<\/span><\/td>\n<td><span>Motherboard<\/span><\/td>\n<td><span>1<\/span><\/td>\n<td><span>1<\/span><\/td>\n<\/tr>\n<tr>\n<td><span>3<\/span><\/td>\n<td><span>CPU<\/span><\/td>\n<td><span>2<\/span><\/td>\n<td><span>1<\/span><\/td>\n<\/tr>\n<tr>\n<td><span>3<\/span><\/td>\n<td><span>CPU fan<\/span><\/td>\n<td><span>3<\/span><\/td>\n<td><span>1<\/span><\/td>\n<\/tr>\n<tr>\n<td><span>4<\/span><\/td>\n<td><span>GPU<\/span><\/td>\n<td><span>2<\/span><\/td>\n<td><span>1<\/span><\/td>\n<\/tr>\n<tr>\n<td><span>5<\/span><\/td>\n<td><span>RAM<\/span><\/td>\n<td><span>2<\/span><\/td>\n<td><span>4<\/span><\/td>\n<\/tr>\n<tr>\n<td><span>6<\/span><\/td>\n<td><span>M.2 drive<\/span><\/td>\n<td><span>2<\/span><\/td>\n<td><span>1<\/span><\/td>\n<\/tr>\n<tr>\n<td><span>7<\/span><\/td>\n<td><span>SSD<\/span><\/td>\n<td><span>2<\/span><\/td>\n<td><span>1<\/span><\/td>\n<\/tr>\n<tr>\n<td><span>8<\/span><\/td>\n<td><span>Power Supply<\/span><\/td>\n<td><span>1<\/span><\/td>\n<td><span>1<\/span><\/td>\n<\/tr>\n<tr>\n<td><span>9<\/span><\/td>\n<td><span>Case<\/span><\/td>\n<td><span>1<\/span><\/td>\n<td><span>1<\/span><\/td>\n<\/tr>\n<tr>\n<td><span>10<\/span><\/td>\n<td><span>Case cooling fans<\/span><\/td>\n<td><span>1<\/span><\/td>\n<td><span>4<\/span><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><span style=\"font-weight: 400;\">Let&#8217;s say we want to list all parts and sub-parts needed to build a desktop computer, along with the quantities required for each.<\/span><\/p>\n<pre class=\"nums:false lang:default decode:true \">WITH RECURSIVE ComponentHierarchy AS (\r\n\u00a0 \u00a0 SELECT\r\n\u00a0 \u00a0 \u00a0 \u00a0 ComponentID,\r\n\u00a0 \u00a0 \u00a0 \u00a0 ComponentName,\r\n\u00a0 \u00a0 \u00a0 \u00a0 ParentComponentID,\r\n\u00a0 \u00a0 \u00a0 \u00a0 Quantity,\r\n\u00a0 \u00a0 \u00a0 \u00a0 1 AS Lvl -- Depth level in the hierarchy\r\n\u00a0 \u00a0 FROM\r\n\u00a0 \u00a0 \u00a0 \u00a0 components\r\n\u00a0 \u00a0 WHERE\r\n\u00a0 \u00a0 \u00a0 \u00a0 ParentComponentID IS NULL -- Starting point: the Desktop Computer itself\r\n\r\n\u00a0 \u00a0 UNION ALL\r\n\r\n\u00a0 \u00a0 SELECT\r\n\u00a0 \u00a0 \u00a0 \u00a0 c.ComponentID,\r\n\u00a0 \u00a0 \u00a0 \u00a0 c.ComponentName,\r\n\u00a0 \u00a0 \u00a0 \u00a0 c.ParentComponentID,\r\n\u00a0 \u00a0 \u00a0 \u00a0 ch.Quantity * c.Quantity AS Quantity, -- Calculate total quantity required at each level\r\n\u00a0 \u00a0 \u00a0 \u00a0 ch.lvl + 1 -- Increment level for each recursion step\r\n\u00a0 \u00a0 FROM\r\n\u00a0 \u00a0 \u00a0 \u00a0 components c\r\n\u00a0 \u00a0 JOIN\r\n\u00a0 \u00a0 \u00a0 \u00a0 ComponentHierarchy ch ON c.ParentComponentID = ch.ComponentID\r\n)\r\nSELECT * FROM ComponentHierarchy;<\/pre>\n<p><span style=\"font-weight: 400;\">This query initializes the recursion with the top-level item (the Desktop Computer) and recursively joins the components table with itself to traverse down the hierarchy, adjusting quantities as needed for each component and sub-component.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">The result will list all parts needed for the Desktop Computer, including the quantity of each and their hierarchical level, which can be useful for understanding the assembly structure or for inventory and ordering purposes.<\/span><\/p>\n<h3><span style=\"font-weight: 400;\">Explanation<\/span><\/h3>\n<p><span style=\"font-weight: 400;\">The CTE starts with the Bicycle (where ParentPartID is NULL) and then recursively finds all components and sub-components.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">The Quantity is adjusted at each level to reflect the total number required for one Bicycle.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">The Level column, though not strictly necessary for all BOM analyses, helps understand the depth of each part within the hierarchy.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">This approach allows for a detailed breakdown of all materials and components required for manufacturing a product, essential for inventory management, cost estimation, and production planning in manufacturing operations.<\/span><\/p>\n<h2><span style=\"font-weight: 400;\">Social Networking use case<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">A common application in a social networking use case is to find the degrees of connection between two users\u2014essentially, how users are connected through a chain of mutual friends. Let&#8217;s say we need to determine the shortest path (in terms of degrees of connection) between two users in a social network. This can help in features like suggesting friendships or understanding network dynamics.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Consider we have the following users and how they are friends with each other, eg. Alice is friends with Bob, and also with Charlie. But Alice is not friends with Dana.<\/span><\/p>\n<p><br style=\"font-weight: 400;\" \/><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-15706\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2024\/05\/image1.png\" alt=\"\" width=\"624\" height=\"129\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/05\/image1.png 624w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/05\/image1-300x62.png 300w\" sizes=\"auto, (max-width: 624px) 100vw, 624px\" \/><br style=\"font-weight: 400;\" \/><br style=\"font-weight: 400;\" \/><\/p>\n<p><span style=\"font-weight: 400;\">In this expanded network, users are connected in various ways, creating multiple paths through which users can be connected.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Let&#8217;s find the degrees of connection between Alice[1] and Frank[6], including the names of users along the path.<\/span><\/p>\n<pre class=\"nums:false lang:default decode:true \">WITH RECURSIVE ConnectionPath AS (\r\n\u00a0 \u00a0 SELECT\r\n\u00a0 \u00a0 \u00a0 \u00a0 u1.UserID,\r\n\u00a0 \u00a0 \u00a0 \u00a0 u1.UserName AS StartUser,\r\n\u00a0 \u00a0 \u00a0 \u00a0 u2.UserID AS FriendID,\r\n\u00a0 \u00a0 \u00a0 \u00a0 u2.UserName AS FriendName,\r\n\u00a0 \u00a0 \u00a0 \u00a0 1 AS Degree\r\n\u00a0 \u00a0 FROM\r\n\u00a0 \u00a0 \u00a0 \u00a0 user_connections uc\r\n\u00a0 \u00a0 JOIN\r\n\u00a0 \u00a0 \u00a0 \u00a0 users u1 ON uc.UserID = u1.UserID\r\n\u00a0 \u00a0 JOIN\r\n\u00a0 \u00a0 \u00a0 \u00a0 users u2 ON uc.FriendID = u2.UserID\r\n\u00a0 \u00a0 WHERE\r\n\u00a0 \u00a0 \u00a0 \u00a0 u1.UserID = 1 -- Starting user (Alice)\r\n\u00a0 \u00a0 \r\n\u00a0 \u00a0 UNION ALL\r\n\u00a0 \u00a0 \r\n\u00a0 \u00a0 SELECT\r\n\u00a0 \u00a0 \u00a0 \u00a0 cp.UserID,\r\n\u00a0 \u00a0 \u00a0 \u00a0 cp.StartUser,\r\n\u00a0 \u00a0 \u00a0 \u00a0 u.UserID AS FriendID,\r\n\u00a0 \u00a0 \u00a0 \u00a0 u.UserName AS FriendName,\r\n\u00a0 \u00a0 \u00a0 \u00a0 cp.Degree + 1\r\n\u00a0 \u00a0 FROM\r\n\u00a0 \u00a0 \u00a0 \u00a0 ConnectionPath cp\r\n\u00a0 \u00a0 JOIN\r\n\u00a0 \u00a0 \u00a0 \u00a0 user_connections uc ON cp.FriendID = uc.UserID\r\n\u00a0 \u00a0 JOIN\r\n\u00a0 \u00a0 \u00a0 \u00a0 users u ON uc.FriendID = u.UserID\r\n\u00a0 \u00a0 WHERE\r\n\u00a0 \u00a0 \u00a0 \u00a0 uc.FriendID NOT IN (cp.UserID) -- Avoid loops by not revisiting the start user\r\n)\r\nSELECT * FROM ConnectionPath\r\nWHERE FriendID = 6 -- Target user (Frank)\r\nORDER BY Degree ASC;<\/pre>\n<h3><span style=\"font-weight: 400;\">Explanation<\/span><\/h3>\n<ul>\n<li style=\"list-style-type: none;\">\n<ul>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Base Case: The CTE starts by identifying direct connections of Alice (UserID 1), including user names for readability.<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Recursive Step: It then recursively finds friends of those connections, extending the search and tracking the degree of connection. The JOINs ensure that user names are included for both the starting user and their friends at each step.<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Termination and Filtering: The recursion continues until it finds connections to Frank (UserID 6). The query filters for paths leading to Frank and orders the results by the degree of connection to identify the shortest paths.<\/span><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p><span style=\"font-weight: 400;\">This query demonstrates how to trace and enumerate paths through a social network, including user names for clarity. It provides a foundation for more complex analyses, such as identifying all mutual connections or exploring network structures.<\/span><\/p>\n<h2><span style=\"font-weight: 400;\">Graph network traversal use case<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">For this use case, I will use a summarized version of the route data from America Airlines. Note that this is not from the Couchbase travel-sample. In this example we use Couchbase SQL++ Recursive CTE query to find all flights from LAX to MAD with &lt; 2 stops from this sample dataset. <\/span><i><span style=\"font-weight: 400;\">Note that this sample data is not based on the `travel-sample`, but\u00a0 a simplified version of the AA routes for 2008.\u00a0<\/span><\/i><\/p>\n<table>\n<tbody>\n<tr>\n<td><b>source_airport_code<\/b><\/td>\n<td><b>destination_airport_code<\/b><\/td>\n<td><b>airline<\/b><\/td>\n<\/tr>\n<tr>\n<td><span>LAX<\/span><\/td>\n<td><span>MAD<\/span><\/td>\n<td><span>AA<\/span><\/td>\n<\/tr>\n<tr>\n<td><span>LAX<\/span><\/td>\n<td><span>LHR<\/span><\/td>\n<td><span>AA<\/span><\/td>\n<\/tr>\n<tr>\n<td><span>LHR<\/span><\/td>\n<td><span>MAD<\/span><\/td>\n<td><span>AA<\/span><\/td>\n<\/tr>\n<tr>\n<td><span>LAX<\/span><\/td>\n<td><span>OPO<\/span><\/td>\n<td><span>AA<\/span><\/td>\n<\/tr>\n<tr>\n<td><span>OPO<\/span><\/td>\n<td><span>MAD<\/span><\/td>\n<td><span>AA<\/span><\/td>\n<\/tr>\n<tr>\n<td><span>MAD<\/span><\/td>\n<td><span>OPO<\/span><\/td>\n<td><span>AA<\/span><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<table>\n<tbody>\n<tr>\n<td><b>SQL++ Query<\/b><\/td>\n<td><b>Results<\/b><\/td>\n<\/tr>\n<tr>\n<td><i><span>\/* List all routes from LAX to MAD with &lt; 2 stops *\/<\/span><\/i><span><br \/>\n<\/span><span>WITH RECURSIVE RouteCTE AS (<\/span><span><br \/>\n<\/span><span>\u00a0 <\/span><b>SELECT<\/b><span> [r.source_airport_code,\u00a0\u00a0\u00a0<\/span><\/p>\n<p><span>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0r.destination_airport_code] <\/span><b>AS<\/b><span> route,<\/span><span><br \/>\n<\/span><span> \u00a0 \u00a0 \u00a0 \u00a0 r.destination_airport_code <\/span><b>AS<\/b><span> lastStop,<\/span><span><br \/>\n<\/span><span> \u00a0 \u00a0 \u00a0 \u00a0 <\/span><span>1<\/span> <b>AS<\/b><span> depth<\/span><span><br \/>\n<\/span><span>\u00a0 <\/span><b>FROM<\/b><span> routes\u00a0 r<\/span><span><br \/>\n<\/span><span>\u00a0 <\/span><b>WHERE<\/b><span> r.source_airport_code = <\/span><span>&#8216;LAX&#8217;<\/span><span><br \/>\n<\/span><b>UNION<\/b> <b>ALL<\/b><span><br \/>\n<\/span><span>\u00a0 <\/span><b>SELECT<\/b> <span>ARRAY_APPEND<\/span><span>(r.route,f.destination_airport_code) <\/span><b>as<\/b><span> route,<\/span><span><br \/>\n<\/span><span> \u00a0 \u00a0 \u00a0 \u00a0 f.destination_airport_code <\/span><b>AS<\/b><span> lastStop,<\/span><span><br \/>\n<\/span><span> \u00a0 \u00a0 \u00a0 \u00a0 r.depth + <\/span><span>1<\/span> <b>AS<\/b><span> depth<\/span><span><br \/>\n<\/span><span>\u00a0 <\/span><b>FROM<\/b><span> RouteCTE\u00a0 r<\/span><span><br \/>\n<\/span><span>\u00a0 <\/span><b>JOIN<\/b><span> routes\u00a0 f <\/span><b>ON<\/b><span> r.lastStop = f.source_airport_code<\/span><span><br \/>\n<\/span><span>\u00a0 <\/span><b>WHERE<\/b><span> f.destination_airport_code != <\/span><span>&#8216;LAX&#8217;<\/span> <b>AND<\/b><span> r.depth &lt; <\/span><span>3<\/span><span><br \/>\n<\/span><span>)<\/span><\/p>\n<p><b>OPTIONS<\/b><span> {&#8220;levels&#8221;:3}<\/span><span><br \/>\n<\/span><b>SELECT<\/b><span> r.*<\/span><span><br \/>\n<\/span><b>FROM<\/b><span> RouteCTE <\/span><b>AS<\/b><span> r<\/span><span><br \/>\n<\/span><b>WHERE<\/b><span> r.lastStop = <\/span><span>&#8216;MAD&#8217;<\/span><span><br \/>\n<\/span><span>AND r.depth &lt; 3;<\/span><\/td>\n<td><span>[ <\/span><span><br \/>\n<\/span><span>\u00a0 {<\/span><span><br \/>\n<\/span><span> \u00a0 \u00a0 &#8220;route&#8221;: [<\/span><span><br \/>\n<\/span><span>\u00a0 \u00a0 \u00a0 <\/span> <span>&#8220;LAX&#8221;,<\/span><span><br \/>\n<\/span><span> \u00a0 \u00a0 <\/span> <span>&#8220;MAD&#8221;<\/span><span><br \/>\n<\/span><span>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 ]<\/span><span><br \/>\n<\/span><span>\u00a0 },<\/span><span><br \/>\n<\/span><span>\u00a0 {<\/span><span><br \/>\n<\/span><span>\u00a0 \u00a0 &#8220;route&#8221;: [<\/span><span><br \/>\n<\/span><span> \u00a0 \u00a0 <\/span> <span> &#8220;LAX&#8221;,<\/span><span><br \/>\n<\/span><span>\u00a0 \u00a0 \u00a0 <\/span> <span> &#8220;LHR&#8221;,<\/span><span><br \/>\n<\/span><span>\u00a0 \u00a0 \u00a0 <\/span> <span> &#8220;MAD&#8221;<\/span><span><br \/>\n<\/span><span> \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 ]<\/span><span><br \/>\n<\/span><span>\u00a0 },<\/span><span><br \/>\n<\/span><span>\u00a0 {<\/span><span><br \/>\n<\/span><span>\u00a0 \u00a0 &#8220;route&#8221;: [<\/span><span><br \/>\n<\/span><span> \u00a0 \u00a0 <\/span> <span>&#8220;LAX&#8221;,<\/span><span><br \/>\n<\/span><span>\u00a0 \u00a0 \u00a0 <\/span> <span>&#8220;OPO&#8221;,<\/span><span><br \/>\n<\/span><span>\u00a0 \u00a0 \u00a0 <\/span> <span>&#8220;MAD&#8221;<\/span><span><br \/>\n<\/span><span> \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 ]<\/span><span><br \/>\n<\/span><span>\u00a0 }<\/span><span><br \/>\n<\/span><span>]<\/span><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h3><span style=\"font-weight: 400;\">Explanation<\/span><\/h3>\n<ul>\n<li style=\"list-style-type: none;\">\n<ul>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">The routeCTE\u00a0 starts with all flights departing from LAX.<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">The recursive part of the routeCTE looks for flights connecting from the lastStop in the current route back to other airports, avoiding routes that loop back to LAX.<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">The route array accumulates the sequence of airport codes to show the path taken.<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">The query outputs all routes that end in MAD, detailing the paths found.<\/span><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<h2><span style=\"font-weight: 400;\">Best Practices Recursive CTE<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">When using Recursive Common Table Expressions (CTEs) in SQL++, developers should be aware of the implications of the recursive nature and the cost for query processing. Here are the best practices:<\/span><\/p>\n<p style=\"padding-left: 40px;\"><b>Set limits for the recursion depth<\/b><span style=\"font-weight: 400;\"> &#8211; Always set a limit on the recursion depth to prevent infinite loops and excessive resource consumption. Use a counter or a condition within the recursive CTE to control how deep the recursion can go, include the options limits.<\/span><\/p>\n<p style=\"padding-left: 40px;\"><b>Monitor Performance <\/b><span style=\"font-weight: 400;\">&#8211;\u00a0 Recursive CTEs can be resource-intensive. Monitor performance closely, especially for long or complex queries, and optimize them as necessary. This might involve indexing,or breaking down overly complex CTEs.<\/span><\/p>\n<p style=\"padding-left: 40px;\"><b>Avoid Unnecessary Complexity<\/b><span style=\"font-weight: 400;\"> &#8211; Keep the logic within the recursive part of the CTE as simple as possible. Overly complex conditions or computations can significantly degrade performance. Check the JOIN condition for correctness.<\/span><\/p>\n<p style=\"padding-left: 40px;\"><b>Ensure Correct Data Structure<\/b><span style=\"font-weight: 400;\"> &#8211;\u00a0 Verify that your data is structured correctly for recursion. Incorrect or malformed data can lead to incorrect results or inefficient queries.<\/span><\/p>\n<p style=\"padding-left: 40px;\"><b>Test Extensively<\/b><span style=\"font-weight: 400;\"> &#8211;\u00a0 Thoroughly test recursive CTEs with various datasets, including edge cases. This helps in catching any issues with infinite loops, incorrect results, or performance bottlenecks.<\/span><\/p>\n<p style=\"padding-left: 40px;\"><b>Set the memory quota<\/b><span style=\"font-weight: 400;\"> &#8211; Set the memory quota either at the request or node level to avoid excessive of memory usage in the recursive query.<\/span><\/p>\n<h2><span style=\"font-weight: 400;\">Limitations<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">Recursive CTEs\u00a0 are a powerful feature in Couchbase SQL++, which are commonly found in other RDBMS. They allow for the execution of complex queries, such as traversing hierarchical and graph network data or performing iterative calculations that are difficult to express with standard SQL. However, there are limitations and considerations to be aware of when using recursive CTEs. These limitations often pertain to performance, syntax restrictions, and the complexity of queries. Here are some specifics:<\/span><\/p>\n<p style=\"padding-left: 40px;\"><b>Aggregates:<\/b><span style=\"font-weight: 400;\"> Recursive CTEs typically do not allow aggregate functions (MIN(), MAX(), SUM(), AVG(), etc.) or DISTINCT within the recursive part of the CTE. These operations do not make sense in the context of adding rows recursively because they imply a final result set after all recursion has been resolved.<\/span><\/p>\n<p style=\"padding-left: 40px;\"><b>Window Functions<\/b><span style=\"font-weight: 400;\">: Like aggregate functions, window functions (ROW_NUMBER(), RANK(), etc.) are generally not used within the recursive part of the CTE. They are intended for use on a set of rows returned by the query, making them suitable for the non-recursive term or in a query selecting from the recursive CTE.<\/span><\/p>\n<p style=\"padding-left: 40px;\"><b>LIMIT \/ ORDER BY<\/b><span style=\"font-weight: 400;\">: These clauses are not allowed within the recursive member of the CTE. The reasoning is that they pertain to the final result set ordering and does not make sense within the context of building the recursive set, where intermediate results are cumulatively constructed through each iteration.<\/span><\/p>\n<h2>Next Steps<\/h2>\n<ul>\n<li style=\"list-style-type: none;\">\n<ul>\n<li>Learn more about <a href=\"https:\/\/www.couchbase.com\/products\/n1ql\/\">Couchbase SQL++<\/a><\/li>\n<li>More CTE examples in blog: <a href=\"https:\/\/www.couchbase.com\/blog\/recursive-query-processing-in-sql-n1ql\/\">Recursive Query Processing in SQL++<\/a> (N1QL)<\/li>\n<li>Get started with a <a href=\"https:\/\/www.couchbase.com\/products\/capella\/\">free trial of Couchbase Capella<\/a><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Recursive Common Table Expressions (CTEs) and Oracle&#8217;s CONNECT BY are well-known SQL constructs among RDBMS users, facilitating the delegation of complex, interdependent data structure exploration to the database layer for enhanced processing efficiency. These constructs are crucial for querying interdependent [&hellip;]<\/p>\n","protected":false},"author":26326,"featured_media":15707,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"inline_featured_image":false,"footnotes":""},"categories":[1815,2225,1816,1812],"tags":[9945,1726],"ppma_author":[8919],"class_list":["post-15705","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-best-practices-and-tutorials","category-cloud","category-couchbase-server","category-n1ql-query","tag-couchbase-7-6","tag-graph-database"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v25.8 (Yoast SEO v25.8) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Query Graph Models With Couchbase Recursive CTE - The Couchbase Blog<\/title>\n<meta name=\"description\" content=\"Couchbase sets itself apart with SQL for JSON, offering a unique approach to Recursive CTEs that also extend its multi model support.\" \/>\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.couchbase.com\/blog\/query-graph-recursive-cte\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Query Graph Models With Couchbase Recursive CTE\" \/>\n<meta property=\"og:description\" content=\"Couchbase sets itself apart with SQL for JSON, offering a unique approach to Recursive CTEs that also extend its multi model support.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.couchbase.com\/blog\/query-graph-recursive-cte\/\" \/>\n<meta property=\"og:site_name\" content=\"The Couchbase Blog\" \/>\n<meta property=\"article:published_time\" content=\"2024-05-09T17:03:54+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2024-05-22T20:47:54+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2024\/05\/graph-network-query-scaled.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"2560\" \/>\n\t<meta property=\"og:image:height\" content=\"1707\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Binh Le\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Binh Le\" \/>\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:\/\/www.couchbase.com\/blog\/query-graph-recursive-cte\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/query-graph-recursive-cte\/\"},\"author\":{\"name\":\"Binh Le\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/f89064928e262c71eb43bee996c48c63\"},\"headline\":\"Query Graph Models With Couchbase Recursive CTE\",\"datePublished\":\"2024-05-09T17:03:54+00:00\",\"dateModified\":\"2024-05-22T20:47:54+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/query-graph-recursive-cte\/\"},\"wordCount\":1549,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/query-graph-recursive-cte\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/05\/graph-network-query-scaled.jpg\",\"keywords\":[\"Couchbase 7.6\",\"graph database\"],\"articleSection\":[\"Best Practices and Tutorials\",\"Couchbase Capella\",\"Couchbase Server\",\"SQL++ \/ N1QL Query\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.couchbase.com\/blog\/query-graph-recursive-cte\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/query-graph-recursive-cte\/\",\"url\":\"https:\/\/www.couchbase.com\/blog\/query-graph-recursive-cte\/\",\"name\":\"Query Graph Models With Couchbase Recursive CTE - The Couchbase Blog\",\"isPartOf\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/query-graph-recursive-cte\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/query-graph-recursive-cte\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/05\/graph-network-query-scaled.jpg\",\"datePublished\":\"2024-05-09T17:03:54+00:00\",\"dateModified\":\"2024-05-22T20:47:54+00:00\",\"description\":\"Couchbase sets itself apart with SQL for JSON, offering a unique approach to Recursive CTEs that also extend its multi model support.\",\"breadcrumb\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/query-graph-recursive-cte\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.couchbase.com\/blog\/query-graph-recursive-cte\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/query-graph-recursive-cte\/#primaryimage\",\"url\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/05\/graph-network-query-scaled.jpg\",\"contentUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/05\/graph-network-query-scaled.jpg\",\"width\":2560,\"height\":1707},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/query-graph-recursive-cte\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.couchbase.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Query Graph Models With Couchbase Recursive CTE\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#website\",\"url\":\"https:\/\/www.couchbase.com\/blog\/\",\"name\":\"The Couchbase Blog\",\"description\":\"Couchbase, the NoSQL Database\",\"publisher\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.couchbase.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\",\"name\":\"The Couchbase Blog\",\"url\":\"https:\/\/www.couchbase.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png\",\"contentUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png\",\"width\":218,\"height\":34,\"caption\":\"The Couchbase Blog\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/f89064928e262c71eb43bee996c48c63\",\"name\":\"Binh Le\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/image\/5b68c37e30928a9d7b2c8470b1a303b7\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/a939f48df6447844a8780bec264bb3be21d589336f3915fabc557075a68fa374?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/a939f48df6447844a8780bec264bb3be21d589336f3915fabc557075a68fa374?s=96&d=mm&r=g\",\"caption\":\"Binh Le\"},\"description\":\"Binh Le is a Principal Product Manager for Couchbase Query service. Prior to Couchbase, he worked at Oracle and led the product management team for Sales Cloud Analytics and CRM OnDemand. Binh holds a Bachelor's Degree in Computer Science from the University of Brighton, UK.\",\"url\":\"https:\/\/www.couchbase.com\/blog\/author\/binh-le-2\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Query Graph Models With Couchbase Recursive CTE - The Couchbase Blog","description":"Couchbase sets itself apart with SQL for JSON, offering a unique approach to Recursive CTEs that also extend its multi model support.","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.couchbase.com\/blog\/query-graph-recursive-cte\/","og_locale":"en_US","og_type":"article","og_title":"Query Graph Models With Couchbase Recursive CTE","og_description":"Couchbase sets itself apart with SQL for JSON, offering a unique approach to Recursive CTEs that also extend its multi model support.","og_url":"https:\/\/www.couchbase.com\/blog\/query-graph-recursive-cte\/","og_site_name":"The Couchbase Blog","article_published_time":"2024-05-09T17:03:54+00:00","article_modified_time":"2024-05-22T20:47:54+00:00","og_image":[{"width":2560,"height":1707,"url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2024\/05\/graph-network-query-scaled.jpg","type":"image\/jpeg"}],"author":"Binh Le","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Binh Le","Est. reading time":"8 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.couchbase.com\/blog\/query-graph-recursive-cte\/#article","isPartOf":{"@id":"https:\/\/www.couchbase.com\/blog\/query-graph-recursive-cte\/"},"author":{"name":"Binh Le","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/f89064928e262c71eb43bee996c48c63"},"headline":"Query Graph Models With Couchbase Recursive CTE","datePublished":"2024-05-09T17:03:54+00:00","dateModified":"2024-05-22T20:47:54+00:00","mainEntityOfPage":{"@id":"https:\/\/www.couchbase.com\/blog\/query-graph-recursive-cte\/"},"wordCount":1549,"commentCount":0,"publisher":{"@id":"https:\/\/www.couchbase.com\/blog\/#organization"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/query-graph-recursive-cte\/#primaryimage"},"thumbnailUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/05\/graph-network-query-scaled.jpg","keywords":["Couchbase 7.6","graph database"],"articleSection":["Best Practices and Tutorials","Couchbase Capella","Couchbase Server","SQL++ \/ N1QL Query"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.couchbase.com\/blog\/query-graph-recursive-cte\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.couchbase.com\/blog\/query-graph-recursive-cte\/","url":"https:\/\/www.couchbase.com\/blog\/query-graph-recursive-cte\/","name":"Query Graph Models With Couchbase Recursive CTE - The Couchbase Blog","isPartOf":{"@id":"https:\/\/www.couchbase.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.couchbase.com\/blog\/query-graph-recursive-cte\/#primaryimage"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/query-graph-recursive-cte\/#primaryimage"},"thumbnailUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/05\/graph-network-query-scaled.jpg","datePublished":"2024-05-09T17:03:54+00:00","dateModified":"2024-05-22T20:47:54+00:00","description":"Couchbase sets itself apart with SQL for JSON, offering a unique approach to Recursive CTEs that also extend its multi model support.","breadcrumb":{"@id":"https:\/\/www.couchbase.com\/blog\/query-graph-recursive-cte\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.couchbase.com\/blog\/query-graph-recursive-cte\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.couchbase.com\/blog\/query-graph-recursive-cte\/#primaryimage","url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/05\/graph-network-query-scaled.jpg","contentUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2024\/05\/graph-network-query-scaled.jpg","width":2560,"height":1707},{"@type":"BreadcrumbList","@id":"https:\/\/www.couchbase.com\/blog\/query-graph-recursive-cte\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.couchbase.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Query Graph Models With Couchbase Recursive CTE"}]},{"@type":"WebSite","@id":"https:\/\/www.couchbase.com\/blog\/#website","url":"https:\/\/www.couchbase.com\/blog\/","name":"The Couchbase Blog","description":"Couchbase, the NoSQL Database","publisher":{"@id":"https:\/\/www.couchbase.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.couchbase.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.couchbase.com\/blog\/#organization","name":"The Couchbase Blog","url":"https:\/\/www.couchbase.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png","contentUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png","width":218,"height":34,"caption":"The Couchbase Blog"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/f89064928e262c71eb43bee996c48c63","name":"Binh Le","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/image\/5b68c37e30928a9d7b2c8470b1a303b7","url":"https:\/\/secure.gravatar.com\/avatar\/a939f48df6447844a8780bec264bb3be21d589336f3915fabc557075a68fa374?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/a939f48df6447844a8780bec264bb3be21d589336f3915fabc557075a68fa374?s=96&d=mm&r=g","caption":"Binh Le"},"description":"Binh Le is a Principal Product Manager for Couchbase Query service. Prior to Couchbase, he worked at Oracle and led the product management team for Sales Cloud Analytics and CRM OnDemand. Binh holds a Bachelor's Degree in Computer Science from the University of Brighton, UK.","url":"https:\/\/www.couchbase.com\/blog\/author\/binh-le-2\/"}]}},"authors":[{"term_id":8919,"user_id":26326,"is_guest":0,"slug":"binh-le-2","display_name":"Binh Le","avatar_url":"https:\/\/secure.gravatar.com\/avatar\/a939f48df6447844a8780bec264bb3be21d589336f3915fabc557075a68fa374?s=96&d=mm&r=g","author_category":"","last_name":"Le","first_name":"Binh","job_title":"","user_url":"","description":"Binh Le is a Principal Product Manager for Couchbase Query service. Prior to Couchbase, he worked at Oracle and led the product management team for Sales Clould Analytics and CRM OnDemand. Binh holds a Bachelor's Degree in Computer Science from the University of Brighton, UK."}],"_links":{"self":[{"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/posts\/15705","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/users\/26326"}],"replies":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/comments?post=15705"}],"version-history":[{"count":0,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/posts\/15705\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/media\/15707"}],"wp:attachment":[{"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/media?parent=15705"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/categories?post=15705"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/tags?post=15705"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/ppma_author?post=15705"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}