日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

查询反模式 - GroupBy、HAVING的理解

發布時間:2025/4/16 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 查询反模式 - GroupBy、HAVING的理解 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

為了最簡單地說明問題,我特地設計了一張這樣的表。

  

一、GROUP BY單值規則

  規則1:單值規則,跟在SELECT后面的列表,對于每個分組來說,必須返回且僅僅返回一個值。

  典型的表現就是跟在SELECT后面的列,如果沒有使用聚合函數,必須出現在GROUP BY子句后面。

  如下面這個查詢報錯:

  

  因為對于按照部門分組之后,技術部分組有3個編號,銷售部分組有2個編號,你讓數據庫顯示哪個呢?

  如果假設你使用聚合函數COUNT(編號)之后,對于每個部門分組,就只有一個值 - 該部門下的人數:

  

  下面來實戰下,我們希望查詢出每個部門,最高工資的那個人的姓名,部門,工資。

  

  Shit,出師不利。第一次實戰就錯誤了,我們來分析下。

  很明顯,上面的姓名列是不符合單值規則的。我們的一廂情愿想法是,MAX(工資)之后,SQL Server就能自動幫我們返回不符合單值規則的'姓名'。但是很遺憾,SQL Server并沒有這么做。理由如下:

  •   如果兩個人的工資相同,那么應該將哪個人的姓名返回?
  •   如果我們使用的不是MAX()聚合函數,而是SUM、AVG等聚合函數(沒有與之匹配的工資),那么姓名返回哪個?
  •   如果在查詢語句中使用了兩個聚合函數,如MAX(),MIN()。那么應該返回的是MAX工資的姓名,還是MIN工資的姓名呢?
  •   綜上所述,數據庫是不可能能夠根據我們輸入的一個聚合函數,就幫助我們判斷并顯示出不符合單值規則的列的。

      對于MYSQL來說,當有這種不符合單值規則的列時,默認是返回這一組結果的第一條記錄。而SQLite是返回最后一條。

      因此,對于以上查詢,我們要另尋解決方案。

      解決方案1:關聯子查詢

    SELECT 姓名,部門,工資 FROM 工資表 AS T1 WHERE NOT EXISTS (SELECT NULL FROM 工資表 AS T2 WHERE T1.部門 = T2.部門 AND T2.工資 > T1.工資)

      輸出如下:

      

      完全符合要求。對于上面的關聯子查詢,可以理解為:

      遍歷工資表的所有記錄,查找不存在比當前記錄部門相同且工資還大的記錄。

      雖然,關聯子查詢的語法非常簡單,但是性能并不好。因為對于每一條記錄,都要執行一次子查詢。

      解決方案2:衍生表

       使用衍生表的思路是,先執行一個子查詢,得到一個臨時結果集,然后用臨時結果集和原表進行INNER JOIN操作。就能得到最高工資的人的信息。

      

      剛寫出這個SQL語句時,覺得非常妙,理解了之后覺得非常妙。

    SELECT 姓名,T1.部門,工資 FROM 工資表 AS T1 INNER JOIN (SELECT 部門,MAX(工資) AS 最高 FROM 工資表 --執行查詢,先記錄兩個字段 部門-最高工資GROUP BY 部門 ) AS T2 --衍生表T2 ON T1.部門 = T2.部門 AND 工資 = 最高

      衍生表的方式性能優于關聯子查詢,因為衍生表的方式只執行了一次子查詢。但是它需要一張臨時表來存儲臨時記錄。因此,這個方案也并不是最佳的解決方案。

      解決方案3:使用JOIN + IS NULL

      這是一個更妙的解決方案,當我們用一個外聯結去匹配記錄時,當匹配的記錄不存在,就會用NULL來代替相應的列。

      我們先來看一條非常簡答的SQL語句:

      

      從中你看到了什么?當T2表中,不存在比T1表中工資高的記錄時就返回NULL。

      那么,那么,那么一個IS NULL是不是就解決問題了呢?

      

      好妙,好妙的方法,讓人拍案叫絕的使用了OUTER JOIN。

      JOIN解決方案適用于針對大量數據查詢并且可伸縮比較時。它總是能比基于子查詢的解決方案更好地適應數據量的變量。

      解決方案4:對額外的列使用聚合函數

      我們知道,GROUP BY時,SELECT列表必須返回的是單值,那么我們可不可以通過使用聚合函數,讓這個列返回單值呢?答案是可以的。

      

      其實,返回的數據是有問題的,當工資相同時,它就返回按姓名從大到小排列的第一個姓名。也就是說,當工資相同時,它只能夠返回一條記錄。

      我們將聚合函數換成MIN看看。

      

      解決方案5:Row_Number() + OVER

      WITH B AS(SELECT row_number() OVER(PARTITION BY Name ORDER BY CreateTime) AS part ,Score, Name, CreateTimeFROM xxx)SELECT * FROM B WHERE Part = 1

    ?

      輸出如下:

      

    二、HAVING的理解

      WHERE與HAVING的區別:

    •   WHERE(分組前過濾):WHERE不能對聚合函數列進行過濾,因為執行WHERE的時候,分組尚未執行,聚合函數也未執行。
    •   HAVING(分組后過濾):主要用于對聚合函數列進行過濾,因為HAVING實在分組之后執行的。HAVING子句只能配合GROUP BY子句使用。沒有GROUP BY子句時不能使用HAVING。

      錯誤使用WHERE的示例:

      

      正確使用WHERE與HAVING的示例:

      

    轉載于:https://www.cnblogs.com/zxtceq/p/7160402.html

    總結

    以上是生活随笔為你收集整理的查询反模式 - GroupBy、HAVING的理解的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。