文章目錄
源自南京大學軟件學院劉嘉老師的《數據庫開發》課程,課堂筆記
數據庫筆記(4.5)
1.3 統計字符出現的次數
-
問題:統計字符傳中有多少個逗號?
selete(length('10,CLARK,MANAGER')- length(replace('10,CLARK,MANAGER',',','')))/length(',') as cnt from t1
現將字符串中所有的
,
都轉變成空格,然後用原來的長度減去轉化後的長度(要記得除以所有減去的字符的長度)
1.4 刪除不想要的字符
-
問題:從數據裏刪除指定的字符,從左邊的結果集中的數據裏刪除所有的0和元音字母(AEIOU),並且將刪除後的值顯示在STRIPPED2列中
selete ename replace( replace( replace( replace( replace(ename,'A',''),'E',''),'I',''),'O',''),'U','') as stripped1, sal, replace(sal,0,'')stripped2 from emp
-
mysql只能用這種笨方法
1.5 分離數字和字符數據
-
問題:把數據中的數字數據和字符數據分開,怎麼辦?
-
oracle——使用replace和translate解決,先把所有的字母換成小寫的
z
,然後把所有的小寫z刪除。selete replace( translate(data,'0123456789','0000000000','0')ename, to_number( replace( translate(lower(data), 'qwertyuiopasdfghjklzxcvbnm', rpad('z',26,'z')),'z'))sal from( selete ename||sal data from emp )
-
mysql只能用26個replace()函數進行嵌套
- 或者在程序體內將字符串先轉換好,別在mysql語句裏面幹這種蠢事。
1.6 判斷含有字母和數字的字符
-
問題:從表中篩選出部分行數據,篩選條件是隻包含字母和數字字符。
-
mysql直接使用正則表達式
^0-9a-zA-Z
來匹配所有的數字和字母,比較方便selete data from V where data regexp'[^0-9a-zA-Z]'=0 # 如果是字母或者數字就會返回0,不是字母或者數字就會返回1.
-
oracle解決思路:找到所有的可能出現的非字母以及數字的形式;先把所有的字符和數字都轉化爲一個單一的字符,然後就能把它們當成一個字符,然後就可以分離出來,全部刪掉。還是使用translate函數
- 把所有的字母和數字全部變成
a
,如果到某一行所有的內容都是a
,那麼這一行就是我們需要找的行
selete data from V where translate(lower(data),'0123456789qwertyuiopasdfghjklzxcvbnm',rpad('a',36,'a')) = rpad('a',length(data),'a')
- 把所有的字母和數字全部變成
1.7 提取姓名的首字母
-
問題:你想把姓名變成首字母縮寫形式
-
mysql:使用
- concat_ws
- substr
- substring_index
- length
-
oracle:使用
selete replace( replace( translate(replace('Stewie Griffin','.',''), 'qwertyuiopasdfghjklzxcvbnm', rpad('#',26,'#')),'#',''),'','.')||'.' from t1
數據庫筆記 4.6 數值處理
2.0 示例表結構
-
emp表
- EMPNO
- ENAME
- JOB
- MGR
- HIREDATE
- SAL
- COMM
- DEPTNO
-
dept表
- DEPTNO
- DNAME
- LOC
-
爲什麼要在sql裏面寫這些數值計算的東西?
- 因爲這樣可以大大減小與數據庫的交互次數,大大減少了吞吐量
- sql原則——儘量單一sql,完成所有任務
2.1 計算平均值
selete avg(sal) as avg_sal from emp
-
遇到空值怎麼辦?——使用coalesce
selete avg(coalesce(sal,0)) from emp
2.2 計算最大值和最小值
max() min()
空值對於尋找最大值和最小值沒有影響
2.3 求和
sum() 同樣對null值不影響
2.4 計算行數
-
count(*)來計算行數,所有的空值與非空值都會被記入總數
-
但是如果只針對某一列的行數,那麼就不一樣了,不會計算空值(count(*)和count(某一列)是不一樣的)
selete count(comm) from emp
這樣就會把空值排除出去。
-
分組計算不同組的行數:
selete deptno,count(*) from emp group by deptno
2.5 累計求和(Running Total)
-
保留加的過程的中間結果
-
Mysql & Oracle —— sum over
selete ename, sal sum(sal) over (order by sal, empno) as running_total from emp order by 2 # order by empno
2.6 計算衆數
-
Oracle
selete max(sal) keep(dense_rank first order by cnt desc)sal from( select sal, count(*)cnt from emp where deptno=20 group by sal )
-
Mysql
select sal from emp where deptno=20 group by sal having count(*) >= all (select count(*) from emp where deptno = 20 group by sal)
2.7 計算中位數
-
mysql
selete avg(sal) from ( select e.sal from emp e, emp d where e.deptno = d.deptno and e.deptno = 20 group by e.sal having sum(case when e.sal = d.sal then 1 else 0 end) >= abs(sum(sign(e.sal - d.sal))) )
-
Oracle——直接有中位數接口
select median(sal) from emp where deptno=20
2.8 計算百分比
-
mysql
select(sum( case when deptno = 10 then sal end)/sum(sal) )*100 as pct from emp
-
Oracle
select distinct(d10/total)*100 as pct from ( select deptno, sum(sal)over() total, sum(sal)over(partition by deptno)d10 from emp )x where deptno = 10
2.9 計算平均值時去掉最大值和最小值
-
mysql
select avg(sal) from emp where sal not in( (select min(sal) from emp), (select max(sal) from emp) )
-
Oracle
select avg(sal) from( select sal, min(sal) over() min_sal,max(sal) over() max_sal from emp )x where sal not in (min_sal, max_sal)
2.10 修改累計值
- 問題,你想一句另一列的值來修改累計值。有這樣一個場景,你希望顯示一個信用卡賬戶的交易歷史,並顯示每一筆交易完成後的餘額。
Part 3 日期處理
3.1 年月日加減法
-
問題,以員工CLARK的hiredate爲例,計算入職的前後五天,入職的前後五個月,以及入職前後5年的日期,hiredate=‘09-JUN-1981’
-
Oracle 通過
add_months()
操作來以月爲單位進行計算select hiredate - 5 as hd_minus_5D, hiredate + 5 as hd_plus_5D, add_months(hiredate,-5) as hd_minus_5M, add_months(hiredate,5) as hd_plus_5M, add_months(hiredate,-5*12) as hd minus_5Y, add_months(hiredate,5*12) as hd plus_5Y from emp where deptno = 10
-
mysql 使用
date_add()
函數或者interval
select date_add(hiredate,interval -5 day) as hd_minus_5D, date_add(hiredate,interval 5 day) as hd_plus_5D, date_add(hiredate,interval -5 month) as hd_minus_5M, date_add(hiredate,interval 5 month) as hd_plus_5M, date_add(hiredate,interval -5 year) as hd minus_5Y, date_add(hiredate,interval -5 year) as hd plus_5Y from emp where deptno = 10 ------------------ or ------------------ select hiredate - interval 5 day as hd_minus_5D, hiredate + interval 5 day as hd_plus_5D, hiredate - interval 5 month as hd_minus_5M, hiredate + interval 5 month as hd_plus_5M, hiredate - interval 5 year as hd minus_5Y, hiredate - interval 5 year as hd plus_5Y from emp where deptno = 10
3.2 計算兩個日期之間的天數
-
Mysql & SQL Server
select datediff(ward_hd,allen_hd) from ( select hiredate as ward_hd from emp where ename='WARD' )x, ( select hiredate as allen_hd from emp where ename='ALLEN' )y
-
Oracle
select ward_hd-allen_hd from( select hiredate as ward_hd from emp where ename = 'WARD' )x, ( select hiredate as allen_hd from emp where ename = 'ALLEN' )y
3.3 計算兩個日期之間的工作日天數
-
Mysql——使用數據透視表
select sum (case when date_fromat) ……(詳見ppt)
- 計算出兩個日期之間相隔多少天
- 排除掉週末(檢索有多少條滿足“工作日”條件的記錄)
3.4 計算當前記錄和下一條記錄之間的日期差
-
計算deptno = 10的部門的每一個員工入職時間相差多少天
-
Mysql
select x.*, datediff(x.next_hd, x.hiredate)diff from( select e.deptno, e.ename, e.hiredate, (select min(d.hiredate) from emp d where d.hiredate > e.hiredate)next_hd from emp e where e.deptno = 10 )x
3.5 判斷閏年
-
Mysql——需要從當前日期開始,酸楚當前日期是第幾天,然後用date_add函數計算current_date - dayofyear 再加上1來找到1月1號,然後用date_add繼續加一個月,再調用last_day找到最後一天
select day( last_day( date_add( date_add( date_add(current_date)day), interval 1 day), interval 1 month)))dy from t1
3.6 計算一年有多少天
-
計算下一年份的第一天和當前年份的第一天的差值(以天爲單位)
-
Oracle
select add_months(trunc(susdate,'y'),12)-trunc(sysdate,'y') from dual
-
Mysql 找到今年的第一天是比較麻煩的事情;先查出當前的日期是當前的年份的第幾天,然後用當前日期減去那個值 加上1,就能找到今年的第一天;在此基礎上加一年,得到的結果就是當前年份有多少天了
select datediff(curr_year + intercal 1 year),curr_year) from ( select adddate(current_date, -dateofyear(current_date)_1)curr_year from t1 )x
3.7 找到當前月份的第一個和最後一個星期一
-
Oracle 先找到錢一個月份的最後一天,然後調用next_day函數計算緊隨的前一個月的最後一天的第一個星期一,也就是當前月份的第一個星期一。
先用trunc函數計算當前月份的第一天,嗲用last_day函數找到這個月的最後一天,然後再減去7,然後根據減去7之後的日子爲起點,尋找第一個星期一。
select next_day(trunc(sysdate,'mm')-1,'MONDAY')first_monday, next_day(last_day(trunc(sysdate,'mm'))-7,'MONDAY')last_monday from dual
-
Mysql 先找出當前月份的第一天,然後緊接着找出第一個星期一,第七行開始的case when語句,dayofweek函數中,星期一對應的值是2(星期天對應的值是1)
select first_monday, case month(adddate(first_monday,28)) when mth then adddate(first_monday,28) else adddate(first_monday,21) end last_monday from ( select case sign(dayofweek(dy) - 2) when 0 then dy when -1 then adddate(dy,abs(dayofweek(dy) - 2)) when 1 then adddate(dy,(7-(dayofweek(dy)-2))) end first_monday, mth from ( select adddate(adddate(current)date,-day(current_date)),1)dy, month(current_date)mth from t1 )x )y
3.8 依據特定時間單位檢索數據
-
指定月份、星期或者其他時間單位來篩選記錄行
-
比如:找到入職月份是February或者December,而且入職當天是星期二的所有員工
-
Mysql
select ename from emp where monthname(hiredate) in ('February','December') or dayname(hiredate) = 'Tuesday'
-
Oracle
select ename from emp where rtrim(to_char(hiredate,'month')) in ('february','december') or rtrim(...)
3.9 識別重疊的日期區間
-
識別兩段時間是否衝突
-
Mysql
select a.empno, a.ename, concat('project',b.proj_id, 'overlaps project',a.proj_...)