1. 程式人生 > >SQL之group by order by 和多欄位的理解和聯想

SQL之group by order by 和多欄位的理解和聯想

一. 使用場景:

group by field having count(1) >1用來判斷某個欄位是否有重複值,比如下頁想驗證t_external_member表裡面是否有重複的值,使用如下的sql:

select * from t_external_member a where a.memberid in (select b.memberid from t_external_member b group by b.memberid having count(1) >1)

二. 細節

group by,order by單個欄位很好理解. 但是很多時候,需要group by,order by多個欄位. 理解sql背後怎麼做很重要.

比如group by a1, a2 order by a2,a3表示先按a1分組返回結果集,再這個結果集上再對a2分組返回結果集, 然後針對返回的結果集再對a1排序返回結果集後,再對a2排序.

比如需要一個操作表op,grpid表示組id,每個組下可以有多種操作型別.比如1~10,step表示操作當前的狀態,0表示未執行,1表示執行完. 表裡麵包含了下面的資料.

id, grpid,type  step
1   101   2     0 
2   101   2     0
3   101   3     0
4   101   4     0
5   101   1     0
6   201   3     0
6   201   2     0
8   201   3     1
9   201   1     0
10  201   1     1

需求是找到表中同組下未執行的相同型別的最小id值. 同時優先處理型別是1,2,3的,型別是6的不產生效率,放到最後處理.

?
SELECT   grpid, minid, ntype FROM   SELECT   o.grpid, MIN (o.id) AS minid, DECODE (o.TYPE, 6, 16, 2, 1, 3, 1, o.TYPE) AS ntype FROM   op o WHERE   o.step = 0 AND o.TYPE IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10) GROUP BY   o.grpid, DECODE (o.TYPE, 6, 16, 2, 1, 3, 1, o.TYPE)
ORDER BY   DECODE (o.TYPE, 6, 16, 2, 1, 3, 1, o.TYPE), MIN (o.id))

結果:

1   101   1(2經過decode運算看成了1,所以返回id不是5,而是1)

4   101   4(4不經過decode且該型別在101組只有1個值,返回)

6   201   1

而測試人員誤以為取的是每個組下的最小opid時,所以會用下面的sql檢驗

?
SELECT   GRPID,COUNT(1) FROM   SELECT   O.GRPID, MIN (O.ID) AS MINID, DECODE (O.TYPE, 6, 16, 2, 1, 3, 1, O.TYPE) AS NTYPE FROM   CPC.CPCOPERATION O WHERE   O.STEP = 0 AND O.TYPE IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10) GROUP BY   O.GRPID, DECODE (O.TYPE, 6, 16, 2, 1, 3, 1, O.TYPE) ORDER BY   DECODE (O.TYPE, 6, 16, 2, 1, 3, 1, O.TYPE), MIN (O.ID)) GROUP BY   GRPID HAVING   COUNT (1) > 1;

結果

grpid   count(1)

101     2 (這樣就會出現誤會. 會發現是需求理解的問題, 認為每個組應該只返回一個id, 其實上面的sql的邏輯是每個組下的每個型別的只返回一個值.)

201     1

------------------------------------------------------------------------------------------------

附網上找到的一些基礎資料:
一.資料分組(group by ):

select 列a,聚合函式(聚合函式規範) from 表明

where 過濾條件

group by 列a

group by 字句也和where條件語句結合在一起使用。當結合在一起時,where在前,group by 在後。即先對select xx from xx的記錄集合用where進行篩選,然後再使用group by 對篩選後的結果進行分組。

二.使用having字句對分組後的結果進行篩選,語法和where差不多:having 條件表示式

需要注意having和where的用法區別:

1.having只能用在group by之後,對分組後的結果進行篩選(即使用having的前提條件是分組)。

2.where肯定在group by 之前,即也在having之前。

3.where後的條件表示式裡不允許使用聚合函式,而having可以。

三、當一個查詢語句同時出現了where,group by,having,order by的時候,執行順序和編寫順序是:

1.執行where xx對全表資料做篩選,返回第1個結果集。

2.針對第1個結果集使用group by分組,返回第2個結果集。

3.針對第2個結果集中的每1組資料執行select xx,有幾組就執行幾次,返回第3個結果集。

4.針對第3個結集執行having xx進行篩選,返回第4個結果集。

5.針對第4個結果集排序。

例子:

完成一個複雜的查詢語句,需求如下:

按由高到低的順序顯示個人平均分在70分以上的學生姓名和平均分,為了儘可能地提高平均分,在計算平均分前不包括分數在60分以下的成績,並且也不計算賤人(jr)的成績。

分析:

1.要求顯示學生姓名和平均分

因此確定第1步select s_name,avg(score) from student

2.計算平均分前不包括分數在60分以下的成績,並且也不計算賤人(jr)的成績

因此確定第2步 where score>=60 and s_name!=’jr’

3.顯示個人平均分

相同名字的學生(同一個學生)考了多門科目 因此按姓名分組

確定第3步 group by s_name

4.顯示個人平均分在70分以上

因此確定第4步 having avg(s_score)>=70

5.按由高到低的順序

因此確定第5步 order by avg(s_score) desc

--------------------------------------------

group by聯想

group by 多欄位對於資料庫的資料來源非常方便,下面是網上的一道筆試題,裡面有類似group by的需求,只不過此時資料來源變成了日誌檔案,無法再使用sql語句,需要應用程式設計資料結構實現:

1,使用者訪問網站時,用隨機的一個字串表示該使用者,同一個使用者訪問別的頁面也用相同的字串表示,使用者id稱為userId。
2,網站的每個頁面用英文字串來表示,稱為page_type_id;
3,網站的訪問日誌的格式是:<userId 空格 page_type_id >
4,訪問路徑:找出使用者訪問最多三節訪問路徑,所謂三節路徑是:home--prouduct---prouduct detail;
或者 prouduct--surport --faq  等等。
要求假設已經有了一個這樣的一個日誌檔案,用java寫一個程式,找出最多的三節路徑。
下面是日誌檔案的示例:
123 home
234 product
456 product detail
123 product
456 product

123 product detail
......
解決思路:二個hashmap,m1:HashMap<userid,id1_id2_id3>   m2:HashMap<id1_id2_id3, count>

按照group by的思路,m1相當於按照group by userid, id1_id2_id3,m2相當於聚合函式count(id1_id2_id3)的感覺。

現在的問題是如何用應用程式構造id1_id2_id3這樣的值,同時如何通過累加實現count的功能。

思路如下:

第一個HashMap<userID,page_type_id>,因為日誌是順序新增的,比如:
userID page_type_id
123    home                1
456    production          2
123    home                3

那麼第一個HashMap儲存['123','home'],同理,第三行標示使用者‘123‘在相同頁面重新整理,那麼可以跳過(等同於HashMap儲存了這個值的話),如果沒有遇到相同的話,
比如:
userID page_type_id
123    home                4
123    production          5
這樣路徑就變成了home-production,Map=['123','home,production'],如果在第N行,使用者123的路徑,沒有在home-production的話,比如:
userID page_type_id
123    detail              N
那麼,這個時候第一個HashMap=['123','home,production,detail'],第二HashMap儲存[‘home,production,detail‘,1](count=1).

接下來,又遇到了使用者“123”的話,把第一個HashMap['123','home,production,detail']的值替換掉,即又變成了[‘123’,‘home’],重複前面的做法,當路徑符合三節的時候,把使用者123在一個HashMap上面的值取出來,跟第二個 HashMap[‘home,production,detail‘,1]的鍵(‘home,production,detail‘)比較,如果有的話,+1,如果沒有的話,新增新的key給第二個HashMap。

二個累加器:一個用來計數,判斷是否達到三層。另一個取map的key,取到加1,取不到放入,置1. 典型的hashmap的用法。