問題模擬場景簡述:
首先,以前的oracle中有以下兩張表:user_info,city_info
user_info: user_id, register_time, register_city(整型)
city_info: id, name, pid
city_info採用的是樹形結構,根節點的pid爲0,樹的層級不確定,id從1開始,根節點爲各個省份和直轄市
user_info中的register_city對應的是city_info的id,並且沒有限制其必須是市,縣或是其他級別
現要隨時統計當前月份,各個省份和直轄市新註冊的用戶量,因爲oracle中有start with等相關操作,這個功能可以得到較好的實現,但pgsql裏,需要自己寫遞歸查詢,而且,輸入並不是外部傳入的一個變量或者定好的一個常量,而是另外一個表查詢出的結果集(因爲如果分開執行,查出所有user_info,再遍歷地去調遞歸sql,性能損失極大),實現相當困難且不會有很好的性能。
最終解決方案:
更改表結構,city_info擴增一個字段city_root,用於存放一個city所屬的省級city的id
寫一個遞歸sql一次性更改數據庫已有數據:
先查出所有省級city的id,遍歷調用遞歸sql,
將當前省級city下的所有city(包含省級自身)的city_root填充爲當前省級city的id:
<update id="changeOnce" parameterType="java.lang.Integer"> update city_info t set t.city_root = #{Id} where t.id in (with RECURSIVE cte as (select t1.id, t1.pid from city_info t1 where t1.id = #{Id} union all select t2.id, t2.pid from conf_admin_city t2 inner join cte c on c.id = t2.pid ) select id from cte) </update>
做完數據更改後,更改表的插入sql,使新插入的city記錄,city_root字段填爲父city的city_root
最後效果:性能非常好
因爲現在進行統計的過程只需要連表查詢就可以輕鬆搞定
<select id="getAddCount" resultMap="CountResult"> select t1.city_root, count(1) as addCount from city_info t1, user_info t2 where t2.register_city = t1.id and <![CDATA[t2.register_time < date_trunc( 'month', now())::timestamp + '1 month' ]]> and <![CDATA[t2.register_time >= date_trunc( 'month', now())]]> group by t1.city_root </select>