想学数据分析但不会Python,过来看看SQL吧(下)~
作者:賈勝杰,碩士,退役軍人,電氣工程專業,現成功轉行K12領域數據挖掘工程師,不僅在數據清理、分析和預測方向,而且在自制力和高效學習方面都有豐富經驗。編輯:王老濕
本篇文章包含的知識點有:分組,子查詢,鏈接表,聚合,條件判斷,時間序列的處理以及數據清理。文章末尾再附上SELECT子句順序和數據分析師的SQL思維導圖。
知識清單
數據分組
創建分組(GROUP BY)
之前學到的篩選操作都是基于整個表去進行的,那如果想要依據某列中的不同類別(比如說不同品牌/不同性別等等)進行分類統計時,就要用到數據分組,在SQL中數據分組是使用GROUP BY子句建立的。
在使用GROUP BY時需要注意的幾點:
GROUP BY子句可以包含任意數量的列,因而可以對分組進行多重嵌套,如按照班級和性別進行分組的話,結果中班級A包含男生組和女生組,班級B也包含男生組和女生組;
GROUP BY子句必須出現在WHERE子句之后,ORDER BY之前。
使用示例:
SELECT?col_1,COUNT(*)?AS?num_col FROM?table_1 GROUP?BY?col_1;以上即可實現按col_1列中的不同類目進行行數統計。
過濾分組(HAVING)
在SQL入門中我們學過WHERE,它是對行數據進行篩選過濾的,那么,如果我想對創建的分組數據進行篩選過濾呢?這時候,你就要用到HAVING子句了,它與WHERE的操作符一致,只是換了關鍵字而已。
使用示例:
SELECT?col_1,COUNT(*)?AS?num_col FROM?table_1 GROUP?BY?col_1 HAVING?COUNT(*)?>=?2;這里我們就篩選出了具有兩個以上類別的分組。
??使用HAVING時應該結合GROUP BY子句。
子查詢與臨時表格
我們之前所涉及到的都是從數據庫中檢索數據的單條語句,但當我們想要檢索的數據并不能直接從數據庫表中獲取,而是需要從篩選后的表格中再度去查詢時,就要用到子查詢和臨時表格了。
子查詢與臨時表格所完成的任務是一致的,只不過子查詢是通過嵌套查詢完成,而另一種是通過WITH創建臨時表格進行查詢。
構建子查詢
構建子查詢十分簡單,只需將被查詢的語句放在小括號里,進行嵌套即可,但在使用時一定要注意格式要清晰。
使用示例:
SELECT?* FROM?(SELECT?day,channel,?COUNT(*)?AS?eventsFROM?web_events?GROUP?BY?1,2?--?按照第一列(day)和第二列(channel)進行分組ORDER?BY?3?DESC)?sub?--?小括號內的查詢語句即為子查詢 GROUP?BY?channel ORDER?BY?2?DESC;如上,我們創建了一個子查詢,放在小括號里,并將其命名為sub。在子查詢中也注意到了各個子句上下對齊,這樣條例更清晰。
臨時表格(WITH)
這種方法,就是使用WITH將子查詢的部分創建為一個臨時表格,然后再進行查詢即可。
我們還是使用上面子查詢的例子,這次用臨時表格的形式實現:
WITH?sub?AS( SELECT?day,channel,?COUNT(*)?AS?events FROM?web_events? GROUP?BY?1,2 ORDER?BY?3?DESC)?--?創建臨時表格SELECT?* FROM?sub??--?對臨時表格進行檢索 GROUP?BY?channel ORDER?BY?2?DESC;?--?這里是根據臨時表格的第二列(channel)進行排序如上,我們將被嵌套的子查詢單獨拎出來,用WITH創建了一個臨時表格,再之后又使用SELECT根據該表格進行查詢。
鏈接表
基本鏈接(JOIN)
SQL最強大的功能之一就是能在數據查詢的執行中進行表的鏈接(JOIN)。
在關系數據庫中,將數據分解為多個表能更有效地存儲,更方便地處理,但這些數據儲存在多個表中,怎樣用一條SELECT語句就檢索出數據呢?那就要使用鏈接。
創建鏈接的方式很簡單,如下便是使用WHERE創建鏈接:
SELECT?col_1,col_2,col_3 FROM?table_1,table_2 WHERE?table_1.id?=?table2.id;如上,col_1和col_2屬于table_1表中,col_3屬于table_2表中,而這兩個表使用相同的id列進行匹配。這種方法被稱為等值鏈接,也就是內鏈接,我們可以使用如下的語句,更直觀地實現內連接:
SELECT?col_1,col_2,col_3 FROM?table_1?INNER?JOIN?table_2 ON?table_1.id?=?table2.id;當然你也可以使用別名,簡化輸入,并且標明各列與表的隸屬關系:
SELECT?t1.col_1,t1.col_2,t2.col_3 FROM?table_1?t1?INNER?JOIN?table_2?t2 ON?t1.id?=?t2.id;如上代碼同樣適用于左鏈接、右鏈接和外鏈接:
LEFT JOIN : 獲取FROM語句后的表格中的所有行,對于那些不存在于 JOIN 語句后的表格中的數據填充None;
RIGHT JOIN : 獲取JOIN語句后的表格中的所有行,對于那些不存在于 FROM語句后的表格中的數據填充None;
FULL JOIN: 只要其中一個表中存在匹配,就返回數據,結果是兩表的并集。
自鏈接
自鏈接經常用于對子查詢的簡化,如下示例:
假如要獲取與Allen同一公司的所有顧客信息,那就需要你先篩選出Allen所在的公司,然后再根據該公司篩選出所有的顧客。使用子查詢的方式如下:
SELECT?id,customer_name,company_name,phone_number FROM?customers WHERE?company_name?=?(SELECT?company_nameFROM?customersWHERE?customer_name?=?'Allen')如果改為自鏈接的方式如下:
SELECT?c1.id,?c1.customer_name,?c1.company_name,?c1.phone_number FROM?customers?c1,customers?c2 WHERE?c1.company_name?=?c2.company_name AND?c2.customer_name?=?'Allen';結果是一樣的,但是使用自鏈接的處理速度比子查詢要快得多。
組合查詢(UNION)
UNION用于合并兩個或多個SELECT 語句的結果集,使用方法也很簡單,只要在多條SELECT語句中添加UNION關鍵字即可。
??UNION 內部的 SELECT 語句必須擁有相同數量的列,列也必須擁有相似的數據類型。而且UNION返回的結果只會選取列中不同的值(即唯一值)。
使用UNION的場合情況:
在一個查詢中從不同的表返回結果;
對一個表執行多個查詢返回結果。
示例:
多數情況下,組合相同表的多個查詢所完成的任務與具有多個WHERE子句的一個查詢是一樣的。
--?語句1:原始語句 --?查詢一 SELECT?customer_name,phone_number FROM?customers WHERE?customer_state?IN?('str1','str2');--查詢二 SELECT?customer_name,phone_number FROM?customers WHERE?customer_state?=?'str3';--?語句2:使用UNION鏈接SELECT?customer_name,phone_number FROM?customers WHERE?customer_state?IN?('str1','str2') UNION SELECT?customer_name,phone_number FROM?customers WHERE?customer_state=?'str3' ORDER?BY?customer_name;--?在最后添加了ORDER BY對所有SELECT語句進行排序,這里只是為了示例在使用UNION時如何進行排序。--?語句3:使用WHERESELECT?customer_name,phone_number FROM?customers WHERE?customer_state?IN?('str1','str2') OR?customer_state?=?'str3';雖然這里看起來使用UNION比WHERE更復雜,但對于較復雜的篩選條件,或者從多個表中檢索數據時,使用UNION更簡單一些。
🙋?♂?如果想要獲取篩選列的所有值,可以使用UNION ALL代替UNION,他們的使用方式是一樣的。
SQL聚合
有時候我們只是需要獲取數據的匯總信息,比如說行數啊、平均值啊這種,并不需要吧所有數據都檢索出來,為此,SQL提供了專門的函數,這也是SQL最強大功能之一。
聚合函數
SQL的聚合函數如下所示:
| AVG() | 返回某列的均值 |
| COUNT() | 返回某列的行數 |
| MAX() | 返回某列的最大值 |
| MIN() | 返回某列的最小值 |
| SUM() | 返回某列的和 |
使用示例:
SELECT?AVG(col_1)?AS?avg_col_1 FROM?table_1;??聚合函數都會忽略列中的NULL值,但是COUNT(*)也就是統計全部數據的行數時,不會忽略NULL值。
聚合不同值
當添加DISTINCT參數時,就可以只對不同值(也就是某列中的唯一值)進行函數操作。
使用示例:
SELECT?AVG(DISTINCT?col_1)?AS?avg_dist_col_1 FROM?table_1;條件判斷
CASE語句是用來做條件判斷的,如果滿足條件A,那么就xxx,如果滿足條件B,那么就xx。
需要注意的幾點:
CASE 語句始終位于 SELECT 條件中。
CASE 必須包含以下幾個部分:WHEN、THEN和 END。ELSE 是可選組成部分,用來包含不符合上述任一 CASE 條件的情況。
你可以在 WHEN 和 THEN之間使用任何條件運算符編寫任何條件語句(例如 WHERE),包括使用 AND 和 OR 連接多個條件語句。
使用示例:
SELECT?account_id,?unit_name, CASE?WHEN?standard_qty?=?0?OR?standard_qty?IS?NULL?THEN?0ELSE?standard_amt_usd/standard_qty?END?AS?unit_price FROM?orders LIMIT?10;如上,我們使用CASE WHEN(條件一) THEN(條件一的結果),ELSE(其他不符合條件一的結果),END語句設立了兩個條件,即當standard_qty為0或者不存在時我們返回0,當standard_qty不為0時進行計算,并儲存為新列unit_price。
時間序列的處理
在SQL中有一套專門的內置函數,用來處理時間序列,那就是DATE函數。
SQL Date 數據類型
先了解一下在不同的數據庫中的時間序列的表示。(了解即可)
MySQL 使用下列數據類型在數據庫中存儲日期或日期/時間值:
DATE - 格式:YYYY-MM-DD
DATETIME - 格式:YYYY-MM-DD HH:MM:SS
TIMESTAMP - 格式:YYYY-MM-DD HH:MM:SS
YEAR - 格式:YYYY 或 YY
SQL Server 使用下列數據類型在數據庫中存儲日期或日期/時間值:
DATE - 格式:YYYY-MM-DD
DATETIME - 格式:YYYY-MM-DD HH:MM:SS
SMALLDATETIME - 格式:YYYY-MM-DD HH:MM:SS
TIMESTAMP - 格式:唯一的數字
`DATE_TRUNC`函數
DATE_TRUNC使你能夠將日期截取到特定部分。常見的截取依據包括日期、月份 和 年份。
語法:
DATE_TRUNC('datepart',?timestamp)其中datepart即為你的截取依據,后面的timestamp類型可以參考上面的Date數據類型。
我總結了一份SQL的datepart速查表放在了下面。
使用示例:
SELECT?DATE_TRUNC('y',col_date)?col_year FROM?table_1 GROUP?BY?1 ORDER?BY?1?DESC LIMIT?10;如上,我們將col_date列按照年(’y’)進行了分組,并按由大至小的順序排序,取前10組數據。
`DATE_PART`函數
DATE_PART 可以用來獲取日期的特定部分,如獲取日期2018-10-6的月份,只會獲得一個結果10,這是它與DATE_TRUNC的最大區別。
語法:
DATE_PART?('datepart',?date或timestamp)其中datepart即為你的截取依據,后面的timestamp類型可以參考上面的Date數據類型。
使用示例:
SELECT?DATE_PART('y',col_date)?col_year FROM?table_1 GROUP?BY?1;如上,我們篩選了col_date列的年份,并依據它做了分組。
想了解更多DATE函數,可以戳SQL日期和時間函數參考
(https://docs.aws.amazon.com/zh_cn/redshift/latest/dg/Date_functions_header.html)
datepart總結
如下給了很多的縮寫,只記住最簡單的即可。
| 世紀 | c、cent、cents |
| 十年 | dec、decs |
| 年 | y、yr、yrs |
| 季度 | qtr、qtrs |
| 月 | mon、mons |
| 周 | w,與 DATE_TRUNC一起使用時將返回離時間戳最近的一個星期一的日期。 |
| 一周中的日 ( DATE_PART支持) | dayofweek、dow、dw、weekday 返回 0–6 的整數(星期日是0,星期六是6)。 |
| 一年中的日 ( DATE_PART支持) | dayofyear、doy、dy、yearday |
| 日 | d |
| 小時 | h、hr、hrs |
| 分鐘 | m、min、mins |
| 秒 | s、sec、secs |
| 毫秒 | ms、msec、msecs、msecond、mseconds、millisec、millisecs、millisecon |
SQL數據清理
這一部分主要針對數據清理講解了幾個SQL中的常用函數,一般來說,也都是用在篩選階段,更詳盡的數據清理還是建議放在python中去進行。
LEFT、RIGHT、LENGTH
LEFT和RIGHT相當于是字符串截取,LEFT 是從左側起點開始,從特定列中的每行獲取一定數量的字符,而RIGHT是從右側。
LENGTH就是獲取字符串的長度,比如說字符串AIGROUP的長度為7。
語法:
LEFT(phone_number,?3)?--?返回從左側數,前3個字符 RIGHT(phone_number,?8)?--?返回從右側數,前8個字符 LENGTH(phone_number)?--?返回phone_number的長度POSITION、STRPOS、SUBSTR
這三個函數都是與位置相關的函數。
POSITION 和STRPOS 可以獲取某一字符在字符串中的位置,這個位置是從左開始計數,最左側第一個字符起始位置為1,但他倆的語法稍有不同。
SUBSTR可以篩選出指定位置后指定數量的字符。
語法:
POSITION(','?IN?city_state)?--?返回‘,’在city_state中的位置 STRPOS(city_state,?‘,’)?--?跟上面的語句等價 SUBSTR(city_state,4,5)?--?返回city_state字符串中,以第4個字符為起始的5個字符。字符串拼接(CONCAT)
顧名思義,就是將兩個字符串進行拼接。
語法:
CONCAT(first_name,?'?',?last_name)?--?結果為:first_name last_name --或者你也可以使用雙豎線來實現上述任務 first_name?||?'?'?||?last_name更改數據格式
TO_DATE函數
TO_DATE函數可以將某列轉為DATE格式,主要是將單獨的月份或者年份等等轉換為SQL可以讀懂的DATE類型數據,這樣做的目的是為了后續可以方便地使用時間篩選函數。
語法:
TO_DATE(col_name,'datepart')? TO_DATE('02?Oct?2001',?'DD?Mon?YYYY');這里是將col_name這列按照datepart轉化為DATE類型的數據,datepart可以參考之前的總結。
CAST函數
CAST函數是SQL中進行數據類型轉換的函數,但經常用于將字符串類型轉換為時間類型。
語法:
CAST(date_column?AS?DATE) --?你也可以寫成這樣 date_column::DATE這里是將date_column轉換為DATE格式的數據,其他時間相關的數據類型與樣式對照可以參考上面寫過的SQL Date數據類型,確保你想轉換的數據樣式與數據類型對應。
缺失值的處理
之前有提到過如何篩選出缺失值,即使用WHERE加上IS NULL或者IS NOT NULL。
那么如何對缺失值進行處理呢?(其實這里可以直接無視,篩選出來后在python中再進行處理)
SQL中提供了一個替換NULL值的函數COALESCE。
使用示例:
COALESCE(col_1,0)?--?將col_1中的NULL值替換為0 COALESCE(col_2,'no?DATA')?--?將col_2中的NULL值替換為no?DATA?總結
好啦,至此課程中的所有SQL知識點已經總結完了,希望大家能夠用得上,除了看這些枯燥的文字和代碼之外,希望大家一定一定要多加練習(SQL zoo),未來的數據分析師之路,還要繼續加油呀!
附:SELECT子句順序
下表中列出了這兩周中涉及到的子句,在進行使用時,應嚴格遵循下表中從上至下的順序。
| SELECT | 要返回的列或表達式 | 是 |
| FROM | 用于檢索數據的表 | 僅在從表中選擇數據時使用 |
| JOIN…ON… | 用于鏈接表 | 僅在需要鏈接表時使用 |
| WHERE | 過濾行數據 | 否 |
| GROUP BY | 分組數據 | 僅在按組計算時使用 |
| HAVING | 過濾分組 | 否 |
| ORDER BY | 對輸出進行排序 | 否 |
| LIMIT | 限制輸出的行數 | 否 |
附:數據分析師的SQL思維導圖
(完)
??長按圖片 1 秒即可關注推薦閱讀:(點擊標題即可跳轉)
總結
以上是生活随笔為你收集整理的想学数据分析但不会Python,过来看看SQL吧(下)~的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 想学数据分析但不会Python,过来看看
- 下一篇: 精心收集汇总的Python学习资源(书籍