PostgreSQL代码分析,查询优化部分,process_duplicate_ors

前端之家收集整理的这篇文章主要介绍了PostgreSQL代码分析,查询优化部分,process_duplicate_ors前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。


这里把规范谓词表达式的部分就整理完了,阅读的顺序如下:

一、PostgreSQL代码分析,查询优化部分,canonicalize_qual

二、PostgreSQL代码分析,查询优化部分,pull_ands()和pull_ors()

三、PostgreSQL代码分析,查询优化部分,process_duplicate_ors


  1. /*
  2. * process_duplicate_ors
  3. * Given a list of exprs which are ORed together,try to apply
  4. * the inverse OR distributive law.
  5. *
  6. * Returns the resulting expression (could be an AND clause,an OR
  7. * clause,or maybe even a single subexpression).
  8. */
  9.  
  10. /*
  11. * 假设我们有四个表,分别是TEST_A,TEST_B,TEST_C,TEST_D,每个表有一列,* 也就是TEST_A有一个A列,TEST_B有一个B列,以此类推。
  12. *
  13. * 这个函数处理这种情况,对于一个选择,SELECT * FROM TEST_A,TEST_D
  14. * WHERE (A=1 AND B=1) OR (A=1 AND C=1) OR (A=1 AND D=1);
  15. *
  16. * 语句中的WHERE条件:
  17. * (A=1 AND B=1) OR (A=1 AND C=1) OR (A=1 AND D=1)
  18. * 可以改写为:
  19. * (A=1)AND (B=1 OR C=1 OR D=1)
  20. * 这就是这个函数的主要功能
  21. *
  22. * 这个函数的参数是一个list,对于上述的WHERE条件,orlist的结构如下:
  23. * orlist中有一个元素,是OR_EXPR类型的BoolExpr,BoolExpr中的结构如下:
  24. typedef struct BoolExpr
  25. {
  26. Expr xpr; = 略
  27. BoolExprType boolop; = OR_EXPR
  28. List *args; = OR中的3个条件,即(A=1 AND B=1) OR (A=1 AND C=1) OR (A=1 AND D=1)
  29. bool plusFlag; = 略
  30. } BoolExpr;
  31. *
  32. * 下面分析函数的具体实现,大致的步骤为:
  33. * 1)分析每个OR中的公共项, 2)提取公共项, 3)合并剩余项为AND。
  34. */
  35. static Expr *
  36. process_duplicate_ors(List *orlist)
  37. {
  38. List *reference = NIL;
  39. int num_subclauses = 0;
  40. List *winners;
  41. List *neworlist;
  42. ListCell *temp;
  43.  
  44. if (orlist == NIL)
  45. return NULL; /* probably can't happen */
  46.  
  47. /* 如果只有一个。。。。,那就算了吧 */
  48. if (list_length(orlist) == 1) /* single-expression OR (can this
  49. * happen?) */
  50. return linitial(orlist);
  51.  
  52. /*
  53. * Choose the shortest AND clause as the reference list --- obvIoUsly,any
  54. * subclause not in this clause isn't in all the clauses. If we find a
  55. * clause that's not an AND,we can treat it as a one-element AND clause,* which necessarily wins as shortest.
  56. */
  57. /*
  58. * “找最短”。
  59. * 在WHERE语句中:
  60. * (A=1 AND B=1) OR (A=1 AND C=1) OR (A=1 AND D=1)
  61. * OR操作串联了3个子语句,找到其中最短的一个,因为如果有公共项,那么最短的那个也一定
  62. * 包含公共项,那么通过找到最短的那个,在后面的操作里能减少 比较 的次数
  63. * 在上面的WHERE语句中,3个子语句的长度相同,按照如下执行过程,找到的应该是(A=1 AND B=1),
  64. * 即第一个。
  65. */
  66. foreach(temp,orlist)
  67. {
  68. Expr *clause = (Expr *) lfirst(temp);
  69.  
  70. if (and_clause((Node *) clause))
  71. {
  72. List *subclauses = ((BoolExpr *) clause)->args;
  73. int nclauses = list_length(subclauses);
  74. /*
  75. * 这里判断子语句里的长度,比如对于(A=1 AND B=1)子语句,
  76. * 他实际上是一个AND连接起来的两个 子子语句, 那么他的长度就是2。
  77. *
  78. * 通过nclauses记录最短的子语句,如果有更短的(nclauses < num_subclauses),
  79. * 那么就替换成最短的。
  80. */
  81. if (reference == NIL || nclauses < num_subclauses)
  82. {
  83. reference = subclauses;
  84. num_subclauses = nclauses;
  85. }
  86. }
  87. else
  88. {
  89. /*
  90. * 还有一种情况, 就是可能子句不是一个AND语句,这样看上去不大符合规则,
  91. * 那么把他看做一个整体,那这个就是最短元素。
  92. *
  93. ******************************
  94. * 如果代码执行到这里,那么只有两种情况:
  95. * 一种是 ... WHERE (A=1 AND B=1) OR (A=1 AND C=1) OR (A=1)。
  96. * 一种是 ... WHERE ((A=1 OR C=1) AND B=1) OR (A=1 OR C=1).
  97. * 如果是这两种情况,都可以做如下简化:
  98. * 第一种情况简化为 A=1
  99. * 第二种情况化简为 (A=1 OR C=1)
  100. *
  101. * 第三种情况待补充...
  102. */
  103. reference = list_make1(clause);
  104. break;
  105. }
  106. }
  107.  
  108. /*
  109. * Just in case,eliminate any duplicates in the reference list.
  110. */
  111. /* 找到最短的, 存到List */
  112. reference = list_union(NIL,reference);
  113.  
  114. /*
  115. * Check each element of the reference list to see if it's in all the OR
  116. * clauses. Build a new list of winning clauses.
  117. */
  118. /*
  119. * “找公共项”。
  120. *
  121. * NOTE:这时候就能体现“找最短”带来的优势,外层循环次数会少一些。
  122. *
  123. * 如果WHERE语句是:
  124. * (A=1 AND B=1) OR (A=1 AND C=1) OR (A=1 AND D=1)
  125. * “找最短”中找到的一定是(A=1 AND B=1)。
  126. * 则外层会有两次循环...(foreach(temp,reference)),两次循环的变量分别为
  127. * A=1 和 B=1。
  128. * 内层有三次循环...(foreach(temp2,orlist)),三次循环的变量分别为
  129. * (A=1 AND B=1) 和 (A=1 AND C=1) 和 (A=1 AND D=1)
  130. *
  131. * 示例如下:
  132. * 假如现在外层循环第一次执行,即查找A=1的公共项,进而假如内层循环也是第一次执行,
  133. * 即在(A=1 AND B=1)中查找是否存在A=1这个公共项,发现是存在的(list_member),
  134. * 则依次判断内层循环的第二个子句...
  135. *
  136. * 如上例,具体来说,这些循环分别作的操作是:
  137. * 外层第一次:
  138. * 判断A=1是否在(A=1 AND B=1),在,判断下一个
  139. * 判断A=1是否在(A=1 AND C=1),在,判断下一个
  140. * 判断A=1是否在(A=1 AND D=1),在,A=1是公共项,记录(winners = lappend...)
  141. * 外层第二次:
  142. * 判断B=1是否在(A=1 AND B=1),在,判断下一个
  143. * 判断B=1是否在(A=1 AND C=1),不在,跳出循环,下一个不用判断了。
  144. * 判断B=1是否在(A=1 AND D=1),未执行,因为上一个不含公共项,就不可能提取了。
  145. */
  146. winners = NIL;
  147. foreach(temp,reference)
  148. {
  149. Expr *refclause = (Expr *) lfirst(temp);
  150. bool win = true;
  151. ListCell *temp2;
  152.  
  153. foreach(temp2,orlist)
  154. {
  155. Expr *clause = (Expr *) lfirst(temp2);
  156.  
  157. if (and_clause((Node *) clause))
  158. {
  159. if (!list_member(((BoolExpr *) clause)->args,refclause))
  160. {
  161. win = false;
  162. break;
  163. }
  164. }
  165. else
  166. {
  167. if (!equal(refclause,clause))
  168. {
  169. win = false;
  170. break;
  171. }
  172. }
  173. }
  174.  
  175. if (win)
  176. winners = lappend(winners,refclause);
  177. }
  178.  
  179. /*
  180. * If no winners,we can't transform the OR
  181. */
  182. if (winners == NIL)
  183. return make_orclause(orlist);
  184.  
  185. /*
  186. * Generate new OR list consisting of the remaining sub-clauses.
  187. *
  188. * If any clause degenerates to empty,then we have a situation like (A
  189. * AND B) OR (A),which can be reduced to just A --- that is,the
  190. * additional conditions in other arms of the OR are irrelevant.
  191. *
  192. * Note that because we use list_difference,any multiple occurrences of a
  193. * winning clause in an AND sub-clause will be removed automatically.
  194. */
  195. /*
  196. * “提取公共项”。
  197. * 用list_difference删除公共项,实现细节不在赘述。
  198. */
  199. neworlist = NIL;
  200. foreach(temp,orlist)
  201. {
  202. Expr *clause = (Expr *) lfirst(temp);
  203.  
  204. if (and_clause((Node *) clause))
  205. {
  206. List *subclauses = ((BoolExpr *) clause)->args;
  207. /* 看这里...看这里...,消除公共项 */
  208. subclauses = list_difference(subclauses,winners);
  209. if (subclauses != NIL)
  210. {
  211. /* 消除后,剩余的拼接起来,拼接成:(B=1 OR C=1 OR D=1)*/
  212. if (list_length(subclauses) == 1)
  213. neworlist = lappend(neworlist,linitial(subclauses));
  214. else
  215. neworlist = lappend(neworlist,make_andclause(subclauses));
  216. }
  217. else
  218. {
  219. /*
  220. * 这说明子语句中,有一个全部是公共项,也就是如下形式:
  221. * ... WHERE (A=1 AND B=1) OR (A=1)
  222. *
  223. * 这时候公共项是A=1,第一个子句是(A=1 AND B=1),第二个子句是(A=1),* 第二个子句经过list_difference,返回的结果是NULL。
  224. * 对于这种情况,实际上可以化简为:A=1,因为(A=1 AND B=1)一定满足A=1的情况。
  225. */
  226. neworlist = NIL; /* degenerate case,see above */
  227. break;
  228. }
  229. }
  230. else
  231. {
  232. if (!list_member(winners,clause))
  233. neworlist = lappend(neworlist,clause);
  234. else
  235. {
  236. neworlist = NIL; /* degenerate case,see above */
  237. break;
  238. }
  239. }
  240. }
  241.  
  242. /*
  243. * Append reduced OR to the winners list,if it's not degenerate,handling
  244. * the special case of one element correctly (can that really happen?).
  245. * Also be careful to maintain AND/OR flatness in case we pulled up a
  246. * sub-sub-OR-clause.
  247. */
  248. if (neworlist != NIL)
  249. {
  250. if (list_length(neworlist) == 1)
  251. winners = lappend(winners,linitial(neworlist));
  252. else
  253. /*neworlist里面应该是(B=1 OR C=1 OR D=1),所以用make_orclause */
  254. winners = lappend(winners,make_orclause(pull_ors(neworlist)));
  255. }
  256.  
  257. /*
  258. * And return the constructed AND clause,again being wary of a single
  259. * element and AND/OR flatness.
  260. */
  261. if (list_length(winners) == 1)
  262. return (Expr *) linitial(winners);
  263. else
  264. /* 返回的形式是:(A=1)AND (B=1 OR C=1 OR D=1),所以会用make_andclause */
  265. return make_andclause(pull_ands(winners));
  266. }

猜你在找的Postgre SQL相关文章