數據庫筆記(4.5 & 4.6)


源自南京大學軟件學院劉嘉老師的《數據庫開發》課程,課堂筆記

數據庫筆記(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)
    
  1. 計算出兩個日期之間相隔多少天
  2. 排除掉週末(檢索有多少條滿足“工作日”條件的記錄)

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_...)
    
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章