PostgreSQL程式碼分析,查詢最佳化部分,process_duplicate_ors,postgresql

來源:互聯網
上載者:User

PostgreSQL程式碼分析,查詢最佳化部分,process_duplicate_ors,postgresql


PostgreSQL程式碼分析,查詢最佳化部分。



/* * process_duplicate_ors *  Given a list of exprs which are ORed together, try to apply *  the inverse OR distributive law. * * Returns the resulting expression (could be an AND clause, an OR * clause, or maybe even a single subexpression). *//* * 假設我們有四個表,分別是TEST_A,TEST_B,TEST_C,TEST_D,每個表有一列分別是A,B,C,D, * 也就是TEST_A有一個A列,TEST_B有一個B列,以此類推。 *  * 這個函數處理這種情況,對於一個選擇,SELECT * FROM TEST_A,TEST_B,TEST_C,TEST_D * WHERE (A=1 AND B=1) OR (A=1 AND C=1) OR (A=1 AND D=1); *  * 語句中的WHERE條件: *(A=1 AND B=1) OR (A=1 AND C=1) OR (A=1 AND D=1) * 可以改寫為: *(A=1)AND (B=1 OR C=1 OR D=1) * 這就是這個函數的主要功能。 * * 這個函數的參數是一個list, 對於上述的WHERE條件,orlist的結構如下: * orlist中有一個元素,是OR_EXPR類型的BoolExpr,BoolExpr中的結構如下:typedef struct BoolExpr{Expr xpr; = 略BoolExprType boolop; = OR_EXPRList *args;= OR中的3個條件,即(A=1 AND B=1) OR (A=1 AND C=1) OR (A=1 AND D=1)bool plusFlag; = 略} BoolExpr; * * 下面分析函數的具體實現,大致的步驟為: * 1)分析每個OR中的公用項, 2)提取公用項, 3)合并剩餘項為AND。 */static Expr *process_duplicate_ors(List *orlist){List   *reference = NIL;intnum_subclauses = 0;List   *winners;List   *neworlist;ListCell   *temp;if (orlist == NIL)return NULL;/* probably can't happen *//* 如果只有一個。。。。,那就算了吧 */if (list_length(orlist) == 1)/* single-expression OR (can this * happen?) */return linitial(orlist);/* * Choose the shortest AND clause as the reference list --- obviously, any * subclause not in this clause isn't in all the clauses. If we find a * clause that's not an AND, we can treat it as a one-element AND clause, * which necessarily wins as shortest. *//* * “找最短”。 * 在WHERE語句中: *(A=1 AND B=1) OR (A=1 AND C=1) OR (A=1 AND D=1) * OR操作串聯了3個子語句,找到其中最短的一個,因為如果有公用項,那麼最短的那個也一定 * 包含公用項,那麼通過找到最短的那個,在後面的操作裡能減少 比較 的次數。 * 在上面的WHERE語句中,3個子語句的長度相同,按照如下執行過程,找到的應該是(A=1 AND B=1), * 即第一個。 */foreach(temp, orlist){Expr   *clause = (Expr *) lfirst(temp);if (and_clause((Node *) clause)){List   *subclauses = ((BoolExpr *) clause)->args;intnclauses = list_length(subclauses);/*  * 這裡判斷子語句裡的長度,比如對於(A=1 AND B=1)子語句, * 他實際上是一個AND串連起來的兩個 子子語句, 那麼他的長度就是2。 * * 通過nclauses記錄最短的子語句,如果有更短的(nclauses < num_subclauses), * 那麼就替換成最短的。 */if (reference == NIL || nclauses < num_subclauses){reference = subclauses;num_subclauses = nclauses;}}else{/*  * 還有一種情況, 就是可能子句不是一個AND語句,這樣看上去不大符合規則, * 那麼把他看做一個整體,那這個就是最短元素。 *  ****************************** * 如果代碼執行到這裡,那麼只有兩種情況: * 一種是 ... WHERE (A=1 AND B=1) OR (A=1 AND C=1) OR (A=1)。 * 一種是 ... WHERE ((A=1 OR C=1) AND B=1) OR (A=1 OR C=1). * 如果是這兩種情況,都可以做如下簡化: * 第一種情況簡化為 A=1 * 第二種情況化簡為 (A=1 OR C=1) *  * 第三種情況待補充... */reference = list_make1(clause);break;}}/* * Just in case, eliminate any duplicates in the reference list. *//* 找到最短的, 存到List */reference = list_union(NIL, reference);/* * Check each element of the reference list to see if it's in all the OR * clauses.  Build a new list of winning clauses. *//* * “找公用項”。 * * NOTE:這時候就能體現“找最短”帶來的優勢,外層迴圈次數會少一些。 * * 如果WHERE語句是: *(A=1 AND B=1) OR (A=1 AND C=1) OR (A=1 AND D=1) * “找最短”中找到的一定是(A=1 AND B=1)。 * 則外層會有兩次迴圈...(foreach(temp, reference)),兩次迴圈的變數分別為 *A=1 和 B=1。 * 內層有三次迴圈...(foreach(temp2, orlist)),三次迴圈的變數分別為 *(A=1 AND B=1) 和 (A=1 AND C=1) 和 (A=1 AND D=1) *  * 樣本如下: * 假如現在外層迴圈第一次執行,即尋找A=1的公用項,進而假如內層迴圈也是第一次執行, * 即在(A=1 AND B=1)中尋找是否存在A=1這個公用項,發現是存在的(list_member), * 則依次判斷內層迴圈的第二個子句... * * 如上例,具體來說,這些迴圈分別作的操作是: *外層第一次: *判斷A=1是否在(A=1 AND B=1),在,判斷下一個 *判斷A=1是否在(A=1 AND C=1),在,判斷下一個 *判斷A=1是否在(A=1 AND D=1),在,A=1是公用項,記錄(winners = lappend...) *外層第二次: *判斷B=1是否在(A=1 AND B=1),在,判斷下一個 *判斷B=1是否在(A=1 AND C=1),不在,跳出迴圈,下一個不用判斷了。 *判斷B=1是否在(A=1 AND D=1),未執行,因為上一個不含公用項,就不可能提取了。 */winners = NIL;foreach(temp, reference){Expr   *refclause = (Expr *) lfirst(temp);boolwin = true;ListCell   *temp2;foreach(temp2, orlist){Expr   *clause = (Expr *) lfirst(temp2);if (and_clause((Node *) clause)){if (!list_member(((BoolExpr *) clause)->args, refclause)){win = false;break;}}else{if (!equal(refclause, clause)){win = false;break;}}}if (win)winners = lappend(winners, refclause);}/* * If no winners, we can't transform the OR */if (winners == NIL)return make_orclause(orlist);/* * Generate new OR list consisting of the remaining sub-clauses. * * If any clause degenerates to empty, then we have a situation like (A * AND B) OR (A), which can be reduced to just A --- that is, the * additional conditions in other arms of the OR are irrelevant. * * Note that because we use list_difference, any multiple occurrences of a * winning clause in an AND sub-clause will be removed automatically. *//* * “提取公用項”。 * 用list_difference刪除公用項,實現細節不在贅述。 */neworlist = NIL;foreach(temp, orlist){Expr   *clause = (Expr *) lfirst(temp);if (and_clause((Node *) clause)){List   *subclauses = ((BoolExpr *) clause)->args;/* 看這裡...看這裡..., 消除公用項 */subclauses = list_difference(subclauses, winners);if (subclauses != NIL){/* 消除後,剩餘的拼接起來,拼接成:(B=1 OR C=1 OR D=1)*/if (list_length(subclauses) == 1)neworlist = lappend(neworlist, linitial(subclauses));elseneworlist = lappend(neworlist, make_andclause(subclauses));}else{/* * 這說明子語句中,有一個全部是公用項,也就是如下形式: *... WHERE (A=1 AND B=1) OR (A=1) * * 這時候公用項是A=1,第一個子句是(A=1 AND B=1),第二個子句是(A=1), * 第二個子句經過list_difference,返回的結果是NULL。 * 對於這種情況,實際上可以化簡為:A=1,因為(A=1 AND B=1)一定滿足A=1的情況。 */neworlist = NIL;/* degenerate case, see above */break;}}else{if (!list_member(winners, clause))neworlist = lappend(neworlist, clause);else{neworlist = NIL;/* degenerate case, see above */break;}}}/* * Append reduced OR to the winners list, if it's not degenerate, handling * the special case of one element correctly (can that really happen?). * Also be careful to maintain AND/OR flatness in case we pulled up a * sub-sub-OR-clause. */if (neworlist != NIL){if (list_length(neworlist) == 1)winners = lappend(winners, linitial(neworlist));else/*neworlist裡面應該是(B=1 OR C=1 OR D=1),所以用make_orclause */winners = lappend(winners, make_orclause(pull_ors(neworlist)));}/* * And return the constructed AND clause, again being wary of a single * element and AND/OR flatness. */if (list_length(winners) == 1)return (Expr *) linitial(winners);else/* 返回的形式是:(A=1)AND (B=1 OR C=1 OR D=1),所以會用make_andclause */return make_andclause(pull_ands(winners));}



PostgreSQL的查詢處理部分原始碼分析

更新第一條資料的時候

1+1 = 2

該列上 2 已經存在,所以 update 會出錯。

一次 update 有唯一性限制的列的多條記錄,可不能這樣幹。
 
PostgreSQL:怎查詢基於使用者(role)設定的參數

下面示範下:一 方法一:通過 pg_user 視圖查詢--1.1 設定使用者的 log_statement 參數 postgres=# alter role francs set log_statement="all";ALTER ROLE --1.2 驗證 postgres=# select * From pg_user where usename='francs';-[ RECORD 1 ]--------------------usename | francsusesysid | 24920usecreatedb | fusesuper | fusecatupd | fuserepl | fpasswd | ********valuntil | useconfig | {log_statement=all}--1.3 設定使用者的 maintenance_work_mem 參數 postgres=# alter role francs set maintenance_work_mem="1GB";ALTER ROLE--1.4 再次驗證 postgres=# select * From pg_user where usename='francs';-[ RECORD 1 ]---------------------------------------------usename | francsusesysid | 24920usecreatedb | fusesuper | fusecatupd | fuserepl | fpasswd | ********valuntil | useconfig | {log_statement=all,maintenance_work_mem=1GB} 備忘:上面是通過 pg_user.useconfig 查詢。二 方法二: 通過 pg_db_role_setting catalog 基表查詢--2.1 pg_db_role_setting 表結構 Table "pg_catalog.pg_db_role_setting" Column | Type | Modifiers -------------+--------+----------- setdatabase | oid | not null setrole | oid | not null setconfig | text[] | Indexes: "pg_db_role_setting_databaseid_rol_index" UNIQUE, btree (setdatabase, setrole), tablespace "pg_global"備忘:可見 pg_db_role_setting 會針對資料庫,使用者層級進行記錄。--2.2 驗證 postgres=# select oid,rolname from pg_authid where rolname='francs'; oid | rolname -------+--------- 24920 | francs(1 row)postgres=# select * From pg_db_role_setting where setrole=24920; setdatabase | setrole | setconfig -----------......餘下全文>>
 

相關文章

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.