Oracle中的 DML, DDL,DCL
DML:資料操作語言,SQL中處理資料等操作統稱為資料操縱語言
它們是SELECT、UPDATE、INSERT、DELETE,就象它的名字一樣,這4條命令是用來對資料庫裡的資料進行操作的語言,不修改表的結構,只修改表的內容
需要commit 才能真正被執行
DDL:資料定義語言,用於定義和管理 SQL 資料庫中的所有物件的語言
DDL比DML要多,主要的命令有CREATE、ALTER、DROP等,DDL主要是用在定義或改變表(TABLE)的結構,資料型別,表之間的連結和約束等初始化工作上,他們大多在建立表時使用
DCL:資料控制語言,用來授予或回收訪問資料庫的某種特權,並控制資料庫操縱事務發生的時間及效果,對資料庫實行監視等
DML(Data Manipulation Language,資料操作語言):用於檢索或者修改資料。
DML包括: SELECT:用於檢索資料; INSERT:用於增加資料到資料庫; UPDATE:用於從資料庫中修改現存的資料 DELETE:用於從資料庫中刪除資料。
DDL(Data Definition Language,資料定義語言): 用於定義資料的結構,比如 建立、修改或者刪除資料庫物件。 DDL包括:DDL語句可以用於建立使用者和重建資料庫物件。下面是DDL命令: CREATE TABLE:建立表 ALTER TABLE DROP TABLE:刪除表 CREATE INDEX DROP INDEX
DCL(Data Control Language,資料控制語言):用於定義資料庫使用者的許可權。
DCL包括: ALTER PASSWORD GRANT REVOKE CREATE SYNONYM
一:DCL(資料控制語言) 1、建立使用者test2,密碼也是test2(記得最有以分;號結束): create user test2 identified by test2;
2、給test2授權:create session;(允許使用者登陸Oracle): grant create session to test2;
3、給test2分配建立表的許可權; grant create table to test2;
4、給test2分配表空間的使用許可權; grant unlimited tablespace to test2;
二:DDL(資料定義語言練習) 1、建立表:SQL> create table t_user( userid number(30) not null primary key, username varchar(20) not null, age number(3), sex varchar(2), departid number(30) not null, foreign key(departid) references t_depart(departid) ); ★alter table student add primary key(userid)這樣用alter也可以建立關聯主外來鍵。
2、刪除表:SQL> drop table t_depart; 3、建立序列: create sequence seq_a minvalue 1000 maxvalue 99999999 start with 1000 increment by 1 nocache;
三:DML(資料操作語言): 1、insert插入SQL: (1)insert into t_depart (departid,departname,createdate) values (1,'市場部',sysdate); (2)insert into t_user values (seq_user.nextval,'馬文濤',23,'男');
2、delete刪除SQL: (1)delete t_user;(太可怕了,如果在刪除時不加條件,則把此表中的所有資料都會刪除!) (2)delete t_user where userid = 3;
3、update更新SQL: (1) update t_user set username = '爭偉',sex = '男'; (太恐怖了,更新時不加條件表中所有行記錄的姓名都被修改了!) (2)update t_user set username = '文濤' where userid = 7; ★小結:我發現在增、刪、改的SQL語句中用不到from關鍵字。 4、select查詢SQL:格式——>select···from···where···group by···having···order by···; (1)查詢所有的使用者:select * from t_user; ★在oracle中這裡的表明用別名時不能加as關鍵字 如: select * from t_user u;正確 select * from t_user as u;錯誤
(2)查詢指定的列: select username,sex from t_user;
(3)as給列以別名顯示:select username as 使用者名稱 from t_user;(這裡的as關鍵字可以省略)
(4)distinct去掉重複的行:select distinct username from t_user;
(5)使用運算子:select age+10 from t_user;(給每個人的年齡都加10歲)
(6)連線字串: select '使用者名稱:' || username from t_user;(Oracle用||做連線字串操作符)
(7)where子句:select * from t_user where username = '寶寶';
(8)between and在···之間: select * from t_user where userid between 9 and 10;這也等價於下面這條SQL: select * from t_user where userid >=9 and userid <=10;
(9)in匹配集合中的任意值:select * from t_user where username in('馬文濤','寶寶');
(10)like模糊查詢:%匹配0個或多個任意字串,_匹配1個任意字串。
select * from t_user where username like '%濤%';
(11)null判斷某列為空:select * from t_user where sex is null; (這裡用is,不能用=,如果要返回不為null的記錄就可以用is not null) (12)order by排序:ASC: 升序排列(可以省略),DESC: 降序排列 升序:select u.userid,u.username from t_user u order by u.userid; 降序:select u.userid,u.username from t_user u order by u.userid desc;
(13)系統函式(對一組資料進行處理,返回一個值): AVG–求平均值,COUNT–統計記錄數,MAX–最大值,MIN–最小值,SUM–求和 <1>返回最小和最大的使用者編號: select min(userid),max(userid) from t_user; <2>返回總記錄數: select count(*) from t_user; <3>返回某個欄位不為空的記錄數: select count(sex) from t_user; <4>返回不為空且不重複的記錄數: select count(distinct sex) from t_user;
(14)group by分組(分組了就不能直接返回*,經常和聚合函式count(age)一起使用): <1>按姓名分組,並統計每組人數:select count(*),username from t_user group by username; <2>根據多個欄位分組: select username,age,count(*) from t_user group by username,age; ◆group by有一個原則,就是 select 後面的所有列中,沒有使用聚合函式的列,必須出現在 group by 後面。
(15)having過濾分組:select username from t_user group by username having count(*) > 2;
(16)子查詢(子查詢自身只能返回一個單獨的值): <1>子查詢放在select後面,作為其中的一個欄位返回。 select u.username,(select d.departname from t_depart d where d.departid = u.departid) from t_user u; (返回使用者和所屬的部門,這中子查詢理論上先執行外面的查詢,只是我的感覺哦,嘿嘿!) <2>子查詢放在from後面,作為一張臨時表。 select * from (select username,sex s from t_user where departid=1) where s = '男'; (這種子查詢應該先執行裡面) <3>子查詢放在where後面,作為條件的一部分。 select * from t_user where departid = (select departid from t_depart where departname = '財務部'); (這種子查詢也應該先執行裡面)
(17)聯合查詢(當n張表連線時, 需要n-1個連線條件): <1>等值連線(內連線):select u.username,d.departname from t_user u,t_depart d where u.departid = d.departid; <2>外連線:即把不滿足條件的記錄也返回,用個+就行了, (+)操作符在哪邊就代表另外一邊不滿足聯合條件的記錄可以被輸出。這個感覺不太常用。 select b.book_id,b.book_name from book_info as b,book_click_num as c where b.book_id = c.book_id(+);
四、觸發器trigger:當特定事件發生時自動執行的程式碼塊。 這些事件包括: (1)DML語句(INSERT,UPDATE,DELETE):before在DML語句被執行前觸發操作,after在DML語句被執行後觸發操作。 (2)DDL語句(CREATE 及 ALTER) (3)系統事件,例如啟動/關閉[startup/shutdown]、錯誤[error] (4)使用者事件, 例如登入/退出[logon/logoff] ★兩個特殊的變數——>:new新的記錄值,:old保留原來的記錄值 簡單示例:create or replace trigger update_depart_trigger after update on t_depart for each row begin update t_user u set u.departid = :new.departid where u.departid = :old.departid; end update_depart_trigger; 此示例的作用:當部門的id改變後,由於使用者中有外來鍵引用了部門,所以使用者的departid這個外來鍵自動執行更新操作。 (這個觸發器很奇怪,在黑視窗用sqlplus就不能建立,用plsql Developer工具就可以建立,很有意思,嘿嘿!)
五、遊標cursor:以迴圈取SQL語句的SELECT內容,它是存放結果集的資料物件,使用遊標,我們只能逐條記錄地得到查詢結果。 作用:查詢資料庫,獲取記錄集合(結果集)的指標,可以讓開發者一次訪問一行結果集,在每條結果集上作操作。 使用:用遊標有四種基本的步驟:宣告遊標(declare)、開啟遊標(open)、提取資料(fetch)、關閉遊標(close)。 ★當你要往每一行插入一個數據只能用遊標,或者更新結果集中的每行記錄時也可用遊標,用儲存過程返回一個結果集。 ★觸發器和儲存過程會和資料庫繫結,即一直儲存在資料庫中,而遊標不會,它是任意時刻建立再開啟再執行再關閉, 與資料庫沒有任何直接關係。 ★在Oracle中,不需要顯示銷燬遊標.因為在Oracle中,很多東西是由JAVA寫的.Oracle會自動銷燬遊標。
簡單示例: -- 定義一個遊標 declare cursor cursor_user is select username,age from t_user; //變數的定義也可以放到遊標定義上面 a t_user.username%type;//定義個a變數,型別是t_user表中username列的型別。 b t_user.age%type;//同上 begin//SQL中可執行程式碼都在begin和end之間。 -- 開啟遊標 open cursor_user; -- 遍歷遊標 loop//迴圈抓取資料(loop是其中一種迴圈方式) fetch cursor_user into a,b; -- 將一行記錄放入到變數中 dbms_output.put_line(a || ' ' || b);列印到輸出控制檯 exit when cursor_user%notfound; -- 當沒有記錄時退出迴圈 end loop;//退出迴圈 -- 關閉遊標 close cursor_user; end;
加if條件判斷遊標示例: declare cursor cursor_user is select username,age from t_user; a t_user.username%type; b t_user.age%type; begin open cursor_user; loop fetch cursor_user into a,b; if a='寶寶'and b=43 then dbms_output.put_line(a || ' ' || b); end if; exit when cursor_user%notfound; end loop; close cursor_user; end;
六、儲存過程procedure 概念:其實就是一組存放在資料庫中SQL語句,普通SQL操作都在專案中寫死的,而他只跟資料庫進行繫結。 更準確的說儲存過程是資料庫伺服器端的一段程式,它有兩種型別。一種類似於SELECT查詢,用於檢索資料,檢索到的資料能夠以資料集的形式返回給客戶(oracle儲存過程本身沒返回值,只是用out引數代替)。另一種類似於INSERT或DELETE查詢,它不返回資料,只是執行一個動作。有的伺服器允許同一個儲存過程既可以返回資料又可以執行動作。
優點: 1、提高效率。儲存過程本身的執行速度非常快,而且,呼叫儲存過程可以大大減少同資料庫的互動次數。 2、提高安全性。假如將SQL語句混合在JSP程式碼中,程式碼外漏以後,也就意味著庫結構外漏。 3、有利於SQL語句的重用。 ★oracle函式有返回值,但儲存過程沒有返回值,它的所有返回值都是通過out引數來替代的。 ★什麼時候需要用儲存過程 如果伺服器定義了儲存過程,應當根據需要決定是否要用儲存過程。儲存過程通常是一些經常要執行的任務,這些任務往往是針對大量的記錄而進行的。在伺服器上執行儲存過程,可以改善應用程式的效能。這是因為: .伺服器往往具有強大的計算能力和速度。 .避免把大量的資料下載到客戶端,減少網路上的傳輸量。 例如,假設一個應用程式需要計算一個數據,這個資料需要涉及到許多記錄。如果不使用儲存過程的話,把這些資料下載到客戶端,導致網路上的流量劇增。 不僅如此,客戶端可能是一臺老掉牙的計算機,它的運算速度很慢。而改用儲存過程後,伺服器會很快地把資料計算出來,並且只需傳遞一個數據給客戶端,其效率之高是非常明顯的。
★儲存過程的引數 要執行伺服器上的儲存過程,往往要傳遞一些引數。這些引數分為四種類型: 第一種稱為輸入引數(in),由客戶程式向儲存過程傳遞值。 第二種稱為輸出引數(out),由儲存過程向客戶程式返回結果。 第三種稱為輸入/輸出引數(in out),既可以由客戶程式向儲存過程傳遞值,也可以由儲存過程向客戶程式返回結果。 第四種稱為狀態引數,由儲存過程向客戶程式返回錯誤資訊。 要說明的是,並不是所有的伺服器都支援上述四種類型的引數,例如,InterBase就不支援狀態引數。
簡單示例: 1、無返回值儲存過程(插入一個使用者) create or replace procedure saveuser ( username in varchar2 , age in number, departid in number //定義兩個輸入引數,引數型別可以是自己寫死,也可以用某個列的型別 比如:username t_user.username%type; ) as begin insert into t_user (userid,username,age,departid) values (seq_user.nextval,username, age,departid); end saveuser; (1)在sql中執行儲存過程call saveuser('文濤',23,1);,也可以用execute和exec。 (2)在java裡呼叫時就用下面的程式碼: package com.hyq.src; import java.sql.*; import java.sql.ResultSet; public class TestProcedureOne { public TestProcedureOne() { } public static void main(String[] args ){ String driver = "oracle.jdbc.driver.OracleDriver"; String strUrl = "jdbc:oracle:thin:@127.0.0.1:1521:ORCLA"; Statement stmt = null; ResultSet rs = null; Connection conn = null; CallableStatement cstmt = null; try { Class.forName(driver); conn = DriverManager.getConnection(strUrl, " hyq ", " hyq "); CallableStatement proc = null; proc = conn.prepareCall("{ call saveuser(?,?,?) }"); proc.setString(1, "馬文濤"); proc.setInt(2,23); proc.setInt(3,1); proc.execute(); } catch (SQLException ex2) { ex2.printStackTrace(); } catch (Exception ex2) { ex2.printStackTrace(); } finally{ try { if(rs != null){ rs.close(); if(stmt!=null){ stmt.close(); } if(conn!=null){ conn.close(); } } } catch (SQLException ex1) { } } } }
2、有返回值的儲存過程(oracle儲存過程本身沒返回值)只是用out引數代替(非列表) //獲得某部門下的所有使用者姓名和年齡 create or replace procedure pro_getUserByDepart//oracle中不區分大小寫,這裡只是為了自己方便。 ( i_departid in t_user.departid%type, o_username out t_user.username%type, o_age out t_user.age%type )as begin select username,age into o_username,o_age from t_user where departid = i_departid; end pro_getUserByDepart;
★在pl/sql Developer中右擊儲存過程選擇測試就可以在下面輸入值直接看列印結果了。 ★由於這個儲存過程會返回行記錄集,所有在Developer中為了測試成功就在此過程中加入rownum=1條件判斷, 即:select username,age into o_username,o_age from t_user where departid = i_departid and rownum=1; 如果不加那個rownum=1(當然你也可以讓它等於2),多行記錄會報“實際返回的行數超出請求的行數”這個異常。
★其實我們用上面這種儲存過程一般都只返回一行記錄集,如果是返回多行那就要用到要用包pagkage和遊標cursor了。
★在利用select...into...語法時(把查詢的結果放入輸出引數中),必須先確保資料庫中有該條記錄,否則會報出"no data found"異常。
3、返回列表(必須要用包了,package裡包含了遊標),分兩個步驟: ★為什麼要在儲存過程中用到遊標時,要把此遊標封裝到一個包中呢,個人理解:由於遊標是機開機關的一個 物件,它無法被儲存過程呼叫,而包中恰恰能封裝遊標、函式等這些物件,所有就把遊標放在一個包中。 (1)1, 建一個程式包。如下: create or replace package userpackage as type usercursor is ref cursor; end userpackage;
(2)建立儲存過程,儲存過程為: create or replace procedure pro_getalluser ( pro_cursor out userpackage.usercursor )is begin open pro_cursor for select * from t_user; end pro_getalluser; 可以看到,它是把遊標(可以理解為一個指標),作為一個out 引數來返回值的。
★在java裡呼叫時就用下面的程式碼(列出主要程式碼): package com.mwt.test;
import java.sql.CallableStatement; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet;
import oracle.jdbc.OracleTypes;
public class TestProcedure { public static void main(String...args){ try{ Class.forName("oracle.jdbc.driver.OracleDriver"); String url = "jdbc:oracle:thin:@127.0.0.1:1521:ORCL9I"; String user = "test2"; String password = "test2"; Connection con = DriverManager.getConnection(url, user, password); String sql = "{call pro_getalluser(?)}"; CallableStatement cs = con.prepareCall(sql); cs.registerOutParameter(1, OracleTypes.CURSOR); cs.execute(); //返回第一個儲存過程的輸出引數,我只返回了一個遊標也就只有一個輸出引數,即就是獲得第一個返輸出引數getObject(1); ResultSet rs = (ResultSet)cs.getObject(1); while(rs.next()){ System.out.println(rs.getString(1)+"----"+rs.getString(2)+"----"+rs.getString(3)); } rs.close(); cs.close(); con.close(); }catch(Exception e){ e.printStackTrace(); } } }