一. 定義
存儲過程 Procedure 是一組爲了完成特定功能的 SQL 語句集合,經編譯後存儲在數據庫中,用戶通過指定存儲過程的名稱並給出參數來執行。存儲過程中可以包含邏輯控制語句和數據操縱語句,它可以接受參數、輸出參數、返回單個或多個結果集以及返回值。
以上是官方的定義。簡單來說,如果把sql當成是代碼,其實存儲過程就相當於函數。把一組具備特定功能的sql語句封裝成起來而已。所以他的本質是爲了更好的執行對數據庫的操作。那麼好在哪裏呢?
二. 存儲過程的優勢
1. 性能。存儲過程在創建時就已經在數據庫服務器中編譯好並存儲起來,調用時只需提供過程名和參數,就可以直接使用。而sql語句,沒執行一句就要編譯一次,這在sql語句發送並不頻繁的情況下還好,但是如果短時間大量發送sql語句的情況下,不僅會降低網絡性能也會增加數據庫負擔。
2. 可完成更復雜的數據庫控制。由於存儲過程中可以包含邏輯控制語句和數據操縱語句,類似遍歷、if這種邏輯可以直接卸載過程中。
3.我在程序中寫了一段代碼,發送一千條sql插入語句到本地數據庫,大概花了6672ms,而在存儲過程中只花了4689ms。以下分別是代碼和存儲過程sql:
@Autowired
private JdbcTemplate jdbcTemplate;
public static final String INSERT_SQL="insert into user (id, name, age) values(?,?,?)";
public void insert(){
long begin = System.currentTimeMillis();
for (int i = 600000; i < 601000; i++) {
jdbcTemplate.update(INSERT_SQL,i,"林"+i,10+Math.random()*10);
}
long end = System.currentTimeMillis();
System.out.println(end-begin);
}
CREATE PROCEDURE test_insert2 ()
BEGIN
DECLARE i INT DEFAULT 601000;
WHILE i<602000
DO
insert into `user` (id,NAME,age) values (i,CONCAT('林',i),18);
SET i=i+1;
END WHILE;
COMMIT;
END;
CALL test_insert2()
三. 存儲過程基本語法和變量講解
1. delimiter // 該語句指定MySQL把“//”當作分隔符。也就是說,如果你在MySQL client中輸入這句,則“;”符號不再被當作結束符。
2. create procedure getAllUser() 表示創建存儲過程,其名爲getAllUser()。
3. call getAllUser ; 調用存儲過程getAllUser。
4. drop procedure getAllUser; 刪除存儲過程。
5. begin...end 之間編寫過程體,即寫sql語句和邏輯代碼。
6. 存儲過程根據需要可能會有輸入、輸出、輸入輸出參數,如果有多個參數用","分割開。MySQL存儲過程的參數用在存儲過程的定義,共有三種參數類型,IN,OUT,INOUT:
- IN參數的值必須在調用存儲過程時指定,在存儲過程中修改該參數的值不能被返回,爲默認值
- OUT:該值可在存儲過程內部被改變,並可返回
- INOUT:調用時指定,並且可被改變和返回
四. 分別在MySQL client中和Navicat中創建存儲過程
- 在client創建一個查詢所有user表記錄的過程。
最後輸入 還原分隔符爲 “;”
調用過程
刪除過程
- 在Navicat中創建一個插入user表數據的存儲過程 ,並且傳遞兩個參數
右鍵新建函數,選擇過程
在這裏寫邏輯語句吧,完整如下:
BEGIN
DECLARE i INT DEFAULT initValue;
WHILE i<=endValue
DO
insert into `user` (id,NAME,age) values (i,CONCAT('林',i),18);
SET i=i+1;
END WHILE;
COMMIT;
END
這裏的意思是 把第一個參數賦給變量i,當i小於第二個參數值時,一直進行while循環,每次循環i+1。
Navicat裏面點擊sql預覽可以看到完整的代碼:
按下ctrl+s保存:
然後運行,輸入兩個參數,逗號隔開:
就可以插入數據了。
五. 在java程序中調用存儲過程
@Autowired
private DataSource dataSource;
public List<User> getUser() {
List<User> users = new ArrayList<User>(10000);
User user = new User();
try {
CallableStatement callableStatement = dataSource.getConnection().prepareCall("{call getUser}");
ResultSet rs = null; //執行查詢操作,並獲取結果集
rs = callableStatement.executeQuery();
while (rs.next()){
user.setName(rs.getString("name"));
user.setAge(rs.getInt("age"));
user.setId(rs.getInt("id"));
users.add(user);
}
} catch (SQLException e) {
e.printStackTrace();
}
return users;
}
調用一個getUser()的存儲過程,並將結果封裝成實體。主要是 CallableStatement callableStatement = dataSource.getConnection().prepareCall("{call getUser}");這句代碼。這裏我認爲應該有更成熟的調用方式纔對,但是我也懶得找資料了。反正原理都一樣。
六. 自定義函數
mysql的自定義函數其實就是我們自己去定義類似sum(...)、max(...)這種函數,很簡單,創建起來跟存儲過程差不多,就不多做講解了。
create function sum1(x int,y int) returns int(20)
begin
//編寫函數體
return xxx ;
end ;
使用 select sum1(215,555)
刪除 drop sum1