Oracle筆記五:單行函數

雖然各個數據庫都是支持SQL語句的,但是每一個數據庫也有每一個數據庫自己所支持的操作函數,這些就是單行函數,而如果要想進行數據庫開發的話,除了要會使用SQL之外 ,就是要多學習函數。

單行函數主要分爲以下五類:字符函數、數字函數、日期函數、轉換函數、通用函數;

一、字符函數

字符函數的功能主要是進行字符串數據的操作,下面給出幾個字符函數:

  • UPPER(字符串 | 列):將輸入的字符串變爲大寫返回;
  • LOWER(字符串 | 列):將輸入的字符串變爲小寫返回;
  • INITCAP(字符串 | 列):開頭首字母大寫;
  • LENGTH(字符串 | 列):求出字符串的長度;
  • REPLACE(字符串 | 列):進行替換;
  • SUBSTR(字符串 | 列,開始點 [,結束點]):字符串截取;

Oracle之中有一點比較麻煩,即使要驗證字符串,也必須編寫完整的SQL語句,所以在Oracle數據庫之中爲了用戶查詢方便,所以專門提供了一個“dual”的虛擬表。

範例:觀察轉大寫的函數

SELECT UPPER('hello') FROM dual;
SQL> SELECT UPPER(‘hello’) FROM dual;





UPPER —– HELLO

大寫轉換的用處:在一般的使用之中,用戶輸入數據的時候去關心數據本身存放的是大寫還小寫嗎?

SELECT * FROM emp WHERE ename='&str';
複製代碼
SQL> SELECT * FROM emp WHERE ename='&str';
輸入 str 的值:  smith
原值    1: SELECT * FROM emp WHERE ename='&str'
新值    1: SELECT * FROM emp WHERE ename='smith'

未選定行

SQL> SELECT * FROM emp WHERE ename='&str';
輸入 str 的值:  SMITH
原值    1: SELECT * FROM emp WHERE ename='&str'
新值    1: SELECT * FROM emp WHERE ename='SMITH'

     EMPNO ENAME      JOB              MGR HIREDATE              SAL       COMM     DEPTNO
---------- ---------- --------- ---------- -------------- ---------- ---------- ----------
      7369 SMITH      CLERK           7902 17-12月-80            800                    20
複製代碼

此時如果輸入的是小寫,則肯定無法查詢出數據,所以這個時候不能要求用戶這麼多,所以這個時候只能由程序自己去適應,加入一個函數:

SELECT * FROM emp WHERE ename=UPPER('&str');
複製代碼
SQL> SELECT * FROM emp WHERE ename=UPPER('&str');
輸入 str 的值:  smith
原值    1: SELECT * FROM emp WHERE ename=UPPER('&str')
新值    1: SELECT * FROM emp WHERE ename=UPPER('smith')

     EMPNO ENAME      JOB              MGR HIREDATE              SAL       COMM     DEPTNO
---------- ---------- --------- ---------- -------------- ---------- ---------- ----------
      7369 SMITH      CLERK           7902 17-12月-80            800                    20
複製代碼

當然以上的“&”的操作屬於替代變量的內容,此部分內容不做重點。

範例:觀察轉小寫的操作,將所有的僱員姓名按照小寫字母返回

SELECT LOWER(ename) FROM emp;

範例:將每一個僱員姓名的開頭首字母大寫

SELECT INITCAP(ename) FROM emp;

範例:查詢出每個僱員姓名的長度

SELECT ename,LENGTH(ename) FROM emp;

範例:要求查詢出姓名長度正好是5的僱員信息

SELECT ename,LENGTH(ename) FROM emp
WHERE LENGTH(ename)=5;

範例:使用字母“_”替換掉姓名中的所有字母“A”

SELECT REPLACE(ename,'A','_') FROM emp;

字符串截取操作有兩種語法:

語法一:SUBSTR(字符串 | 列,開始點),表示從開始點一直截取到結尾;

SELECT ename,SUBSTR(ename,3) FROM emp;

語法二:SUBSTR(字符串 | 列,開始點,結束點),表示從開始點截取到結束點,截取部分內容;

SELECT ename,SUBSTR(ename,0,3) FROM emp;

SELECT ename,SUBSTR(ename,1,3) FROM emp;

範例:要求截取每個僱員姓名的後三個字母

  • 正常思路:通過長度-2確定開始點
SELECT ename,SUBSTR(ename,LENGTH(ename)-2) FROM emp;
  • 新思路:設置負數,表示從後指定截取位置;
SELECT ename,SUBSTR(ename,-3) FROM emp;

面試題:請問SUBSTR()函數截取的時候下標從0還是從1開始?

  • 在Oracle數據庫之中,SUBSTR()函數從0或1開始都是一樣的;
  • SUBSTR()也可以設置爲負數,表示由後指定截取的開始點;

二、數字函數

數字函數一共有三個:

  • ROUND(數字 | 列 [,保留小數的位數]):四捨五入的操作;
  • TRUNC(數字 | 列 [,保留小數的位數]):捨棄指定位置的內容;
  • MOD(數字 1,數字2):取模,取餘數;

範例:驗證ROUND()函數

SELECT ROUND(903.53567),ROUND(-903.53567), ROUND(903.53567,2), ROUND(-90353567,-1) FROM dual;
ROUND(903.53567) ROUND(-903.53567) ROUND(903.53567,2) ROUND(-90353567,-1)
---------------- ----------------- ------------------ -------------------
             904              -904             903.54           -90353570

範例:驗證TRUNC()函數

SELECT TRUNC(903.53567),TRUNC(-903.53567), TRUNC(903.53567,2), TRUNC(-90353567,-1) FROM dual;
TRUNC(903.53567) TRUNC(-903.53567) TRUNC(903.53567,2) TRUNC(-90353567,-1)
---------------- ----------------- ------------------ -------------------
             903              -903             903.53           -90353560

範例:取模操作

SELECT MOD(10,3) FROM dual;
 MOD(10,3)
----------
         1

以上的三個主要的數學函數,在學習Java中也會有相匹配的內容。

三、日期函數

如果現在要想進行日期的操作,則首先有一個必須要解決的問題,就是如何取得當前的日期,這個當前日期可以使用“SYSDATE”取得,代碼如下:

SELECT SYSDATE FROM dual;

除了以上的當前日期之外,在日期中也可以進行若干計算:

  • 日期 + 數字 = 日期,表示若干天之後的日期;
SELECT SYSDATE + 3,SYSDATE + 300 FROM dual;
  • 日期 – 數字 = 日期,表示若干天前的日期;
SELECT SYSDATE - 3,SYSDATE - 300 FROM dual;
  • 日期 – 日期 = 數字,表示的是兩個日期間的天數,但是肯定是大日期 – 小日期;

範例:求出每個僱員到今天爲止的僱傭天數

SELECT ename,hiredate,SYSDATE-hiredate FROM emp;

而且很多的編程語言之中,也都會提出一種概念,日期可以通過數字表示出來。

除了以上的三個公式之外,也提供瞭如下的四個操作函數:

  • LAST_DAY(日期):求出指定日期的最後一天;

範例:求出本月的最後一天日期

SELECT LAST_DAY(SYSDATE) FROM dual;
  • NEXT_DAY(日期,星期數):求出下一個指定星期X的日期;

範例:求出下一個週一

SELECT NEXT_DAY(SYSDATE,'星期一') FROM dual;
  • ADD_MONTHS(日期,數字):求出若干月之後的日期;

範例:求出四個月後的日期

SELECT ADD_MONTHS(SYSDATE,4) FROM dual;
  • MONTHS_BETWEEN(日期1,日期2):求出兩個日期之間所經歷的月份;

範例:求出每個僱員到今天爲止的僱傭月份

SELECT ename,hiredate,TRUNC(MONTHS_BETWEEN(SYSDATE,hiredate)) FROM emp;

在所有的開發之中,如果是日期的操作,建議使用以上的函數,因爲這些函數可以避免閏年的問題。

四、轉換函數

現在已經接觸到了Oracle數據庫之中的三種數據:數字(NUMBER)、字符串(VARCHAR2)、日期(DATE),轉換函數的主要功能是完成這幾種數據間的互相轉換操作,一共有三種轉換函數:

  • TO_CHAR(字符串 | 列,格式字符串):將日期或者是數字變爲字符串顯示;
  • TO_DATE(字符串,格式字符串):將字符串變爲DATE數據顯示;
  • TO_NUMBER(字符串):將字符串變爲數字顯示;

a、TO_CHAR()函數

在之前查詢過當前的系統日期時間:

SELECT SYSDATE FROM dual;

這個時候是按照“日-月-年”的格式顯示,很明顯這種顯示格式不符合正常的思路,正常是“年-月-日”,所以這種情況下可以使用TO_CHAR()函數,但是使用此函數的話需要一些格式字符串:年(yyyy),月(mm),日(dd)。

SELECT TO_CHAR(SYSDATE,'yyyy-mm-dd'),TO_CHAR(SYSDATE,'yyyy') year, TO_CHAR(SYSDATE,'mm') month, TO_CHAR(SYSDATE,'dd') day FROM dual;
TO_CHAR(SY YEAR MO DA
---------- ---- -- --
2012-08-12 2012 08 12

但是這個時候的顯示數據之中可以發現會存在前導0,如果要想消除掉這個0的話,可以加入一個“fm”。

SELECT TO_CHAR(SYSDATE,'fmyyyy-mm-dd') day FROM dual;
DAY
----------
2012-8-12

正常人都加0,所以這個標記知道就行了,可是在Oracle之中,DATE裏面是包含了時間的,但是之前的代碼沒有顯示出時間,要想顯示時間則需要增加標記:

SELECT TO_CHAR(SYSDATE,'fmyyyy-mm-dd hh24:mi:ss') day FROM dual;
DAY
-------------------
2012-8-12 16:13:38

一定要注意,使用TO_CHAR()函數之後,所有的內容都是字符串,不再是之前的DATE型數據,TO_CHAR()函數也可以用於數字的格式化上,這個時候每一個“9”表示一位數字的概念,而不是數字9的概念。

SELECT TO_CHAR(89078907890,'L999,999,999,999,999') FROM dual;
TO_CHAR(89078907890,'L999,999,
------------------------------
              ¥89,078,907,890

其中的字母“L”,表示的是“Local”的含義,即:當前的所在的語言環境下的貨幣符號。

b、TO_DATE()函數

此函數的主要功能是將一個字符串變爲DATE型數據。

SELECT TO_DATE('1989-09-12','yyyy-mm-dd') FROM dual;
TO_DATE('1989-
--------------
12-9月 -89

一般此函數在更新數據庫的時候使用較多;

c、TO_NUMBER()函數:基本不用

TO_NUMBER()函數一看就知道是將字符串變數字的:

SELECT TO_NUMBER('1') + TO_NUMBER('2') FROM dual;

但是在Oracle之中真的很智能,所以以上的功能不使用TO_NUMBER()也可完成:

SELECT '1' + '2' FROM dual;
複製代碼
SQL> SELECT TO_NUMBER('1') + TO_NUMBER('2') FROM dual;





TO_NUMBER('1')+TO_NUMBER('2') ----------------------------- 3 SQL> SELECT '1' + '2' FROM dual; ‘1’+’2’ ———- 3
複製代碼

所以現在的TO_NUMBER()函數基本上已經是不考慮了,重點的函數在TO_CHAR()上,其次是TO_DATE()函數。

五、通用函數

通用函數主要有兩個:NVL()、DECODE(),這兩個函數算是Oracle自己的特色函數了;

a、NVL()函數,處理null

範例:要求查詢出每個僱員的全部年薪

SELECT ename,sal,comm,(sal+comm)*12 FROM emp;
複製代碼
SQL> SELECT ename,sal,comm,(sal+comm)*12 FROM emp;

ENAME             SAL       COMM (SAL+COMM)*12
---------- ---------- ---------- -------------
SMITH             800
ALLEN            1600        300         22800
WARD             1250        500         21000
JONES            2975
MARTIN           1250       1400         31800
BLAKE            2850
CLARK            2450
SCOTT             800
KING             5000
TURNER           1500          0         18000
ADAMS            1100
JAMES             950
FORD             3000
MILLER           1300

已選擇14行。
複製代碼

這個時候有的僱員的年薪就變成了null,而造成這種問題的關鍵是在於comm字段上爲null,那麼要想解決這個問題,就必須做一種處理:將null變爲0,而這個就是NVL()函數的作用。

SELECT ename,sal,comm,(sal+NVL(comm,0))*12,NVL(comm,0) FROM emp;
複製代碼
SQL> SELECT ename,sal,comm,(sal+NVL(comm,0))*12,NVL(comm,0) FROM emp;

ENAME             SAL       COMM (SAL+NVL(COMM,0))*12 NVL(COMM,0)
---------- ---------- ---------- -------------------- -----------
SMITH             800                            9600           0
ALLEN            1600        300                22800         300
WARD             1250        500                21000         500
JONES            2975                           35700           0
MARTIN           1250       1400                31800        1400
BLAKE            2850                           34200           0
CLARK            2450                           29400           0
SCOTT             800                            9600           0
KING             5000                           60000           0
TURNER           1500          0                18000           0
ADAMS            1100                           13200           0
JAMES             950                           11400           0
FORD             3000                           36000           0
MILLER           1300                           15600           0

已選擇14行。
複製代碼

b、DECODE()函數:多數值判斷

DECODE()函數非常類似於程序中的if…else…語句,唯一不同的是DECODE()函數判斷的是數值,而不是邏輯條件。

例如,現在要求顯示全部僱員的職位,但是這些職位要求替換爲中文顯示:

  • CLERK:辦事員;
  • SALESMAN:銷售;
  • MANAGER:經理;
  • ANALYST:分析員;
  • PRESIDENT:總裁;

這種判斷肯定是逐行進行判斷,所以這個時候就必須採用DECODE(),而此函數的語法如下:

DECODE(數值 | 列 ,判斷值1,顯示值1,判斷值2,顯示值2,判斷值3,顯示值3,…)

範例:實現顯示的操作功能

SELECT empno,ename,job,DECODE(job,'CLERK','辦事員','SALESMAN','銷售人員','MANAGER','經理','ANALYST','分析員','PRESIDENT','總裁')
FROM emp;
複製代碼
SQL> SELECT empno,ename,job,DECODE(job,'CLERK','辦事員','SALESMAN','銷售人員','MANAGER','經理','ANALYST','分析員','PRESIDENT','總裁')
     FROM emp;

     EMPNO ENAME      JOB       DECODE(J
---------- ---------- --------- --------
      7369 SMITH      CLERK     辦事員
      7499 ALLEN      SALESMAN  銷售人員
      7521 WARD       SALESMAN  銷售人員
      7566 JONES      MANAGER   經理
      7654 MARTIN     SALESMAN  銷售人員
      7698 BLAKE      MANAGER   經理
      7782 CLARK      MANAGER   經理
      7788 SCOTT      CLERK     辦事員
      7839 KING       PRESIDENT 總裁
      7844 TURNER     SALESMAN  銷售人員
      7876 ADAMS      CLERK     辦事員
      7900 JAMES      CLERK     辦事員
      7902 FORD       ANALYST   分析員
      7934 MILLER     CLERK     辦事員

已選擇14行。
複製代碼

DECODE()函數是整個Oracle之中最具特點的函數,一定要將其掌握。

習題講解

1、 選擇部門30中的所有員工。

SELECT * FROM emp WHERE deptno=30;

2、 列出所有辦事員(CLERK)的姓名,編號和部門編號。

SELECT empno, ename, deptno FROM emp WHERE job='CLERK';

3、 找出佣金高於薪金的員工。

SELECT * FROM emp WHERE comm>sal;

4、 找出佣金高於薪金的60%的員工。

SELECT * FROM emp WHERE comm>sal*0.6;

5、 找出部門10中所有經理(MANAGER)和部門20中所有辦事員(CLERK)的詳細資料。

SELECT * FROM emp
WHERE (job='MANAGER' AND deptno=10) OR (job='CLERK' AND deptno=20);

6、 找出部門10中所有經理(MANAGER),部門20中所有辦事員(CLERK),既不是經理又不是辦事員但其薪金大於或等於2000的所有員工的詳細資料。

SELECT * FROM emp
WHERE (job='MANAGER' AND deptno=10) OR (job='CLERK' AND deptno=20)
OR (job NOT IN ('MANAGER','CLERK') AND sal>=2000);

7、 找出收取佣金的員工的不同工作。

SELECT DISTINCT job FROM emp WHERE comm IS NOT NULL;

8、 找出不收取佣金或收取的佣金低於100的員工。

SELECT * FROM emp WHERE comm IS NULL OR comm<100;

9、 找出各月倒數第3天受僱的所有員工。

每一個僱員的僱傭日期肯定是不一樣的,所以現在必須找到每一個僱員僱傭所在月的最後一天,之後按照“日期-數字”的方式求出前三天的日期,這個日期必須和僱傭日期相符合才滿足條件。

SELECT * FROM emp WHERE LAST_DAY(hiredate)-2=hiredate;

10、 找出早於12年前受僱的員工。

如果要求年份,最準確的做法是使用總月數/12;

SELECT * FROM emp WHERE MONTHS_BETWEEN(SYSDATE,hiredate)/12>12;

11、 以首字母大寫的方式顯示所有員工的姓名。

SELECT INITCAP(ename) FROM emp;

12、 顯示正好爲5個字符的員工的姓名。

SELECT ename FROM emp WHERE LENGTH(ename)=5;

13、 顯示不帶有“R”的員工的姓名。

SELECT ename FROM emp WHERE ename NOT LIKE '%R%';

14、 顯示所有員工姓名的前三個字符。

SELECT SUBSTR(ename,0,3) FROM emp;

15、 顯示所有員工的姓名,用“a”替換所有“A”。

SELECT REPLACE(ename,'A','a') FROM emp;

16、 顯示滿10年服務年限的員工的姓名和受僱日期。

SELECT ename, hiredate FROM emp
WHERE MONTHS_BETWEEN(SYSDATE,hiredate)/12>10;

17、 顯示員工的詳細資料,按姓名排序。

SELECT * FROM emp ORDER BY ename;

18、 顯示員工的姓名和受僱日期,根據其服務年限,將最老的員工排在最前面。

SELECT ename, hiredate FROM emp ORDER BY hiredate;

19、 顯示所有員工的姓名、工作和薪金,按工作的降序排序,若工作相同則按薪金排序。

SELECT ename, job, sal FROM emp ORDER BY job DESC,sal;

20、 顯示所有員工姓名、加入公司的年份和月份,按受僱日期所有月排序,若月份相同則將最早年份的員工排在最前面。

本程序需要從日期之中取出年份和月份,用TO_CHAR()函數完成。

SELECT ename,TO_CHAR(hiredate,'yyyy') year,TO_CHAR(hiredate,'mm') months
FROM emp
ORDER BY months,year;

21、 顯示在一個月爲30天的情況所有員工的日薪金,忽略餘數。

SELECT ename,sal,TRUNC(sal/30) FROM emp;

22、 找出在(任何年份的)2月受聘的所有員工。

SELECT * FROM emp WHERE TO_CHAR(hiredate,'mm')=2;

23、 對於每個員工,顯示其加入公司的天數。

SELECT ename,SYSDATE-hiredate FROM emp;

24、 顯示姓名字段的任何位置包含“A”的所有員工的姓名。

SELECT ename FROM emp WHERE ename LIKE '%A%';

25、 以年月日的方式顯示所有員工的服務年限。

第一步:求出每個僱員的僱傭年數:被僱傭的總月數 / 12 = 年數;

SELECT ename, hiredate,
TRUNC(MONTHS_BETWEEN(SYSDATE,hiredate)/12) year
FROM emp;

第二步:求出月數,以上計算之中被忽略的小數點實際上都是月份,所以直接取餘即可;

SELECT ename, hiredate,
TRUNC(MONTHS_BETWEEN(SYSDATE,hiredate)/12) year,
TRUNC(MOD(MONTHS_BETWEEN(SYSDATE,hiredate),12)) months
FROM emp;

第三步:求出天數,最準確的做法是在不超過30天的範圍之內求;

現在已經知道當前的時間使用SYSDATE取出,而僱傭的日期使用hiredate取出,可是hiredate和SYSDATE之間的差距太大了,所以肯定會有誤差,那麼就必須想辦法將hiredate的日期提升到與SYSDATE差距在30天的範圍之內。

在之前學習過兩個函數:

  • MONTHS_BETWEEN():求出兩個日期間的月數,如果是:MONTHS_BETWEEN(SYSDATE,hiredate)求出的是僱傭日期到今天爲止的僱傭月份;
  • ADD_MONTHS():在一個日期上加入指定的月之後的日期,如果說hiredate + 與今天相距的月數 = 一個新的日期,而且這個新的日期肯定和SYSDATE相距不超過30天。
SELECT ename, hiredate,
TRUNC(MONTHS_BETWEEN(SYSDATE,hiredate)/12) year,
TRUNC(MOD(MONTHS_BETWEEN(SYSDATE,hiredate),12)) months,
TRUNC(SYSDATE-ADD_MONTHS(hiredate,MONTHS_BETWEEN(SYSDATE,hiredate))) day
FROM emp;

以上的這道程序,屬於日期函數的綜合應用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章