用好CASE,輕鬆表達複雜條件

用好CASE,輕鬆表達複雜條件

22 人贊了文章

SQL語言在數據分析中的重要性不言而喻,它是每個數據分析師必備的技能。畢竟,能否準確、快速地從資料庫中取數、得到想要的數據,關係到之後進行的每一步分析。

相對於很多其他語言來說,SQL是最容易入門的。不誇張地說,一些簡單的語句一天之內就能學會。但是,如果想要用最簡單、最高效的語句得到想要的結果,還是需要下一番功夫的。

就我個人而言,因為公司用了Oracle資料庫,有時候也會需要編寫一些語句來驗證或是提取數據。但用得最多的無非就是SELECT,或者JOIN幾個表,都是非常粗淺的內容。為了今後的職業發展,我從頭開始系統地學習了SQL語言。

學完之後可以在SQLZOO這個網站上做練習,題目都很經典。部分答案請參考我的另一篇文章:

Miaooo:SQLZOO?

zhuanlan.zhihu.com圖標

我用的參考書主要是日本作者MICK的《SQL基礎教程》和《SQL進階教程》,鏈接如下:

SQL基礎教程 (豆瓣)?

book.douban.com

SQL進階教程 (豆瓣)?

book.douban.com圖標

這兩本書我都非常推薦,講得淺顯易懂但是卻很實用。

其中有一個知識點給我的印象非常深刻:CASE表達式的運用。接下來我將著重介紹該表達式的一種用法,這也是進階教程中所反覆提到的:特徵函數。作者這樣介紹:

在這裡,CASE表達式的作用相當於進行判斷的函數,用來判斷各個元素(=行)是否滿足了某種條件的集合。

他對此的評價是:

熟練掌握這些方法之後,不管多麼複雜的條件都能輕鬆表達出來(不是誇張,是真的)。

下面就讓我們來研究下是不是真的這麼厲害。


首先了解一下CASE表達式的語法。

CASE WHEN <判斷表達式> THEN <表達式> WHEN <判斷表達式> THEN <表達式> WHEN <判斷表達式> THEN <表達式> ... ELSE <表達式>END

《SQL基礎教程》中這樣介紹:

CASE表達式會對最初的WHEN字句中的<判斷表達式>進行判斷開始執行。如果該表達式的真值為真(TRUE),那麼就返回THEN字句中的表達式,CASE表達式到此為止。如果結果不為真,那麼就跳轉到下一句WHEN字句的判斷之中。如果直到最後的WHEN字句為止返回結果都不為真,那麼就會返回ELSE中的表達式,執行終止。

通過一個簡單的例子就能了解。

假設有這樣一張名為「province」的表:

表:province

想要在結果中顯示每個省份屬於南方還是北方,可以這樣編寫SQL語句:

SELECT name, (CASE WHEN name=上海 THEN 南方 WHEN name=北京 THEN 北方 WHEN name=廣東 THEN 南方 WHEN name=浙江 THEN 南方 WHEN name=河北 THEN 北方 ELSE NULL END) AS areaFROM province

得到的結果如下:

result1

那麼,CASE表達式的特徵函數是如何使用的呢?

我們看一個書上的例子:

表:Students

一張名為Students的表,記錄了學生編號、學院和提交日期。我們想要從中查詢哪些學院的學生全部提交了報告。用CASE表達式可以這樣寫:

SELECT dptFROM StudentsGROUP BY dptHAVING COUNT(*) = SUM(CASE WHEN sbmt_date IS NOT NULL THEN 1 ELSE 0 END)

得到的結果自然是理學院和經濟學院。

這個語句的重點是最後一句,翻譯成通俗的語言就是:這個學院的總人數和提交日期不為空的人數相同,也就等同於全部的學生都提交了報告。

接下來看一個複雜些的例子:

表:TestResult

一張名為TestResult的表(部分內容),記錄了如下信息。我們想要查詢女生平均分比男生平均分高的班級。乍一看是不是覺得無從下手?使用CASE表達式的特徵函數就能輕鬆解決:

SELECT classFROM TestResultsGROUP BY classHAVING AVG(CASE WHEN sex = 男 THEN score ELSE NULL END) < AVG(CASE WHEN sex = 女 THEN score ELSE NULL END)

是不是有一種豁然開朗的感覺?而且代碼非常簡潔優雅。

利用CASE表達式的特徵函數的核心思想就是,在HAVING語句後面將需要查詢的條件列出來,用CASE表達式判斷是否滿足條件,滿足則進行運算,不滿足賦0或者空值(視情況而定)。聚合函數都可以運用在判斷之中。


這一運用在數據分析中也頗有用處,下面我們將在MySQL中,對一個真實的數據集進行實際操作,進行初步的分析。MySQL的安裝等請參考以下猴子老師的文章,可以說非常詳細了:

猴子:超級詳細的mysql安裝指南?

zhuanlan.zhihu.com圖標

本次處理的數據來源於kaggle網站,是一個有關線上課程的數據集,收集了哈佛和MIT開辦的一系列課程的參與情況。

在導入資料庫之前數據經過了一些預處理,將課程主題分成了4列表示,以便進行之後的處理。

將其導入Navicat Premium中。

Table: appendix

首先看一下具體的列名,可以看到該數據集包括了以下內容:Institution(機構)、Course Number(課程編號)、Launch Date(發布日期)、Course Title(課程名稱)、Instructors(講師)、Course Subject(課程主題)、Year(年份)、Honor Code Certificates(是否有榮譽證書)、Participants (Course Content Accessed)(參加人數)、Audited (> 50% Course Content Accessed)(學完一半的人數)、Certified(獲得證書)、 %Audited(%學完一半的人數)、%Certified(%獲得證書)% Certified of > 50% Course Content Accessed(%學完一半的人中獲得證書的人數) 、%Played Video(看過視頻的人數) 、%Posted in Forum (參與論壇的人數%)、%Grade Higher Than Zero(%評分高於0)、Total Course Hours (Thousands)(課程總時長)、Median Hours for Certification(獲得證書所花時間中位數)、Median Age(年齡中位數)、% Male(%男性)、% Female(%女性)、% Bachelors Degree or Higher(%高於本科學歷)。

先查看一下有多少不同主題的課程。創建一個視圖備用。

CREATE VIEW Course_Subject(`subject`)ASSELECT DISTINCT `Course Subject1`FROM appendixUNIONSELECT DISTINCT `Course Subject2`FROM appendixUNIONSELECT DISTINCT `Course Subject3`FROM appendixUNIONSELECT DISTINCT `Course Subject4`FROM appendixORDER BY `Course Subject1`

得到如下結果:

View: Course_Subject

再創建一個視圖,把課程和主題對應起來。為了便於觀察,我們用開辦日期、課程編號、講師來唯一確定一個課程。

CREATE VIEW Course_Number_Subject(launch_date, number, instructors, subject)ASSELECT app.`Launch Date`,app.`Course Number`, app.Instructors, CS.subjectFROM appendix AS app JOIN Course_Subject AS csON CS.subject IN (app.`Course Subject1`, app.`Course Subject2`, app.`Course Subject3`, app.`Course Subject4`)

得到如下結果:

View: Course_Number_Subject

下面我們根據課程主題進行分析。

如果想要得到哪些主題中,80%以上的課程男性比女性參與者多,應該如何查詢呢?

嘗試以下代碼:

SELECT subjectFROM Course_Number_Subject AS cns JOIN appendix AS appON cns.number = app.`Course Number` AND cns.launch_date = app.`Launch Date` AND cns.instructors = app.InstructorsGROUP BY subjectHAVING COUNT(*)*0.8 < SUM(CASE WHEN app.`% Male` > 50 THEN 1 ELSE 0 END)

得到如下結果:

result

可以看出,男性在技術、數學、健康、政府等方面比女性更感興趣。

想要知道哪些主題中,70%以上的課程獲得證書的概率都在5%以上?可以這樣改寫:

SELECT subjectFROM Course_Number_Subject AS cns JOIN appendix AS appON cns.number = app.`Course Number` AND cns.launch_date = app.`Launch Date` AND cns.instructors = app.InstructorsGROUP BY subjectHAVING COUNT(*)*0.7 < SUM(CASE WHEN app.`% Certified` > 5 THEN 1 ELSE 0 END)

得到的結果如下:

result2

可以看出,文科性質的課程更容易獲得證書。

如果想要查詢課程主題的其他信息,改變CASE表達式中的條件就可以了。

該數據集還有很多其他值得探索的方向,這裡只著重於CASE表達式的使用,其他的不多作介紹了。


原先覺得自己已經入了SQL的門,越看書越覺得自己的知識實在是太淺薄。我最近在看一本《Oracle查詢優化改寫》:

Oracle查詢優化改寫技巧與案例 (豆瓣)?

book.douban.com圖標

這本書適合有一定基礎的同學學習,看完上面兩本可以拿來翻翻,主要是要在實際的資料庫練手,書中有一些案例還是很實用的。

簡單的語句想要高效、準確,還是需要下功夫的。學無止境,與大家共勉!


推薦閱讀:

淺談SQL性能調優
系統優化怎麼做-SQL優化

TAG:SQL | SQL語句 | SQL優化 |