{"action":"opened","issue":{"url":"https://api.github.com/repos/guevara/read-it-later/issues/1120","repository_url":"https://api.github.com/repos/guevara/read-it-later","labels_url":"https://api.github.com/repos/guevara/read-it-later/issues/1120/labels{/name}","comments_url":"https://api.github.com/repos/guevara/read-it-later/issues/1120/comments","events_url":"https://api.github.com/repos/guevara/read-it-later/issues/1120/events","html_url":"https://github.com/guevara/read-it-later/issues/1120","id":372305624,"node_id":"MDU6SXNzdWUzNzIzMDU2MjQ=","number":1120,"title":"SQL优化五 PostgreSQL 递归CTE 通用表表达式","user":{"login":"guevara","id":228309,"node_id":"MDQ6VXNlcjIyODMwOQ==","avatar_url":"https://avatars1.githubusercontent.com/u/228309?v=4","gravatar_id":"","url":"https://api.github.com/users/guevara","html_url":"https://github.com/guevara","followers_url":"https://api.github.com/users/guevara/followers","following_url":"https://api.github.com/users/guevara/following{/other_user}","gists_url":"https://api.github.com/users/guevara/gists{/gist_id}","starred_url":"https://api.github.com/users/guevara/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/guevara/subscriptions","organizations_url":"https://api.github.com/users/guevara/orgs","repos_url":"https://api.github.com/users/guevara/repos","events_url":"https://api.github.com/users/guevara/events{/privacy}","received_events_url":"https://api.github.com/users/guevara/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":0,"created_at":"2018-10-21T11:39:17Z","updated_at":"2018-10-21T11:39:17Z","closed_at":null,"author_association":"OWNER","body":"SQL优化（五） PostgreSQL （递归）CTE 通用表表达式<br>\n<br>\nhttps://ift.tt/28UJCxp<br>\n<br>\n郭俊 Jason<br>\n<br>\n<div><div>\n<div>\n<blockquote>\n<p>原创文章，转载请务必将下面这段话置于文章开头处（保留超链接）。<br>本文转发自<a href=\"http://www.jasongj.com\"><strong>技术世界</strong></a>，<a href=\"http://www.jasongj.com/sql/cte/\">原文链接</a>　<a href=\"http://www.jasongj.com/sql/cte/\">http://www.jasongj.com/sql/cte/</a></p>\n</blockquote>\n<h1>\n<a title=\"CTE or WITH\" href=\"http://www.jasongj.com/sql/cte/#CTE-or-WITH\"></a>CTE or WITH</h1>\n<p>WITH语句通常被称为通用表表达式（Common Table Expressions）或者CTEs。</p>\n<p>WITH语句作为一个辅助语句依附于主语句，WITH语句和主语句都可以是<code>SELECT</code>，<code>INSERT</code>，<code>UPDATE</code>，<code>DELETE</code>中的任何一种语句。</p>\n<h2>\n<a title=\"例讲CTE\" href=\"http://www.jasongj.com/sql/cte/#%E4%BE%8B%E8%AE%B2CTE\"></a>例讲CTE</h2>\n<p>WITH语句最基本的功能是把复杂查询语句拆分成多个简单的部分，如下例所示<br><figure><table><tr>\n<td><pre><p>1</p><p>2</p><p>3</p><p>4</p><p>5</p><p>6</p><p>7</p><p>8</p><p>9</p><p>10</p><p>11</p><p>12</p><p>13</p><p>14</p><p>15</p><p>16</p><p>17</p></pre></td>\n<td><pre><p>WITH regional_sales AS (</p><p> <span>SELECT</span> region, <span>SUM</span>(amount) <span>AS</span> total_sales</p><p> <span>FROM</span> orders</p><p> <span>GROUP</span> <span>BY</span> region</p><p>), top_regions <span>AS</span> (</p><p> <span>SELECT</span> region</p><p> <span>FROM</span> regional_sales</p><p> <span>WHERE</span> total_sales &gt; (<span>SELECT</span> <span>SUM</span>(total_sales)/<span>10</span> <span>FROM</span> regional_sales</p><p>)</p><p><span>SELECT</span></p><p> region,</p><p> product,</p><p> <span>SUM</span>(quantity) <span>AS</span> product_units,</p><p> <span>SUM</span>(amount) <span>AS</span> product_sales</p><p><span>FROM</span> orders</p><p><span>WHERE</span> region <span>IN</span> (<span>SELECT</span> region <span>FROM</span> top_regions)</p><p><span>GROUP</span> <span>BY</span> region, product;</p></pre></td>\n</tr></table></figure></p>\n<p>该例中，定义了两个WITH辅助语句，regional_sales和top_regions。前者算出每个区域的总销售量，后者了查出所有销售量占所有地区总销售里10%以上的区域。主语句通过将这个CTEs及订单表关联，算出了顶级区域每件商品的销售量和销售额。</p>\n<p>当然，本例也可以不使用CTEs而使用两层嵌套子查询来实现，但使用CTEs更简单，更清晰，可读性更强。</p>\n<h2>\n<a title=\"在WITH中使用数据修改语句\" href=\"http://www.jasongj.com/sql/cte/#%E5%9C%A8WITH%E4%B8%AD%E4%BD%BF%E7%94%A8%E6%95%B0%E6%8D%AE%E4%BF%AE%E6%94%B9%E8%AF%AD%E5%8F%A5\"></a>在WITH中使用数据修改语句</h2>\n<p>文章开头处提到，WITH中可以不仅可以使用<code>SELECT</code>语句，同时还能使用<code>DELETE</code>，<code>UPDATE</code>，<code>INSERT</code>语句。因此，可以使用WITH，在一条SQL语句中进行不同的操作，如下例所示。<br><figure><table><tr>\n<td><pre><p>1</p><p>2</p><p>3</p><p>4</p><p>5</p><p>6</p><p>7</p><p>8</p><p>9</p></pre></td>\n<td><pre><p>WITH moved_rows AS (</p><p> <span>DELETE</span> <span>FROM</span> products</p><p> <span>WHERE</span></p><p> <span>\"date\"</span> &gt;= <span>'2010-10-01'</span></p><p> <span>AND</span> <span>\"date\"</span> &lt; <span>'2010-11-01'</span></p><p> <span>RETURNING</span> *</p><p>)</p><p><span>INSERT</span> <span>INTO</span> products_log</p><p><span>SELECT</span> * <span>FROM</span> moved_rows;</p></pre></td>\n</tr></table></figure></p>\n<p>本例通过WITH中的DELETE语句从products表中删除了一个月的数据，并通过<code>RETURNING</code>子句将删除的数据集赋给moved_rows这一CTE，最后在主语句中通过INSERT将删除的商品插入products_log中。</p>\n<p>如果WITH里面使用的不是SELECT语句，并且没有通过RETURNING子句返回结果集，则主查询中不可以引用该CTE，但主查询和WITH语句仍然可以继续执行。这种情况可以实现将多个不相关的语句放在一个SQL语句里，实现了在不显式使用事务的情况下保证WITH语句和主语句的事务性，如下例所示。<br><figure><table><tr>\n<td><pre><p>1</p><p>2</p><p>3</p><p>4</p><p>5</p><p>6</p><p>7</p><p>8</p></pre></td>\n<td><pre><p>WITH d AS (</p><p> <span>DELETE</span> <span>FROM</span> foo</p><p>),</p><p>u <span>as</span> (</p><p> <span>UPDATE</span> foo <span>SET</span> a = <span>1</span></p><p> <span>WHERE</span> b = <span>2</span></p><p>)</p><p><span>DELETE</span> <span>FROM</span> bar;</p></pre></td>\n</tr></table></figure></p>\n<h2>\n<a title=\"WITH使用注意事项\" href=\"http://www.jasongj.com/sql/cte/#WITH%E4%BD%BF%E7%94%A8%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9\"></a>WITH使用注意事项</h2>\n<ol>\n<li>WITH中的数据修改语句会被执行一次，并且肯定会完全执行，无论主语句是否读取或者是否读取所有其输出。而WITH中的SELECT语句则只输出主语句中所需要记录数。</li>\n<li>WITH中使用多个子句时，这些子句和主语句会并行执行，所以当存在多个修改子语句修改相同的记录时，它们的结果不可预测。</li>\n<li>所有的子句所能“看”到的数据集是一样的，所以它们看不到其它语句对目标数据集的影响。这也缓解了多子句执行顺序的不可预测性造成的影响。</li>\n<li>如果在一条SQL语句中，更新同一记录多次，只有其中一条会生效，并且很难预测哪一个会生效。</li>\n<li>如果在一条SQL语句中，同时更新和删除某条记录，则只有更新会生效。</li>\n<li>目前，任何一个被数据修改CTE的表，不允许使用条件规则，和ALSO规则以及INSTEAD规则。</li>\n</ol>\n<h1>\n<a title=\"WITH RECURSIVE\" href=\"http://www.jasongj.com/sql/cte/#WITH-RECURSIVE\"></a>WITH RECURSIVE</h1>\n<p>WITH语句还可以通过增加<code>RECURSIVE</code>修饰符来引入它自己，从而实现递归</p>\n<h2>\n<a title=\"WITH RECURSIVE实例\" href=\"http://www.jasongj.com/sql/cte/#WITH-RECURSIVE%E5%AE%9E%E4%BE%8B\"></a>WITH RECURSIVE实例</h2>\n<p>WITH RECURSIVE一般用于处理逻辑上层次化或树状结构的数据，典型的使用场景是寻找直接及间接子结点。</p>\n<p>定义下面这样的表，存储每个区域（省、市、区）的id，名字及上级区域的id<br><figure><table><tr>\n<td><pre><p>1</p><p>2</p><p>3</p><p>4</p><p>5</p><p>6</p></pre></td>\n<td><pre><p><span>CREATE</span> <span>TABLE</span> chinamap</p><p>(</p><p> <span>id</span> <span>INTEGER</span>,</p><p> pid <span>INTEGER</span>,</p><p> <span>name</span> <span>TEXT</span></p><p>);</p></pre></td>\n</tr></table></figure></p>\n<p>需要查出某个省，比如湖北省，管辖的所有市及市辖地区，可以通过WITH RECURSIVE来实现，如下<br><figure><table><tr>\n<td><pre><p>1</p><p>2</p><p>3</p><p>4</p><p>5</p><p>6</p><p>7</p><p>8</p><p>9</p><p>10</p><p>11</p><p>12</p><p>13</p><p>14</p><p>15</p><p>16</p><p>17</p><p>18</p><p>19</p></pre></td>\n<td><pre><p>WITH RECURSIVE result AS</p><p>(</p><p> SELECCT</p><p> id,</p><p> name</p><p> FROM chinamap</p><p> WHERE id = 11</p><p> UNION ALL</p><p> <span>SELECT</span></p><p> origin.id,</p><p> result.name || <span>' &gt; '</span> || origin.name</p><p> <span>FROM</span> <span>result</span></p><p> <span>JOIN</span> chinamap origin</p><p> <span>ON</span> origin.pid = result.id</p><p>)</p><p><span>SELECT</span></p><p> <span>id</span>,</p><p> <span>name</span></p><p><span>FROM</span> <span>result</span>;</p></pre></td>\n</tr></table></figure></p>\n<p>结果如下<br><figure><table><tr>\n<td><pre><p>1</p><p>2</p><p>3</p><p>4</p><p>5</p><p>6</p><p>7</p><p>8</p><p>9</p><p>10</p><p>11</p><p>12</p><p>13</p><p>14</p><p>15</p><p>16</p><p>17</p><p>18</p><p>19</p></pre></td>\n<td><pre><p> id | name </p><p>-----+--------------------------</p><p> 11 | 湖北省</p><p> 110 | 湖北省 &gt; 武汉市</p><p> 120 | 湖北省 &gt; 孝感市</p><p> 130 | 湖北省 &gt; 宜昌市</p><p> 140 | 湖北省 &gt; 随州市</p><p> 150 | 湖北省 &gt; 仙桃市</p><p> 160 | 湖北省 &gt; 荆门市</p><p> 170 | 湖北省 &gt; 枝江市</p><p> 180 | 湖北省 &gt; 神农架市</p><p> 111 | 湖北省 &gt; 武汉市 &gt; 武昌区</p><p> 112 | 湖北省 &gt; 武汉市 &gt; 下城区</p><p> 113 | 湖北省 &gt; 武汉市 &gt; 江岸区</p><p> 114 | 湖北省 &gt; 武汉市 &gt; 江汉区</p><p> 115 | 湖北省 &gt; 武汉市 &gt; 汉阳区</p><p> 116 | 湖北省 &gt; 武汉市 &gt; 洪山区</p><p> 117 | 湖北省 &gt; 武汉市 &gt; 青山区</p><p>(16 rows)</p></pre></td>\n</tr></table></figure></p>\n<h2>\n<a title=\"WITH RECURSIVE 执行过程\" href=\"http://www.jasongj.com/sql/cte/#WITH-RECURSIVE-%E6%89%A7%E8%A1%8C%E8%BF%87%E7%A8%8B\"></a>WITH RECURSIVE 执行过程</h2>\n<p>从上面的例子可以看出，WITH RECURSIVE语句包含了两个部分</p>\n<ul>\n<li>non-recursive term（非递归部分），即上例中的union all前面部分</li>\n<li>recursive term（递归部分），即上例中union all后面部分</li>\n</ul>\n<p>执行步骤如下</p>\n<ol>\n<li>执行non-recursive term。（如果使用的是union而非union all，则需对结果去重）其结果作为recursive term中对result的引用，同时将这部分结果放入临时的working table中</li>\n<li>重复执行如下步骤，直到working table为空：用working table的内容替换递归的自引用，执行recursive term，（如果使用union而非union all，去除重复数据），并用该结果（如果使用union而非union all，则是去重后的结果）替换working table</li>\n</ol>\n<p>以上面的query为例，来看看具体过程<br>1.执行<br><figure><table><tr>\n<td><pre><p>1</p><p>2</p><p>3</p><p>4</p><p>5</p></pre></td>\n<td><pre><p><span>SELECT</span></p><p> <span>id</span>,</p><p> <span>name</span></p><p><span>FROM</span> chinamap</p><p><span>WHERE</span> <span>id</span> = <span>11</span></p></pre></td>\n</tr></table></figure></p>\n<p>结果集和working table为<br><figure></figure></p>\n<p>2.执行<br><figure><table><tr>\n<td><pre><p>1</p><p>2</p><p>3</p><p>4</p><p>5</p><p>6</p></pre></td>\n<td><pre><p><span>SELECT</span></p><p> origin.id,</p><p> result.name || <span>' &gt; '</span> || origin.name</p><p><span>FROM</span> <span>result</span></p><p><span>JOIN</span> chinamap origin</p><p><span>ON</span> origin.pid = result.id</p></pre></td>\n</tr></table></figure></p>\n<p>结果集和working table为<br><figure><table><tr>\n<td><pre><p>1</p><p>2</p><p>3</p><p>4</p><p>5</p><p>6</p><p>7</p><p>8</p></pre></td>\n<td><pre><p>110 | 湖北省 &gt; 武汉市</p><p>120 | 湖北省 &gt; 孝感市</p><p>130 | 湖北省 &gt; 宜昌市</p><p>140 | 湖北省 &gt; 随州市</p><p>150 | 湖北省 &gt; 仙桃市</p><p>160 | 湖北省 &gt; 荆门市</p><p>170 | 湖北省 &gt; 枝江市</p><p>180 | 湖北省 &gt; 神农架市</p></pre></td>\n</tr></table></figure></p>\n<p>3.再次执行recursive query，结果集和working table为<br><figure><table><tr>\n<td><pre><p>1</p><p>2</p><p>3</p><p>4</p><p>5</p><p>6</p><p>7</p></pre></td>\n<td><pre><p>111 | 湖北省 &gt; 武汉市 &gt; 武昌区</p><p>112 | 湖北省 &gt; 武汉市 &gt; 下城区</p><p>113 | 湖北省 &gt; 武汉市 &gt; 江岸区</p><p>114 | 湖北省 &gt; 武汉市 &gt; 江汉区</p><p>115 | 湖北省 &gt; 武汉市 &gt; 汉阳区</p><p>116 | 湖北省 &gt; 武汉市 &gt; 洪山区</p><p>117 | 湖北省 &gt; 武汉市 &gt; 青山区</p></pre></td>\n</tr></table></figure></p>\n<p>4.继续执行recursive query，结果集和working table为空<br>5.结束递归，将前三个步骤的结果集合并，即得到最终的WITH RECURSIVE的结果集</p>\n<p>严格来讲，这个过程实现上是一个迭代的过程而非递归，不过RECURSIVE这个关键词是SQL标准委员会定立的，所以PostgreSQL也延用了RECURSIVE这一关键词。 </p>\n<h2>\n<a title=\"WITH RECURSIVE 防止死循环\" href=\"http://www.jasongj.com/sql/cte/#WITH-RECURSIVE-%E9%98%B2%E6%AD%A2%E6%AD%BB%E5%BE%AA%E7%8E%AF\"></a>WITH RECURSIVE 防止死循环</h2>\n<p>从上一节中可以看到，决定是否继续迭代的working table是否为空，如果它永不为空，则该CTE将陷入无限循环中。<br>对于本身并不会形成循环引用的数据集，无段作特别处理。而对于本身可能形成循环引用的数据集，则须通过SQL处理。</p>\n<p>一种方式是使用UNION而非UNION ALL，从而每次recursive term的计算结果都会将已经存在的数据清除后再存入working table，使得working table最终会为空，从而结束迭代。</p>\n<p>然而，这种方法并不总是有效的，因为有时可能需要这些重复数据。同时UNION只能去除那些所有字段都完全一样的记录，而很有可能特定字段集相同的记录即应该被删除。此时可以通过数组（单字段）或者ROW（多字段）记录已经访问过的记录，从而实现去重的目的。</p>\n<h2>\n<a title=\"WITH RECURSIVE 求最短路径\" href=\"http://www.jasongj.com/sql/cte/#WITH-RECURSIVE-%E6%B1%82%E6%9C%80%E7%9F%AD%E8%B7%AF%E5%BE%84\"></a>WITH RECURSIVE 求最短路径</h2>\n<p>定义无向有环图如下图所示<br><img alt=\"Non-directional cycle graph\" src=\"http://www.jasongj.com/img/sql/5cte/graph.png\"></p>\n<p>定义如下表并存入每条边的权重<br><figure><table><tr>\n<td><pre><p>1</p><p>2</p><p>3</p><p>4</p><p>5</p><p>6</p><p>7</p><p>8</p><p>9</p><p>10</p><p>11</p><p>12</p><p>13</p><p>14</p><p>15</p><p>16</p><p>17</p></pre></td>\n<td><pre><p><span>CREATE</span> <span>TABLE</span> graph</p><p>(</p><p> <span>id</span> <span>char</span>,</p><p> neighbor <span>char</span>,</p><p> <span>value</span> <span>integer</span></p><p>);</p><p><span>INSERT</span> <span>INTO</span> graph</p><p><span>VALUES</span>(<span>'A'</span>, <span>'B'</span>, <span>3</span>),</p><p>(<span>'A'</span>, <span>'C'</span>, <span>5</span>),</p><p>(<span>'A'</span>, <span>'D'</span>, <span>4</span>),</p><p>(<span>'B'</span>, <span>'E'</span>, <span>8</span>),</p><p>(<span>'B'</span>, <span>'C'</span>, <span>4</span>),</p><p>(<span>'E'</span>, <span>'C'</span>, <span>7</span>),</p><p>(<span>'E'</span>,<span>'F'</span>, <span>10</span>),</p><p>(<span>'C'</span>, <span>'D'</span>, <span>3</span>),</p><p>(<span>'C'</span>, <span>'F'</span>, <span>6</span>),</p><p>(<span>'F'</span>,<span>'D'</span>, <span>5</span>);</p></pre></td>\n</tr></table></figure></p>\n<p>计算思路如下：</p>\n<ul>\n<li>因为是无向图，所以首先要将各条边的id和neighbor交换一次以方便后续计算。</li>\n<li>利用WITH RECURSIVE算出所有可能的路径并计算其总权重。</li>\n<li>因为该图有环，为避免无限循环，同时为了计算路径，将经过的结点存于数据中，当下一个结点已经在数据中时，说明该结点已被计算。</li>\n<li>最终可算出所有可能的路径及其总权重</li>\n</ul>\n<p>实现如下<br><figure><table><tr>\n<td><pre><p>1</p><p>2</p><p>3</p><p>4</p><p>5</p><p>6</p><p>7</p><p>8</p><p>9</p><p>10</p><p>11</p><p>12</p><p>13</p><p>14</p><p>15</p><p>16</p><p>17</p><p>18</p><p>19</p><p>20</p><p>21</p><p>22</p><p>23</p><p>24</p><p>25</p><p>26</p><p>27</p><p>28</p><p>29</p><p>30</p><p>31</p><p>32</p><p>33</p><p>34</p><p>35</p></pre></td>\n<td><pre><p> WITH RECURSIVE edges AS (</p><p> <span>SELECT</span> <span>id</span>, neighbor, <span>value</span> <span>FROM</span> graph</p><p> <span>UNION</span> ALL</p><p> <span>SELECT</span> neighbor, <span>id</span>, <span>value</span> </p><p> <span>FROM</span> graph</p><p>), </p><p>all_path (<span>id</span>, neighbor, <span>value</span>, <span>path</span>, <span>depth</span>, <span>cycle</span>) <span>AS</span> (</p><p> <span>SELECT</span></p><p> <span>id</span>, neighbor, <span>value</span>, <span>ARRAY</span>[<span>id</span>], <span>1</span>, <span>'f'</span>::<span>BOOLEAN</span></p><p> <span>FROM</span> edges</p><p> <span>WHERE</span> <span>id</span> = <span>'A'</span></p><p> <span>UNION</span> ALL</p><p> <span>SELECT</span></p><p> all_path.id,</p><p> edges.neighbor,</p><p> edges.value + all_path.value,</p><p> all_path.path || <span>ARRAY</span>[edges.id],</p><p> <span>depth</span> + <span>1</span>,</p><p> edges.id = <span>ANY</span>(all_path.path)</p><p> <span>FROM</span> edges</p><p> <span>JOIN</span> all_path</p><p> <span>ON</span> all_path.neighbor = edges.id</p><p> <span>AND</span> <span>NOT</span> <span>cycle</span></p><p>), a_f <span>AS</span> (</p><p> <span>SELECT</span></p><p> <span>rank</span>() <span>over</span>(<span>order</span> <span>by</span> <span>value</span>) <span>AS</span> <span>rank</span>,</p><p> <span>path</span> || neighbor <span>AS</span> <span>path</span>,</p><p> <span>value</span>,</p><p> <span>depth</span></p><p> <span>FROM</span> all_path</p><p> <span>WHERE</span> neighbor = <span>'F'</span></p><p>)</p><p><span>SELECT</span> <span>path</span>, <span>value</span>, <span>depth</span></p><p><span>FROM</span> a_f</p><p><span>WHERE</span> <span>rank</span> = <span>1</span>;</p></pre></td>\n</tr></table></figure></p>\n<h1>\n<a title=\"WITH RECURSIVE 使用限制\" href=\"http://www.jasongj.com/sql/cte/#WITH-RECURSIVE-%E4%BD%BF%E7%94%A8%E9%99%90%E5%88%B6\"></a>WITH RECURSIVE 使用限制</h1>\n<ul>\n<li>如果在recursive term中使用LEFT JOIN，自引用必须在“左”边</li>\n<li>如果在recursive term中使用RIGHT JOIN，自引用必须在“右”边</li>\n<li>recursive term中不允许使用FULL JOIN</li>\n<li>recursive term中不允许使用GROUP BY和HAVING</li>\n<li>不允许在recursive term的WHERE语句的子查询中使用CTE的名字</li>\n<li>不支持在recursive term中对CTE作aggregation</li>\n<li>recursive term中不允许使用ORDER BY</li>\n<li>LIMIT / OFFSET不允许在recursive term中使用</li>\n<li>FOR UPDATE不可在recursive term中使用</li>\n<li>recursive term中SELECT后面不允许出现引用CTE名字的子查询</li>\n<li>同时使用多个CTE表达式时，不允许多表达式之间互相访问（支持单向访问）</li>\n<li>在recursive term中不允许使用FOR UPDATE</li>\n</ul>\n<h1>\n<a title=\"CTE 优缺点\" href=\"http://www.jasongj.com/sql/cte/#CTE-%E4%BC%98%E7%BC%BA%E7%82%B9\"></a>CTE 优缺点</h1>\n<ul>\n<li>可以使用递归 WITH RECURSIVE，从而实现其它方式无法实现或者不容易实现的查询</li>\n<li>当不需要将查询结果被其它独立查询共享时，它比视图更灵活也更轻量</li>\n<li>CTE只会被计算一次，且可在主查询中多次使用</li>\n<li>CTE可极大提高代码可读性及可维护性</li>\n<li>CTE不支持将主查询中where后的限制条件push down到CTE中，而普通的子查询支持</li>\n</ul>\n<h1>\n<a title=\"SQL优化系列\" href=\"http://www.jasongj.com/sql/cte/#SQL%E4%BC%98%E5%8C%96%E7%B3%BB%E5%88%97\"></a>SQL优化系列</h1>\n</div>\n</div></div><br>\n<br>\n<br>\n<br>\n<br>\n<br>\nvia 技术世界 <br>\n<br>\nOctober 21, 2018 at 07:38PM"}}
{"actor":{"display_login":"guevara"}}