国产激情自拍_国产9色视频_丁香花在线电影小说观看 _久久久久国产精品嫩草影院

首頁(yè) > 數(shù)據(jù)庫(kù) > PostgreSQL > 正文

在PostgreSQL中實(shí)現(xiàn)遞歸查詢的教程

2020-10-29 21:50:01
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

 介紹

在Nilenso,哥在搞一個(gè) (開(kāi)源的哦!)用來(lái)設(shè)計(jì)和發(fā)起調(diào)查的應(yīng)用。

下面這個(gè)是一個(gè)調(diào)查的例子:

2015421111930604.png (1436×992)

在內(nèi)部,它是這樣表示滴: 

2015421112055942.png (787×751)

 一個(gè)調(diào)查包括了許多問(wèn)題(question)。一系列問(wèn)題可以歸到(可選)一個(gè)分類(category)中。我們實(shí)際的數(shù)據(jù)結(jié)構(gòu)會(huì)復(fù)雜一點(diǎn)(特別是子問(wèn)題sub-question部分),但先當(dāng)它就只有question跟category吧。


我們是這樣保存question跟category的。

每個(gè)question和category都有一個(gè)order_number字段。是個(gè)整型,用來(lái)指定它自己與其它兄弟的相對(duì)關(guān)系。

舉個(gè)例子,比如對(duì)于上面這個(gè)調(diào)查: 

2015421112241113.png (482×219)

 Bar的order_number比Baz的小。

這樣一個(gè)分類下的問(wèn)題就能按正確的順序出現(xiàn):
 

# In category.rb def sub_questions_in_order questions.order('order_number')end

實(shí)際上一開(kāi)始我們就是這樣fetch整個(gè)調(diào)查的。每個(gè)category會(huì)按順序獲取到全部其下的子問(wèn)題,依此類推遍歷整個(gè)實(shí)體樹(shù)。

這就給出了整棵樹(shù)的深度優(yōu)先的順序: 

2015421112308130.png (999×504)

 對(duì)于有5層以上的內(nèi)嵌、多于100個(gè)問(wèn)題的調(diào)查,這樣搞跑起來(lái)奇慢無(wú)比。

遞歸查詢

哥也用過(guò)那些awesome_nested_set之類的gem,但據(jù)我所知,它們沒(méi)一個(gè)是支持跨多model來(lái)fetch的。

后來(lái)哥無(wú)意中發(fā)現(xiàn)了一個(gè)文檔說(shuō)PostgreSQL有對(duì)遞歸查詢的支持!唔,這個(gè)可以有。

那就試下用遞歸查詢搞搞這個(gè)問(wèn)題吧(此時(shí)哥對(duì)它的了解還很水,有不到位,勿噴)。

要在Postgres做遞歸查詢,得先定義一個(gè)初始化查詢,就是非遞歸部分。

本例里,就是最上層的question跟category。最上層的元素不會(huì)有父分類,所以它們的category_id是空的。
 

( SELECT id, content, order_number, type, category_id FROM questions WHERE questions.survey_id = 2 AND questions.category_id IS NULL)UNION( SELECT id, content, order_number, type, category_id FROM categories WHERE categories.survey_id = 2 AND categories.category_id IS NULL)

(這個(gè)查詢和接下來(lái)的查詢假定要獲取的是id為2的調(diào)查)

這就獲取到了最上層的元素。

2015421112756202.png (615×252)

下面要寫(xiě)遞歸的部分了。根據(jù)下面這個(gè)Postgres文檔: 

2015421112828817.png (883×190)

 遞歸部分就是要獲取到前面初始化部分拿到的元素的全部子項(xiàng)。
 

WITH RECURSIVE first_level_elements AS ( -- Non-recursive term (  (   SELECT id, content, order_number, category_id FROM questions   WHERE questions.survey_id = 2 AND questions.category_id IS NULL  UNION   SELECT id, content, order_number, category_id FROM categories   WHERE categories.survey_id = 2 AND categories.category_id IS NULL  ) ) UNION -- Recursive Term SELECT q.id, q.content, q.order_number, q.category_id FROM first_level_elements fle, questions q WHERE q.survey_id = 2 AND q.category_id = fle.id)SELECT * from first_level_elements;

等等,遞歸部分只能獲取question。如果一個(gè)子項(xiàng)的第一個(gè)子分類是個(gè)分類呢?Postgres不給引用非遞歸項(xiàng)超過(guò)一次。所以在question跟category結(jié)果集上做UNION是不行的。這里得搞個(gè)改造一下:

 

WITH RECURSIVE first_level_elements AS ( (  (   SELECT id, content, order_number, category_id FROM questions   WHERE questions.survey_id = 2 AND questions.category_id IS NULL  UNION   SELECT id, content, order_number, category_id FROM categories   WHERE categories.survey_id = 2 AND categories.category_id IS NULL  ) ) UNION (   SELECT e.id, e.content, e.order_number, e.category_id   FROM   (    -- Fetch questions AND categories    SELECT id, content, order_number, category_id FROM questions WHERE survey_id = 2    UNION    SELECT id, content, order_number, category_id FROM categories WHERE survey_id = 2   ) e, first_level_elements fle   WHERE e.category_id = fle.id ))SELECT * from first_level_elements;

在與非遞歸部分join之前就將category和question結(jié)果集UNION了。

這就產(chǎn)生了所有的調(diào)查元素: 

2015421112900874.png (628×342)

 不幸的是,順序好像不對(duì)。
 
在遞歸查詢內(nèi)排序

這問(wèn)題出在雖然有效的為一級(jí)元素獲取到了全部二級(jí)元素,但這做的是廣度優(yōu)先的查找,實(shí)際上需要的是深度優(yōu)先。

這可怎么搞呢?

Postgres有能在查詢時(shí)建array的功能。

那就就建一個(gè)存放fetch到的元素的序號(hào)的array吧。將這array叫做path好了。一個(gè)元素的path就是:

    父分類的path(如果有的話)+自己的order_number

如果用path對(duì)結(jié)果集排序,就可以將查詢變成深度優(yōu)先的啦!
 

WITH RECURSIVE first_level_elements AS ( (  (   SELECT id, content, category_id, array[id] AS path FROM questions   WHERE questions.survey_id = 2 AND questions.category_id IS NULL  UNION   SELECT id, content, category_id, array[id] AS path FROM categories   WHERE categories.survey_id = 2 AND categories.category_id IS NULL  ) ) UNION (   SELECT e.id, e.content, e.category_id, (fle.path || e.id)   FROM   (    SELECT id, content, category_id, order_number FROM questions WHERE survey_id = 2    UNION    SELECT id, content, category_id, order_number FROM categories WHERE survey_id = 2   ) e, first_level_elements fle   WHERE e.category_id = fle.id ))SELECT * from first_level_elements ORDER BY path;

2015421113222989.png (999×360)

這很接近成功了。但有兩個(gè) What's your favourite song?

這是由比較ID來(lái)查找子項(xiàng)引起的:
 

WHERE e.category_id = fle.id

fle同時(shí)包含question和category。但需要的是只匹配category(因?yàn)閝uestion不會(huì)有子項(xiàng))。

那就給每個(gè)這樣的查詢硬編碼一個(gè)類型(type)吧,這樣就不用試著檢查question有沒(méi)有子項(xiàng)了:

 

WITH RECURSIVE first_level_elements AS ( (  (   SELECT id, content, category_id, 'questions' as type, array[id] AS path FROM questions   WHERE questions.survey_id = 2 AND questions.category_id IS NULL  UNION   SELECT id, content, category_id, 'categories' as type, array[id] AS path FROM categories   WHERE categories.survey_id = 2 AND categories.category_id IS NULL  ) ) UNION (   SELECT e.id, e.content, e.category_id, e.type, (fle.path || e.id)   FROM   (    SELECT id, content, category_id, 'questions' as type, order_number FROM questions WHERE survey_id = 2    UNION    SELECT id, content, category_id, 'categories' as type, order_number FROM categories WHERE survey_id = 2   ) e, first_level_elements fle   -- Look for children only if the type is 'categories'   WHERE e.category_id = fle.id AND fle.type = 'categories' ))SELECT * from first_level_elements ORDER BY path;

2015421113429449.png (691×342)

 這看起來(lái)就ok了。搞定!

下面就看看這樣搞的性能如何。


用下面這個(gè)腳本(在界面上創(chuàng)建了一個(gè)調(diào)查之后),哥生成了10個(gè)子問(wèn)題序列,每個(gè)都有6層那么深。
 

survey = Survey.find(9)10.times do category = FactoryGirl.create(:category, :survey => survey) 6.times do  category = FactoryGirl.create(:category, :category => category, :survey => survey) end FactoryGirl.create(:single_line_question, :category_id => category.id, :survey_id => survey.id)end

每個(gè)問(wèn)題序列看起來(lái)是這樣滴: 

2015421113346254.png (448×397)

 那就來(lái)看看遞歸查詢有沒(méi)有比一開(kāi)始的那個(gè)快一點(diǎn)吧。
 

pry(main)> Benchmark.ms { 5.times { Survey.find(9).sub_questions_using_recursive_queries }}=> 36.839999999999996 pry(main)> Benchmark.ms { 5.times { Survey.find(9).sub_questions_in_order } }=> 1145.1309999999999

快了31倍以上?不錯(cuò)不錯(cuò)。

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
国产激情自拍_国产9色视频_丁香花在线电影小说观看 _久久久久国产精品嫩草影院
中文字幕在线视频免费观看| 亚洲欧洲成人| 日本高清中文字幕二区在线| 91最新在线| 中文在线视频| 四虎成人免费观看在线网址| 久久99精品久久久久久野外| 中文字幕免费中文| 精品女厕厕露p撒尿| 国产私人尤物无码不卡| 国产网站在线播放| 国产95在线|亚洲| 精品国内自产拍在线视频| av亚洲男人天堂| 好吊日视频在线观看| 国产一二三在线观看| 国产超碰在线| 国产免费福利| 天天操中文字幕视频| 中文字幕在线看精品乱码| 777电影在线观看| 欧美xxxx黑人又粗又长| 国产激情视频一区二区三区| 超碰91在线| 99在线免费视频| gogo高清在线播放免费| 国产免费av高清在线| 精品剧情v国产在线观看| 九九热视频精品在线观看| 在线三级av| 国产精品合集一区二区| 日本中文字幕高清视频| 亚洲精品手机在线| 精品国语对白精品自拍视| 午夜免费福利在线观看| 色综合久久五月天| 2021av天天| 国产成人va亚洲电影| 国产亚av手机在线观看 | 毛片在线视频| 91亚洲精选| 精品剧情v国产在线观看| 国产一级二级在线| 久久99精品久久久久久野外| 97在线超碰| 在线天堂中文www视软件| 国产区卡一卡二卡三乱码免费| 免费看的毛片| 欧美高清视频| www.成人.com| 欧美性猛交xxxx免费看久久| av一本在线| 日本三级在线视频| 在线黄色国产电影| 亚洲日本伊人| 九九热精品在线视频| 久草福利资源在线视频| 狠狠操狠狠色| 国产网站在线播放| 在线观看免费黄色| 日本福利午夜视频在线| 狠狠干天天爱| 国产porn在线| www.91在线播放| 国产永久av在线| 国产区av在线| a视频免费看| 国产精选在线视频拍拍拍| 国产在线视频精品视频免费看| 最近中文字幕mv免费高清电影 | 国产毛片视频| 国产精品免费视频一区一| 国产啊啊啊视频在线观看| 在线观看免费观看在线91| 日本aⅴ写真网站免费| 成人超碰在线| 中文乱码字幕av网站| 懂色一区二区三区| 99久久免费精品国产免费| 麻豆精品免费视频入口| 88av在线| 日韩欧美中文字幕不卡| 国产三级在线| 国产69久久| 欧美精品日韩少妇| 国产成人精品18| 国产香蕉在线| 国产免费福利| www在线视频观看| 精品麻豆视频| 国产福利在线免费观看| 中文字幕成人乱码在线电影| 四虎国产精品永久地址998| 国产精品免费视频二三区| 国产在线日本| 亚洲精品国自产拍在线观看| 国产极品嫩模在线视频一区| 国产精品自拍亚洲| 久久久久久国产视频| 四虎成人精品在永久在线观看| 国产日韩欧美一区二区三区视频| 久久精品无码一区二区日韩av| 天堂中文在线观看| 亚洲私人影吧| 亚洲图区综合| 亚洲第一页在线播放| 在线观看的网站你懂的| 亚洲成年人视频| 麻豆网站在线| 国产乱视频在线观看播放| 九九热精品在线视频| 丁香婷婷在线| 国产黄色高清在线| 91久久麻豆| 亚洲欧美日韩综合精品网| 伊人免费视频| 欧美性猛交xxxx免费看蜜桃| 黄色av网址在线免费观看| 国产精品亚洲色图| 国产黄色片大全| 亚洲天堂久久久| 黄色av网站在线| 国产精品久久久精品a级小说| 国产福利在线看| 亚洲日本久久久午夜精品| 色吊丝av中文字幕| 国产免费高清| 精品麻豆国产| 懂色av一区| 99爱视频在线观看| 好男人社区在线视频| 国产激情视频一区二区三区| 夜夜操天天干| 国产网站麻豆精品视频| baoyu777.永久免费视频| 天天操天天操一操| 99色在线观看| 国产蜜臀在线| av网站在线播放| 国产一级片在线| 在线看黄网站| 中文字幕在线免费| 天天插天天干| 精品国产二区三区| 国产性网软件大全| 欧美人成在线观看网站高清| 国产va在线| 国产精品免费91| 黄色国产网站在线播放| 在线观看中文字幕的网站| 国产三级视频在线| 国产裸舞福利在线视频合集| eeuss影院在线观看第一页| 国产精品久久在线| 午夜在线小视频| 国产在线高清| 欧美性猛交p30| 中文在线官网天堂| 国产激情视频一区二区三区| 二人午夜免费观看在线视频| 免费在线播放av| eeuss影院www在线观看| 影音先锋中文字幕在线| 国产91在线视频蝌蚪| 国产福利片在线| 国产盗摄一区二区| 国产高清在线观看| 91中文字幕网| 99高清免费国产自产拍| 精品乱码一区二区三四区视频| 国产精品美女视频免费观看软件| 高潮毛片在线观看| 国产网红在线| 国产黄色在线免费观看| 国产福利视频在线| 中文字幕av网| 青青免费在线视频| 国产秒拍福利视频露脸| 国产一级免费在线观看| 国产成人亚洲欧美电影| 午夜性爽视频男人的天堂| 黄色av免费在线| 国产日产精品久久久久久婷婷| √天堂资源中文www| 亚洲第一成人在线视频| 久久久久久久久免费视频| 国产理论片免费观看| 国产黄a三级三级三级av在线看| 精品国产免费观看一区| 欧美人成在线观看网站高清| 国产精选在线视频拍拍拍| 精品国产美女福利到在线不卡| 日本黄在线观看| 国产福利视频在线| 青青草视频在线免费观看| 国产视频二区三区| 久久精品视频免费看| 亚洲欧美一区二区三区在线播放| 国产免费a∨片在线观看不卡| 国产成人夜间影院在线观看|