1. 程式人生 > >Postgresql中的分組函式(group by 和 having)

Postgresql中的分組函式(group by 和 having)

在通過了WHERE過濾器之後,生成的輸出表可以繼續用GROUP BY 子句進行分組,然後用HAVING子句刪除一些分組行。 
Sql程式碼  收藏程式碼
  1. SELECT select_list  
  2.  FROM ...  
  3.  [WHERE ...]  
  4.  GROUP BY grouping_column_reference [, grouping_column_reference]...  


GROUP BY 子句 用於把那些在表中所列出的列上共享相同值的行聚集在一起。 這些列的列出順序並沒有什麼關係。 效果是把每組共享相同值的行縮減為一個組行,它代表該組裡的所有行。 這樣就可以刪除輸出裡的重複和/或計算應用於這些組的聚集。 比如: 

Sql程式碼  收藏程式碼
  1. => SELECT * FROM test1;  
  2.  x | y  
  3. ---+---  
  4.  a | 3  
  5.  c | 2  
  6.  b | 5  
  7.  a | 1  
  8. (4 rows)  
  9. => SELECT x FROM test1 GROUP BY x;  
  10.  x  
  11. ---  
  12.  a  
  13.  b  
  14.  c  
  15. (3 rows)  


在第二個查詢裡,我們不能寫成 SELECT * FROM test1 GROUP BY x, 因為欄位 y 裡沒有哪個值可以和每個組相關聯起來。 被分組的欄位可以在選擇列表中引用是因為它們每個組都有單一的數值。 

通常,如果一個表被分了組,那麼沒有在分組中引用的欄位都不能引用,除了在聚集表示式中以外。 一個帶聚集表示式的例子是: 

Sql程式碼  收藏程式碼
  1. => SELECT x, sum(y) FROM test1 GROUP BY x;  
  2.  x | sum  
  3. ---+-----  
  4.  a |   4  
  5.  b |   5  
  6.  c |   2  
  7. (3 rows)  


這裡的 sum 是一個聚集函式,它在整個組上計算一個數值。 有關可用的聚集函式的更多資訊可以在 Section 9.15 中找到。 

    提示: 沒有聚集表示式的分組實際上計算了一個欄位中獨立數值的集合。 我們也可以用 DISTINCT 子句實現(參閱Section 7.3.3)。 

這裡是另外一個例子:它計算每種產品的總銷售額。(而不是所有產品的總銷售額)。 

Sql程式碼  收藏程式碼
  1. SELECT pid, p.name, (sum(s.units) * p.price) AS sales  
  2.   FROM products p LEFT JOIN sales s USING ( pid )  
  3.   GROUP BY pid, p.name, p.price;  


在這個例子裡,欄位pid, p.name,和p.price必須在GROUP BY子句裡, 因為它們都在查詢選擇列表裡被引用到。 (根據產品表具體的設定的不同,名字和價格可能和產品 ID 完全無關,因此理論上額外的分組可能是不必的, 但是這些尚未實現。) 欄位s.units不必在GROUP BY列表裡,因為它只是在一個聚集表示式(sum(...)) 裡使用,它代表一組產品的銷售額。對於每種產品,這個查詢都返回一個該產品的所有銷售額的總和。 

在嚴格的 SQL 裡,GROUP BY只能對源表的列進行分組,但 PostgreSQL 把這個擴充套件為也允許GROUP BY那些在選擇列表中的欄位。也允許對值表示式進行分組,而不僅是簡單的欄位. 

如果一個表已經用GROUP BY子句分了組,然後你又只對其中的某些組感興趣, 那麼就可以用HAVING子句,它很象WHERE子句,用於刪除一個分了組的表中的一些組。 語法是: 
Sql程式碼  收藏程式碼
  1. SELECT select_list FROM ... [WHERE ...] GROUP BY ... HAVING boolean_expression  


在 HAVING 子句中的表示式可以引用分組的表示式和未分組的表示式(後者必須涉及一個聚集函式)。 
例子: 
Sql程式碼  收藏程式碼
  1. => SELECT x, sum(y) FROM test1 GROUP BY x HAVING sum(y) > 3;  
  2.  x | sum  
  3. ---+-----  
  4.  a |   4  
  5.  b |   5  
  6. (2 rows)  
  7. => SELECT x, sum(y) FROM test1 GROUP BY x HAVING x < 'c';  
  8.  x | sum  
  9. ---+-----  
  10.  a |   4  
  11.  b |   5  
  12. (2 rows)  


然後是一個更現實的例子: 
Sql程式碼  收藏程式碼
  1. SELECT product_id, p.name, (sum(s.units) * (p.price - p.cost)) AS profit  
  2.     FROM products p LEFT JOIN sales s USING (product_id)  
  3.     WHERE s.date > CURRENT_DATE - INTERVAL '4 weeks'  
  4.     GROUP BY product_id, p.name, p.price, p.cost  
  5.     HAVING sum(p.price * s.units) > 5000;  


在上面的例子裡,WHERE子句用於那些非分組的欄位選擇資料行。 (表示式只是對那些最近四周發生的銷售為真)。 而HAVING子句選擇那些單價超過 5000 的組的行。 請注意聚集函式不需要在查詢中的所有地方都一樣。