1. 程式人生 > 其它 >Excel & SQL | 資料運算 | 03

Excel & SQL | 資料運算 | 03

目錄

上表儲存了id(銷售人員ID)、 name (銷售人員姓名)、sales_a( a產品銷量) 、sales_b ( b產品銷量)、price_a ( a產品價格)和price_b ( b產品價格)六個欄位。我們把上表中的資料儲存在demo資料庫的chapter7表中。

算術運算

算術運算就是我們所熟悉的加減乘除運算,是比較常見的、簡單的運算。

無論是在Excel中還是SQL中,我們都可以直接對任意兩列或多列進行相應的運算。

在SQL中,我們要對某兩列或多列進行算術運算時,直接將相應的列名與相應的運算子連線即可。現在需要獲取每個銷售產品的所有銷量,即a產品銷量+b產品銷量;a產品與b產品的銷量差﹔每個銷售產品的總銷售額,即a產品銷量×a產品價格+b產品銷量×b產品價格;a產品與b產品的價格倍數;a產品銷量的2倍。具體實現程式碼如下∶

select 
	id,
	(sales_a + sales_b) as all_sales,
	(sales_a - sales_b) as sales_a_b,
	(sales_a * price_a + sales_b * price_b) as gmv,
	(price_a / price_b) as price_a_b,
	(sales_a * 2) as 2_sales_a
    from chapter7;

在SQL中,加減乘除運算的優先順序和數學運算中的優先順序是一樣的,即先算乘除再算加減。
在算術運算中除了加減乘除,還有整除(div)和求餘(%和mod)兩種運算。

select 7 div 2 -- 結果為3
select 7 % 2 -- 結果為1
select 7 mod 2 -- 結果為1

這裡需要特別說明一下與null相關的運算,如果null參與加減乘除的算術運算,會得到什麼結果?

-- 結果都為null 
select 
	1+null,
	1-null,
	1*null,
	1/null;

執行上面的程式碼,結果為4個null,這是因為null與任何數進行運算,結果都是null,類似於0乘任何數都得0。

比較運算

前面的幾個運算子讀者應該比較瞭解,後四個運算子可能不太熟悉,這四個運算子只可以在SQL中使用,而不可以在Excel中使用。

在SQL中,要實現比較運算,需要先指明待比較的具體列,然後用比較運算子將不同的列連線起來。

select 
	id,sales_a,sales_b,
	sales_a>sales_b as "大於",
	sales_a<sales_b as "小於",
	sales_a=sales_b as "等於",
	sales_a!=sales_b as "不等於",
	sales_a is null as "空值",
	sales_a is not null as "非空值"
	from chapter7;

在上面的程式碼中,我們對chapter7表中的sales_a列和sales_b列進行了各種比較運算,最後得到不同的結果,結果展示與Excel有所不同,在Excel中,如果比較結果是正確的,則返回TRUE,否則返回FALSE;而在SQL中如果比較結果是正確的,則返回1,否則返回0。執行上面的程式碼,具體執行結果如下表所示。

比較運算不僅可以被用於列與列之間的比較,也可以被用於前面講的條件篩選中,只需在where後面寫明具體的比較運算即可。比如,我們要獲取a產品的銷量為15~20範圍內的id列和sales_a列,可以通過如下程式碼實現∶

select id,sales_a from chapter7
	where sales_a between 15 and 20;

如果要獲取a產品銷量大於15的id列和sales_a列,則可以通過如下程式碼實現∶

select id,sales_a from chapter7
	where sales_a > 15;

邏輯運算

邏輯運算子主要用來連線多個條件,有and、or、not三種。

在SQL中實現邏輯運算與在Excel中類似,比如,我們要給每個id加兩個標籤∶雙優和單優。雙優的標準是sales_a列和sales_b列均大於15,單優的標準是隻要sales_a列和sales_b列中有一列大於15即可。具體實現程式碼如下:

select id,sales_a,sales_b,
	((sales_a>15)and(sales_b>15)) as "雙優",
	((sales_a>15)or(sales_b>15)) as "單優"
	from chapter7;

執行上面的程式碼,滿足雙優標準的id會被加上1標籤,不滿足的被加上O標籤,單優也是如此,具體執行結果如下:

數學運算

數學運算就是與數學相關的一些運算,比如三角函式、對數運算等。

求絕對值

讀者應該都知道絕對值是什麼意思,求絕對值也是比較常見的一種運算。比如,我們想要求每個id對應的sales_a列和sales_b列的絕對差值,如果直接將這兩列做差,得到的結果肯定有正有負,但我們想要求的是絕對差值,所以我們需要對直接做差後的結果求絕對值。具體實現程式碼如下∶

select id,sales_a,sales_b,
	(sales_a-sales_b) as "差值",
	abs(sales_a-sales_b) as "絕對差值"
	from chapter7;

求最小整數值

有時候,我們會按照某個規則生成某個數對應的整數值,比如,生成不小於x的最小整數值。在SQL中,我們使用的是ceil()函式,具體實現程式碼如下︰

select ceil(2.9);

求最大整數值

與最小整數值對應的是最大整數值,比如,生成不大於x的最大整數值。在SQL中,我們使用的是floor()函式,具體實現程式碼如下:

select floor(2.1);

隨機數生成

所謂隨機數,就是隨機產生的數,在SQL中,我們使用rand()函式來生成隨機數,rand()函式返回0~1範圍內的一個隨機浮點數。

直接執行下面的程式碼,就會得到0~1範圍內的一個隨機浮點數,每次執行下面的程式碼,會得到不同的結果∶

select rand();

小數點位數調整

我們平時會經常遇到各種小數,有的小數的小數點位數比較多,我們可以根據自己的需要進行調整。在SQL中,我們使用round()函式來對小數點位數進行調整。具體實現程式碼如下:

select round(1.1111,2);

如果我們要對一整列的小數點位數進行調整,只需要把1.1111換成對應的列名,把2換成想要保留的小數點位數即可。

如果我們要給每個id對應生成一個隨機數,則可以通過如下程式碼實現︰

select id,rand() as "隨機數" from chapter7;

隨機數生成還可以用在隨機抽樣中,比如,我們現在要從9個id中隨機抽取出3個,現在每個id都有一個隨機數,那麼我們只需要把id按照隨機數大小進行排序,最後前3行的資料就是我們隨機抽樣的結果。具體實現程式碼如下∶

select id,rand() as "隨機數" from chapter7
	order by rand() limit 3;

正負判斷

有時候,我們要判斷兩個數的大小關係,可以對這兩個數進行做差,然後根據差值進行正負判斷,通過正負號就可以得到這兩個數的大小關係。在SQL中,我們使用sign()函式來進行正負判斷。比如,我們要判斷每個id對應
的sales_a列和sales_b列的差值的正負,可以通過如下程式碼實現∶

select id,sales_a,sales_b,
	(sales_a-sales_b) as sales_a_b,
	sign(sales_a-sales_b) as "正負"
	from chapter7;

執行上面的程式碼,就會得到每個id對應的sales_a列和sales_b列的差值,以及差值的正負。如果差值為正,則結果為1;如果差值為負,則結果為-1;如果差值為0,則結果為0。

字串運算

字串運算也是比較常見的一種運算,字串運算的函式如下表所示。

字串替換

有時候,我們需要對一個長字串中的某個或某些字元進行替換。在SQL中,我們使用的是replace()函式,具體實現程式碼如下∶

select repalce("AaAaAa","A","a")

執行上面的程式碼,字串AaAaAa中的所有A被替換成a,最後得到的結果為aaaaaa。

如果我們要對某一列中的每個值進行替換,比如,把chapter7表中id列的字元E替換成e,具體實現程式碼如下:

select id,replace(id,"E","e") as repalce_id from chapter7;

字串合併

字串合併就是將多個字串合併成一個字串,在SQL中,我們使用的是concat()函式。
有的表中姓和名是分為兩列儲存的,所以我們需要將姓和名合併起來組成姓名,具體實現程式碼如下︰

select concat("Hello",", ","World!");

如果將一張表中的兩列或多列合併,則直接在concat()函式的括號中指明要合併的列名即可,比如,將chapter7表中的id列和name列合併,具體實現程式碼如下∶

select id,name,concat(id,name) as id_name from chapter7;

有時候,我們想用固定的符號合併不同的字串或列,這個時候就需要用到另一個函式concat_ws() :

select id,name,concat_ws("-",id,name) as id_name from chapter7;

-- concat()也有同款效果
select id,name,concat(id,"-",name) as id_name from chapter7;

字串擷取

字串擷取就是從一個字串中擷取我們需要的部分字元,主要有左、中、右三種擷取方式。
例如,現在有一個字串2019-10-01 12:30:21,如果我們只想要日期部分,那麼可以擷取這個字串的左邊部分﹔如果我們只想要時間部分,那麼可以擷取這個字串的右邊部分;如果我們只想要月份部分,那麼就可以擷取這個字串的中間部分。
擷取字串的左邊部分使用的是left()函式︰

select left("2019-10-01 12:39:21",10);

上面的程式碼擷取的是字串左邊的10個字元,即2019-10-01。

擷取字串的右邊部分使用的是right()函式:

select right("2019-10-01 12:39:21",8);

上面的程式碼擷取的是字串右邊的8個字元,即12:30:21。

擷取字串的中間部分使用的是substring()函式∶

select substring("2019-10-01 12:39:21",6,2);

上面的程式碼表示從字串的第6位開始擷取,擷取長度為2的字元,
即10。
如果要對某一列中的每個字串進行對應的擷取,只需要把上面的日期時間字串換成對應的列名即可。

字串匹配

字串匹配常用在where中,用於篩選滿足匹配規則的資料。在SQL中用於字串匹配的是like , like在英文中除了喜歡的意思,還有長得像的意思。

like有兩種匹配符號:%和_。

%用於匹配任意長度的字元,可以是0個,而_用於匹配單個長度的字元。

比如,我們要把姓張的同學全部提取出來,假設名字列為name,正常的名字都是先姓後名的,所以想要獲取所有姓張的同學,只需要保證第一個字元是張,後面可以是任意長度的字元,具體實現程式碼如下︰

select * from chapter7 where name like "張%";

再如,我們要把name列中包含凱的名字全部提取出來,只需要保證中間字元是凱,而前面和後面可以是若干個字元,具體實現程式碼如下:

select * from where name like "%凱%";

又如,我們要獲取name列中姓張的,且姓名為兩個字的同學,可以通過如下程式碼實現∶

select * from chapter7 where name like "張_";

上面舉的例子都是獲取name列中能匹配到的資料,如果我們想要獲取匹配不到的資料,只需要把like換成notlike即可。
比如,我們要獲取非張姓同學的資訊,可以通過如下程式碼實現︰

select * from chapter7 where name not like "張%";

字串計數

字串計數就是統計一個字串中包含多少個字元。在SQL中,我們使用的是char_length()函式∶

select char_length("sql");
select char_length("我愛學習");

分別執行上面的兩行程式碼,執行第一行程式碼得到的結果為3,執行第二行程式碼得到的結果為4。
char_length()函式類似的一個函式是length(),我們來看一下同樣的字串,使用length()函式會得到什麼結果∶

select length("sql");
select length("我愛學習");

分別執行上面的兩行程式碼,執行第一行程式碼得到的結果依舊為3,但是執行第二行程式碼得到的結果卻變成
了12。
我們可以看出,對於“sql”字串,char_length()和length()函式得到的結果是一樣的。而對於“我愛學習”字串,兩個函式得到的結果卻是不一樣的。這是因為char_length()函式是基於字元計數的,而length()函式是基於位元組計數的。

那什麼是字元,什麼又是位元組呢?字元是由位元組組成的。英文字母1個字元由1個位元組組成;中文1個字元在utf-8編碼環境下是由3個位元組組成的,在g bk編碼環境下是由2個位元組組成的。這裡是utf-8編碼環境,所以使用length()函式對於“我愛學習”字串進行計數得到的結果為12。

去除字串空格

有的字串中會因為各種原因出現空格,但是空格一般不是我們實際想要的資料,空格在一定程度上會導致資料結果出現偏差,比如,空格在字元計數的時候也被算作一個字元。所以我們需要對含有空格的字串進行去除空格操作,有去除字串左邊的空格、去除字串右邊的空格、去除字串兩邊的空格三種方式。具體實現程式碼如下∶

select 
	length(" abcdef ") as str_length,
	length(ltrim(" abcdef ")) as lstr_length,
	length(rtrim(" abcdef ")) as rstr_length,
	length(trim(" abcdef ")) as tstr_length;

字串" abcdef”兩邊各含有1個空格,所以該字串總長度為8;使用ltrim()函式去掉左邊的空格以後字串長度變為7;使用rtrim()函式去掉右邊的空格以後字串長度也變為7;使trim()函式去掉兩邊的空格以後,字串長度變為6。

字串重複

字串重複是將同一個字串重複若干次後合併成一個字串,在SQL中使用的是repeat()函式。具體實現形式如下∶

select repeat(”sql“,3)

上面的程式碼表示將字串Sql重複3次以後合併成一個字串輸出,最後得到的結果為SqlSqlSql.

聚合運算

聚合運算是指將多個值聚合在一起進行某種運算,比如,求和、求平均值等。

count()計數

count(函式是用來對多個非缺失值進行計數的,常用於查看錶中某列有多少非空值,比如,我們要檢視chapter7表中的id列一共有多少非空值,就可以使用count()函式來實現,具體實現程式碼如下∶

selct count(id) from chapter7;

執行上面的程式碼,最後得到的結果為9。
如果我們想要檢視chapter7表中一共有多少行,那麼只需要把括號中的id換成*即可。讀者可能會想,查看錶中隨便一列有多少行不就知道這張表一共有多少行了嗎?多數情況下是可以的,但是如果這一列中有缺失值,那麼資料就會變少,因為count()函式是對非缺失值進行計數的。

select count(*) from chapter7;

我們在前面說過,缺失值主要有三種表現形式:null、空格、空值。null和空值是不算入計數的,而空格是算入計數的。

select count(" "); -- 空格算如計數

執行上面的程式碼,最後得到的結果為1,而執行下面的程式碼,最後得到的結果為0。

select count(null); -- 空值,null不算計數

有時候,表中某些列的值可能會重複,如果我們想得到刪除重複值後的計數,則可以和前面學過的重複值處理相結合,即count()函式和distinct相結合。比如,我們想檢視chapter7表中產品a一共有幾種銷量水平,即
對sales_a列刪除重複值後的計數,具體實現程式碼如下︰

-- 去除重複值計數
selct count(distinct sales_a) from chapter7;

sum()求和

sum()函式主要用於對錶中某列的所有值進行求和彙總,比如,我們要分別獲取chapter7表中產品a和產品b的總銷量,就可以使用sum()函式,具體實現程式碼如下∶

select sum(sales_a),sum(sales_b) from chapter7;

avg()求平均值

avg()函式主要用於對錶中某列的所有值進行求平均值運算,比如,我們要分別獲取chapter7表中產品a和產品b的平均銷量,就可以使用avg()函式,具體實現程式碼如下︰

select avg(sales_a),avg(sales_b) from chapter7;

max()求最大值

max()函式主要用於獲取表中某列的最大值,比如,我們要分別獲
取chapter7表中產品a和產品b的最高銷量,就可以使用max()函式,具體實現程式碼如下︰

select max(sales_a),max(sales_b) from chapter7;

min()求最小值

min()函式與max()函式相對應,用於獲取表中某列的最小值,比如,我們要分別獲取chapter7表中產品a和產品b的最低銷量,就可以使用min()函式,具體實現程式碼如下∶

select min(sales_a),min(sales_b) from chapter7;

求方差

方差用於反映一組資料的離散程度,即波動程度,方差越大,說明資料波動越厲害,方差的計算公式如下∶

在實際工作中,一組資料的總體是比較難獲得的,也就是說,我們看到的資料只是總體資料中的一部分,這個時候的數值個數就是N-1,而不是N。如果分母是N,則表示總體方差﹔如果分母是N-1,則表示樣本方差。

在SQL中,求總體方差,使用的是var_pop()函式﹔求樣本方差,使用的是var_samp()函式。具體實現程式碼如下∶

select var_pop(sales_a),var_samp(sales_a) from chapter7;

求標準差

標準差是方差的開方,也是用於反映資料的離散程度的,讀者可能會想,不是已經用方差來反映資料的離散程度了嗎,為什麼還要用標準差呢?那是因為方差雖然可以反映資料的離散程度,但是不具有實際業務意義。因為標準差與實際資料的單位是一致的,比如,中學生身高的標準差的單位是釐米,而方差是釐米的平方就比較難理解。因為標準差是方差的開方,方差有總體方差和樣本方差,所以標準差也有總體標準差和樣本標準差。
在SQL中,求總體標準差,使用的是std()函式;求樣本標準差,使用的是stddev_samp()函式。具體實現程式碼如下:

select 
	std(sales_a),
	stddev_samp(sales_a)
	from chapter7;

聚合函式之間的運算

上面講的聚合函式都是針對某一列進行聚合的,我們平常還有有針對多列進行聚合的需求,比如,我們要獲取產品a和產品b的總銷量,就需要先對sales_a列進行求和聚合運算,然後對sales_b列進行求和聚合運算,最後把聚合運算後的兩個值進行求和聚合運算,就是產品a和產品b的總銷量。

select 
	sum(sales_a) as a_group,
	sum(sales_b) as b_group,
	sum(sales_a)+sum(sales_b) as a_b
	from chapter7;

需要注意的是,我們在對聚合運算後的sales_ a列和sales_ b列進行求和聚合運算時,使用的是sum(sales_ a) +sum(sales b) ,而非a_ group + b_group ,這是因為a_ group和b_ group是聚合運算後結果的別名,而非表中實際存在的列名,如果直接對二者進行求和聚合運算,程式則會報錯,提示列名不存在。

小結

算術運算
	+ - * / div mod %
	SQL可以直接在select中對兩列進行結果的計算(運算結果為一個新列)
	null和任何數運算,結果都是null
	select 
	id,
	(sales_a + sales_b) as all_sales,
	(sales_a - sales_b) as sales_a_b,
	(sales_a * price_a + sales_b * price_b) as gmv,
	(price_a / price_b) as price_a_b,
	(sales_a * 2) as 2_sales_a
    from chapter7;


比較運算
	> < >= <= != <> = between is not null is null 
	列與列之間的比較
		select 
            id,sales_a,sales_b,
            sales_a>sales_b as "大於",
            sales_a<sales_b as "小於",
            sales_a=sales_b as "等於",
            sales_a!=sales_b as "不等於",
            sales_a is null as "空值",
            sales_a is not null as "非空值"
            from chapter7;
	where中進行比較
		select id,sales_a from chapter7
	where sales_a between 15 and 20;
		select id,sales_a from chapter7
	where sales_a > 15;

邏輯運算
	and or not
	select id,sales_a,sales_b,
        ((sales_a>15)and(sales_b>15)) as "雙優",
        ((sales_a>15)or(sales_b>15)) as "單優"
        from chapter7;

數學運算
	求絕對值 abs()
		select id,sales_a,sales_b,
	(sales_a-sales_b) as "差值",
	abs(sales_a-sales_b) as "絕對差值"
	from chapter7;
	求最小整數值 ceil()
		select ceil(2.9);
	求最大整數值 floor()
		select floor(2.1);
	隨機數生成 rand() 生成0~1之間的浮點隨機數
		select rand();
	小數點位數調整 round(浮點數,位數)
		select rountd(1.1111,2)
		select id,rand() as "隨機數" from chapter7;
		select id,rand() as "隨機數" from chapter7 order by rand() limit 3; # 實現隨機選3個id
	正負判斷
		sign()
		select id,sales_a,sales_b,
            (sales_a-sales_b) as sales_a_b,
            sign(sales_a-sales_b) as "正負"
            from chapter7;

字串運算
	字串替換 replace() 
		select repalce("AaAaAa","A","a")
		select id,replace(id,"E","e") as repalce_id from chapter7;
	字串合併 concat() 可以連線多個
		select concat("Hello ","World!");
		select id,name,concat(id,"-",name) as id_name from chapter7;
	字串擷取
		left() right() substring()
		select left("2019-10-01 12:39:21",10);
		select right("2019-10-01 12:39:21",8);
		select substring("2019-10-01 12:39:21",6,2);
	字串匹配 
		like not like 
		% _
		select * from where name like "%凱%";
		select * from chapter7 where name not like "張%";
	字串計數
		char_length() 基於字元計數
		length() 基於位元組計數
	去除字串空格
		ltrim() rtrim() trim()
	字串重複
		select repeat(”sql“,3)

聚合運算
	count()
		selct count(distinct sales_a) from chapter7;
	sum()
		select sum(sales_a),sum(sales_b) from chapter7;
	avg()
	max()
	min()
	方差 var_pop() var_samp()
		select var_pop(sales_a),var_samp(sales_a) from chapter7;
	標準差 std() stddev_samp()
		select 
            std(sales_a),
            stddev_samp(sales_a)
            from chapter7;
	聚合函式之間的運算
		select 
            sum(sales_a) as a_group,
            sum(sales_b) as b_group,
            sum(sales_a)+sum(sales_b) as a_b
            from chapter7;