1. 程式人生 > >SQL Server 儲存過程詳解

SQL Server 儲存過程詳解

一. 什麼是儲存過程

系統儲存過程是系統建立的儲存過程,目的在於能夠方便的從系統表中查詢資訊或完成與更新資料庫表相關的管理任務或其他的系統管理任務。系統儲存過程主要儲存在master資料庫中,以“sp”下劃線開頭的儲存過程。儘管這些系統儲存過程在master資料庫中,但我們在其他資料庫還是可以呼叫系統儲存過程。有一些系統儲存過程會在建立新的資料庫的時候被自動建立在當前資料庫中。

二. 儲存過程執行流程

這裡寫圖片描述

儲存過程是由一些SQL語句和控制語句組成的被封裝起來的過程,它駐留在資料庫中,可以被客戶應用程式呼叫,也可以從另一個過程或觸發器呼叫。它的引數可以被傳遞和返回。與應用程式中的函式過程類似,儲存過程可以通過名字來呼叫,而且它們同樣有輸入引數和輸出引數。

根據返回值型別的不同,我們可以將儲存過程分為三類:

  • 返回記錄集的儲存過程的執行結果是一個記錄集,典型的例子是從資料庫中檢索出符合某一個或幾個條件的記錄;
  • 返回數值的儲存過程執行完以後返回一個值,例如在資料庫中執行一個有返回值的函式或命令;
  • 行為儲存過程僅僅是用來實現資料庫的某個功能,而沒有返回值,例如在資料庫中的更新和刪除操作。

個人認為,儲存過程說白了就是一堆 SQL 的合併。中間加了點邏輯控制。

  1. 但是儲存過程處理比較複雜的業務時比較實用。比如說,

一個複雜的資料操作。如果你在前臺處理的話。可能會涉及到多次資料庫連線。但如果你用儲存過程的話。就只有一次。從響應時間上來說有優勢。

  1. 也就是說儲存過程可以給我們帶來執行效率提高的好處。

另外,程式容易出現 BUG 不穩定,而儲存過程,只要資料庫不出現問題,基本上是不會出現什麼問題的。也就是說從安全上講,使用了儲存過程的系統更加穩定。

那麼問題來了,什麼時候才可以用儲存?對於資料量不是很大以及業務處理不是很複雜的小專案就無需要了麼?

答:錯。儲存過程不僅僅適用於大型專案,對於中小型專案,使用儲存過程也是非常有必要的。其威力和優勢主要體現在:

  1. 儲存過程只在創造時進行編譯,以後每次執行儲存過程都不需再重新編譯,而一般 SQL 語句每執行一次就編譯一次,所以使用儲存過程可提高資料庫執行速度。
  2. 當對資料庫進行復雜操作時(如對多個表進行 Update,Insert,Query,Delete 時),可將此複雜操作用儲存過程封裝起來與資料庫提供的事務處理結合一起使用。這些操作,如果用程式來完成,就變成了一條條的 SQL 語句,可能要多次連線資料庫。而換成儲存,只需要連線一次資料庫就可以了。
  3. 儲存過程可以重複使用,可減少資料庫開發人員的工作量。
  4. 安全性高,可設定只有某此使用者才具有對指定儲存過程的使用權。
  5. 減少網路通訊量。呼叫一個行數不多的儲存過程與直接呼叫SQL語句的網路通訊量可能不會有很大的差別,可是如果儲存過程包含上百行SQL語句,那麼其效能絕對比一條一條的呼叫SQL語句要高得多。
  6. 執行速度更快。有兩個原因:首先,在儲存過程建立的時候,資料庫已經對其進行了一次解析和優化。其次,儲存過程一旦執行,在記憶體中就會保留一份這個儲存過程,這樣下次再執行同樣的儲存過程時,可以從記憶體中直接呼叫。
  7. 更強的適應性:由於儲存過程對資料庫的訪問是通過儲存過程來進行的,因此資料庫開發人員可以在不改動儲存過程介面的情況下對資料庫進行任何改動,而這些改動不會對應用程式造成影響。
  8. 布式工作:應用程式和資料庫的編碼工作可以分別獨立進行,而不會相互壓制。

儲存過程的使用,好像一直是一個爭論。

我不傾向於儘可能使用儲存過程,是這麼認為的:

  1. 執行速度: 大多數高階的資料庫系統都有statement cache的,所以編譯sql的花費沒什麼影響。但是執行儲存過程要比直接執行sql花費更多(檢查許可權等),所以對於很簡單的sql,儲存過程沒有什麼優勢。
  2. 網路負荷:如果在儲存過程中沒有多次資料互動,那麼實際上網路傳輸量和直接sql是一樣的。
  3. 團隊開發:很遺憾,比起成熟的IDE,沒有什麼很好儲存過程的IDE工具來支援,也就是說,這些必須手工完成。
  4. 安全機制:對於傳統的C/S結構,連線資料庫的使用者可以不同,所以安全機制有用;但是在web的三層架構中,資料庫使用者不是給使用者用的,所以基本上,只有一個使用者,擁有所有許可權(最多還有一個開發使用者)。這個時候,安全機制有點多餘。
  5. 使用者滿意:實際上這個只是要將訪問資料庫的介面統一,是用儲存過程,還是EJB,沒太大關係,也就是說,在三層結構中,單獨設計出一個數據訪問層,同樣能實現這個目標。
  6. 開發除錯:一樣由於IDE的問題,儲存過程的開發除錯要比一般程式困難(老版本DB2還只能用C寫儲存過程,更是一個災難)。
  7. 移植性:算了,這個不用提,反正一般的應用總是繫結某個資料庫的,不然就無法靠優化資料庫訪問來提高效能了。
  8. 維護性:的確,儲存過程有些時候比程式容易維護,這是因為可以實時更新DB端的儲存過程,但是在3層結構下,更新server端的資料訪問層一樣能實現這個目標,可惜現在很多平臺不支援實時更新而已。

常用系統儲存過程有:

exec sp_databases; --檢視資料庫
exec sp_tables;        --查看錶
exec sp_columns student;--檢視列
exec sp_helpIndex student;--檢視索引
exec sp_helpConstraint student;--約束
exec sp_stored_procedures;
exec sp_helptext 'sp_stored_procedures';--檢視儲存過程建立、定義語句
exec sp_rename student, stuInfo;--修改表、索引、列的名稱
exec sp_renamedb myTempDB, myDB;--更改資料庫名稱
exec sp_defaultdb 'master', 'myDB';--更改登入名的預設資料庫
exec sp_helpdb;--資料庫幫助,查詢資料庫資訊
exec sp_helpdb master;

系統儲存過程示例:

--表重新命名
exec sp_rename 'stu', 'stud';
select * from stud;
--列重新命名
exec sp_rename 'stud.name', 'sName', 'column';
exec sp_help 'stud';
--重新命名索引
exec sp_rename N'student.idx_cid', N'idx_cidd', N'index';
exec sp_help 'student';

--查詢所有儲存過程
select * from sys.objects where type = 'P';
select * from sys.objects where type_desc like '%pro%' and name like 'sp%';

使用者自定義儲存過程

create proc | procedure pro_name
    [{@引數資料型別} [=預設值] [output],
     {@引數資料型別} [=預設值] [output],
     ....
    ]
as
    SQL_statements

2、 建立不帶引數儲存過程

--建立儲存過程
if (exists (select * from sys.objects where name = 'proc_get_student'))
    drop proc proc_get_student
go
create proc proc_get_student
as
    select * from student;

--呼叫、執行儲存過程
exec proc_get_student;

3、 修改儲存過程

--修改儲存過程
alter proc proc_get_student
as
select * from student;

4、 帶參儲存過程

--帶參儲存過程
if (object_id('proc_find_stu', 'P') is not null)
    drop proc proc_find_stu
go
create proc proc_find_stu(@startId int, @endId int)
as
    select * from student where id between @startId and @endId
go

exec proc_find_stu 2, 4;

5、 帶萬用字元引數儲存過程

--帶萬用字元引數儲存過程
if (object_id('proc_findStudentByName', 'P') is not null)
    drop proc proc_findStudentByName
go
create proc proc_findStudentByName(@name varchar(20) = '%j%', @nextName varchar(20) = '%')
as
    select * from student where name like @name and name like @nextName;
go

exec proc_findStudentByName;
exec proc_findStudentByName '%o%', 't%';

6、 帶輸出引數儲存過程

if (object_id('proc_getStudentRecord', 'P') is not null)
    drop proc proc_getStudentRecord
go
create proc proc_getStudentRecord(
    @id int, --預設輸入引數
    @name varchar(20) out, --輸出引數
    @age varchar(20) output--輸入輸出引數
)
as
    select @name = name, @age = age  from student where id = @id and sex = @age;
go

-- 
declare @id int,
        @name varchar(20),
        @temp varchar(20);
set @id = 7; 
set @temp = 1;
exec proc_getStudentRecord @id, @name out, @temp output;
select @name, @temp;
print @name + '#' + @temp;

7、 不快取儲存過程

--WITH RECOMPILE 不快取
if (object_id('proc_temp', 'P') is not null)
    drop proc proc_temp
go
create proc proc_temp
with recompile
as
    select * from student;
go

exec proc_temp;

8、 加密儲存過程

--加密WITH ENCRYPTION 
if (object_id('proc_temp_encryption', 'P') is not null)
    drop proc proc_temp_encryption
go
create proc proc_temp_encryption
with encryption
as
    select * from student;
go

exec proc_temp_encryption;
exec sp_helptext 'proc_temp';
exec sp_helptext 'proc_temp_encryption';

9、 帶遊標引數儲存過程

if (object_id('proc_cursor', 'P') is not null)
    drop proc proc_cursor
go
create proc proc_cursor
    @cur cursor varying output
as
    set @cur = cursor forward_only static for
    select id, name, age from student;
    open @cur;
go
--呼叫
declare @exec_cur cursor;
declare @id int,
        @name varchar(20),
        @age int;
exec proc_cursor @cur = @exec_cur output;--呼叫儲存過程
fetch next from @exec_cur into @id, @name, @age;
while (@@fetch_status = 0)
begin
    fetch next from @exec_cur into @id, @name, @age;
    print 'id: ' + convert(varchar, @id) + ', name: ' + @name + ', age: ' + convert(char, @age);
end
close @exec_cur;
deallocate @exec_cur;--刪除遊標

10、 分頁儲存過程

---儲存過程、row_number完成分頁
if (object_id('pro_page', 'P') is not null)
    drop proc proc_cursor
go
create proc pro_page
    @startIndex int,
    @endIndex int
as
    select count(*) from product
;    
    select * from (
        select row_number() over(order by pid) as rowId, * from product 
    ) temp
    where temp.rowId between @startIndex and @endIndex
go
--drop proc pro_page
exec pro_page 1, 4
--
--分頁儲存過程
if (object_id('pro_page', 'P') is not null)
    drop proc pro_stu
go
create procedure pro_stu(
    @pageIndex int,
    @pageSize int
)
as
    declare @startRow int, @endRow int
    set @startRow = (@pageIndex - 1) * @pageSize +1
    set @endRow = @startRow + @pageSize -1
    select * from (
        select *, row_number() over (order by id asc) as number from student 
    ) t
    where t.number between @startRow and @endRow;

exec pro_stu 2, 2;

Raiserror返回使用者定義的錯誤資訊,可以指定嚴重級別,設定系統變數記錄所發生的錯誤。

Raiserror({msg_id | msg_str | @local_variable}
  {, severity, state}
  [,argument[,…n]]
  [with option[,…n]]
)

# msg_id:在sysmessages系統表中指定的使用者定義錯誤資訊

# msg_str:使用者定義的資訊,資訊最大長度在2047個字元。

# severity:使用者定義與該訊息關聯的嚴重級別。當使用msg_id引發使用sp_addmessage建立的使用者定義訊息時,raiserror上指定嚴重性將覆蓋sp_addmessage中定義的嚴重性。

任何使用者可以指定0-18直接的嚴重級別。只有sysadmin固定伺服器角色常用或具有alter trace許可權的使用者才能指定19-25直接的嚴重級別。19-25之間的安全級別需要使用with log選項。

# state:介於1至127直接的任何整數。State預設值是1。

raiserror('is error', 16, 1);
select * from sys.messages;
--使用sysmessages中定義的訊息
raiserror(33003, 16, 1);
raiserror(33006, 16, 1);

相關推薦

SQL Server 儲存過程

一. 什麼是儲存過程 系統儲存過程是系統建立的儲存過程,目的在於能夠方便的從系統表中查詢資訊或完成與更新資料庫表相關的管理任務或其他的系統管理任務。系統儲存過程主要儲存在master資料庫中,以“sp”下劃線開頭的儲存過程。儘管這些系統儲存過程在maste

【PL/SQL儲存過程

什麼是儲存過程 儲存過程是一種命名的PL/SQL程式塊,既可以沒有引數也可以有若干個輸入,輸出引數,但是它通常沒有返回值。儲存過程被儲存在資料庫中,可以被SQL語句直接呼叫,只能通過EXECUT命令或者在PL/SQL程式塊內部被呼叫。由於儲存過程是已經編譯好的程式碼,因此被呼叫或者引用時,執行效

Java呼叫SQL Server儲存過程

                本文較長,包含了如下幾部分                    1使用不帶引數的儲存過程     使用 JDBC 驅動程式呼叫不帶引數的儲存過程時,必須使用 call SQL 轉義序列。不帶引數的 call 轉義序列的語法如下所示: {call procedure-name}

SQL語句執行過程

使用 錯誤信息 意思 排錯 表達 對象 data 才會 結果集   一、SQL語句執行原 第一步:客戶端把語句發給服務器端執行當我們在客戶端執行 select 語句時,客戶端會把這條 SQL 語句發送給服務器端,讓服務器端的進程來處理這語句。也就是說,Oracl

SQL server 儲存過程的編寫

USE [資料庫名稱] GO /****** Object:  StoredProcedure [dbo].[insertbGait]    Script Date: 2018/7/17 15:51:22 ******/ SET ANSI_NULLS ON GO

SQL SERVER儲存過程加密和安全上下文

對SQL Server 2008的安全入門略作小結,以作備忘。本文涉及兩個應用:儲存過程加密和安全上下文。 <一>儲存過程加密 SQL server,我已經成了儲存過程的忠實擁躉。在直接使用SQL語句還是儲存過程來處理業務邏輯時,我基本會毫不猶豫地選擇後者。 理由如下:

IDEA , 用JDBC驅動連線SQL Server伺服器異常

本文適用於IDEA,sqlserver伺服器的使用者。 今天研究JDBC連線伺服器,一直琢磨了一晚上,才解決這個問題。 這類問題網上也有很多類似的部落格,但是不一定有用(本人親測),畢竟程式版本不同可能適用性就不同。 剛開始每次執行程式碼都是這樣的問題: 這是在資料庫連線成

SQL Server 儲存過程返回值的幾種方式

獲得儲存過程的返回值–通過查詢分析器獲得 (1)不帶任何引數的儲存過程(儲存過程語句中含有return) —建立儲存過程 CREATE PROCEDURE testReturn AS return 145 GO —執行儲存過程 DECLARE @RC int exec @RC=

Yii2.0呼叫sql server儲存過程並獲取返回值

1、首先展示建立sql server儲存過程的語句,建立一個簡單的儲存過程,測試用。 1 SET ANSI_NULLS ON 2 GO 3 SET QUOTED_IDENTIFIER ON 4 GO 5 6 CREATE PROCEDURE [dbo].[register_info]

win10 SQL SERVER 2017安裝

目錄 SQL Server 2017安裝 SSMS安裝 寫在之前 安裝的sql程式包是cn_sql_server_2017_developer_x64_dvd_11296175 在安裝之前最好是安裝了iis和關閉防火牆(如果沒有關閉,警告忽略即可)  

SQL Server儲存過程的使用

   儲存過程概述   簡單來說,儲存過程就是一條或者多條sql語句的集合,可視為批處理檔案,但是其作用不僅限於批處理。  基本概念:   SQL Server中的儲存過程是使用T_SQL編寫的程式碼段。它的目的在於能夠方便的從系統表中查詢資訊,   或者完成與更新資料

資料庫儲存過程 儲存過程

轉自張龍豪原文儲存過程詳解 儲存過程(Procedure)可以說是一個記錄集吧,它是由一些T-SQL語句組成的程式碼塊,這些T-SQL語句程式碼像一個方法一樣實現一些功能(對單表或多表的增刪改查),然後再給這個程式碼塊取一個名字,在用到這個功能的時候呼叫他就行了。 儲存過程的好處:1.由於資料庫執行動作時

MySQL檢視和儲存過程

一 mysql檢視 1 檢視的目的: 讓同一個資料庫被訪問時,對於不同的登入帳號,顯示不同的資料資訊 2 檢視的優點: 1)資料獨立: 一旦檢視結構確定,可以遮蔽表結構對使用者的影響 2)安全: 使用者只能看到檢視中的資料 3)簡單: 使用者不需要關心檢視中的資料如何查詢獲得,檢視中的資

mongo 儲存過程

儲存過程 關係型資料庫的儲存過程描述為: 一組為了完成特定功能的SQL 語句集,經編譯後儲存在資料庫中,使用者通過指定儲存過程的名字並給出引數(如果該儲存過程帶有引數)來執行它 。 MongoDB 為很多問題提供了一系列的解決方案,針對於其它資料庫的特性,它仍然毫不示 弱,表現的非比尋常。MongoDB

SQL Server 儲存過程返回值彙總

獲得儲存過程的返回值–通過查詢分析器獲得 (1)不帶任何引數的儲存過程(儲存過程語句中含有return) —建立儲存過程 CREATE PROCEDURE testReturn AS return 145 GO —執行儲存過程 DECLARE @RC in

撩課-Mysql第19部分儲存過程

學習地址: 撩課-JavaWeb系列1之基礎語法-前端基礎 撩課-JavaWeb系列2之XML 撩課-JavaWeb系列3之MySQL 撩課-JavaWeb系列4之JDBC 撩課-JavaWeb系列5之web伺服器-idea 什麼是儲存過程 一組可程式設計的函式, 是為了完成特

Sql Server 儲存過程 - 使用者操作例項

使用者的增刪改查 新增 --新增使用者 create procedure addDeviceUser @name nvarchar(36) , @phone nvarchar(36) , @password nvarchar(255) as begin if exists(

Sql server 儲存過程

-- ======================================= -- Author: <zengyongbin> -- Create date: <2018-11-11> -- Description: <新增> --

關於SQL SERVER 儲存過程使用(2)

  現在有一場景,需要調用出公司員工在本週的請假天數。程式碼如下(1),本人一開始寫的時候用遍歷裡面套著一個遍歷,發現效果出不來如下(2),求是否有什麼方法可以實現遍歷裡面還有個遍歷。 SQL程式碼(1): --建立儲存過程 create procedure LeaveT

SQL Server儲存過程中top後為變數時的處理

@pageRecordNum為每頁顯示的記錄數,@currentPageNum為當前的頁數 create proc pagination  @pageRecordNum int,@currentPageNum int as  declare @recNum varchar(