如何使用 SQL 存儲過程簡化複雜的操作

来源:https://www.cnblogs.com/vin-c/archive/2022/05/19/16288002.html
-Advertisement-
Play Games

本文介紹什麼是 SQL 存儲過程,為什麼要使用存儲過程,如何使用存儲過程,以及創建和使用存儲過程的基本語法。 一、存儲過程 迄今為止,我們使用的大多數 SQL 語句都是針對一個或多個表的單條語句。並非所有操作都這麼簡單,經常會有一些複雜的操作需要多條語句才能完成,例如以下的情形。 為了處理訂單,必須 ...


目錄

本文介紹什麼是 SQL 存儲過程,為什麼要使用存儲過程,如何使用存儲過程,以及創建和使用存儲過程的基本語法。

一、存儲過程

迄今為止,我們使用的大多數 SQL 語句都是針對一個或多個表的單條語句。並非所有操作都這麼簡單,經常會有一些複雜的操作需要多條語句才能完成,例如以下的情形。

  • 為了處理訂單,必須核對以保證庫存中有相應的物品。
  • 如果物品有庫存,需要預定,不再出售給別的人,並且減少物品數據以反映正確的庫存量。
  • 庫存中沒有的物品需要訂購,這需要與供應商進行某種交互。
  • 關於哪些物品入庫(並且可以立即發貨)和哪些物品退訂,需要通知相應的顧客。

這顯然不是一個完整的例子,它甚至超出了本文的範圍,但足以表達我們的意思了。執行這個處理需要針對許多表的多條 SQL 語句。

此外,需要執行的具體 SQL 語句及其次序也不是固定的,它們可能會根據物品是否在庫存中而變化。

那麼,怎樣編寫代碼呢?可以單獨編寫每條 SQL 語句,並根據結果有條件地執行其他語句。在每次需要這個處理時(以及每個需要它的應用中),都必須做這些工作。

可以創建存儲過程。簡單來說,存儲過程就是為以後使用而保存的一條或多條 SQL 語句。可將其視為批文件,雖然它們的作用不僅限於批處理。

說明:不適用於 SQLite

SQLite 不支持存儲過程。

說明:還有更多內容

存儲過程很複雜,全面介紹它需要很大篇幅。

市面上有專門講存儲過程的書。

本文不打算講解存儲過程的所有內容,只給出簡單介紹,讓讀者對它們的功能有所瞭解。

因此,這裡給出的例子只提供 Oracle 和 SQL Server 的語法。

二、為什麼要使用存儲過程

我們知道了什麼是存儲過程,那麼為什麼要使用它們呢?理由很多,下麵列出一些主要的。

  • 通過把處理封裝在一個易用的單元中,可以簡化複雜的操作(如前面例子所述)。
  • 由於不要求反覆建立一系列處理步驟,因而保證了數據的一致性。如果所有開發人員和應用程式都使用同一存儲過程,則所使用的代碼都是相同的。
  • 上一點的延伸就是防止錯誤。需要執行的步驟越多,出錯的可能性就越大。防止錯誤保證了數據的一致性。
  • 簡化對變動的管理。如果表名、列名或業務邏輯(或別的內容)有變化,那麼只需要更改存儲過程的代碼。使用它的人員甚至不需要知道這些變化。
  • 上一點的延伸就是安全性。通過存儲過程限制對基礎數據的訪問,減少了數據訛誤(無意識的或別的原因所導致的數據訛誤)的機會。
  • 因為存儲過程通常以編譯過的形式存儲,所以 DBMS 處理命令所需的工作量少,提高了性能。
  • 存在一些只能用在單個請求中的 SQL 元素和特性,存儲過程可以使用它們來編寫功能更強更靈活的代碼。

換句話說,使用存儲過程有三個主要的好處,即簡單、安全、高性能。顯然,它們都很重要。不過,在將 SQL 代碼轉換為存儲過程前,也必須知道它的一些缺陷。

  • 不同 DBMS 中的存儲過程語法有所不同。事實上,編寫真正的可移植存儲過程幾乎是不可能的。

    不過,存儲過程的自我調用(名字以及數據如何傳遞)可以相對保持可移植。

    因此,如果需要移植到別的 DBMS,至少客戶端應用代碼不需要變動。

  • 一般來說,編寫存儲過程比編寫基本 SQL 語句複雜,需要更高的技能,更豐富的經驗。

    因此,許多資料庫管理員把限制存儲過程的創建作為安全措施(主要受上一條缺陷的影響)。

儘管有這些缺陷,存儲過程還是非常有用的,並且應該使用。事實上,多數 DBMS 都帶有用於管理資料庫和表的各種存儲過程。更多信息請參閱具體的 DBMS 文檔。

說明:不會寫存儲過程?你依然可以使用

大多數 DBMS 將編寫存儲過程所需的安全和訪問許可權與執行存儲過程所需的安全和訪問許可權區分開來。

這是好事情,即使你不能(或不想)編寫自己的存儲過程,也仍然可以在適當的時候執行別的存儲過程。

三、執行存儲過程

存儲過程的執行遠比編寫要頻繁得多,因此我們先介紹存儲過程的執行。

執行存儲過程的 SQL 語句很簡單,即 EXECUTE

EXECUTE 接受存儲過程名和需要傳遞給它的任何參數。

請看下麵的例子(你無法運行這個例子,因為 AddNewProduct 這個存儲過程還不存在):

EXECUTE AddNewProduct('JTS01',
                      'Stuffed Eiffel Tower',
                      6.49,
                      'Plush stuffed toy with the text La Tour Eiffel in red white and blue');

這裡執行一個名為 AddNewProduct 的存儲過程,將一個新產品添加到 Products 表中。

AddNewProduct 有四個參數,分別是:供應商 ID(Vendors 表的主鍵)、產品名、價格和描述。

這 4 個參數匹配存儲過程中 4 個預期變數(定義為存儲過程自身的組成部分)。此存儲過程將新行添加到 Products 表,並將傳入的屬性賦給相應的列。

我們註意到,在 Products 表中還有另一個需要值的列 prod_id 列,它是這個表的主鍵。

為什麼這個值不作為屬性傳遞給存儲過程?要保證恰當地生成此 ID,最好是使生成此 ID 的過程自動化(而不是依賴於最終用戶的輸入)。

這也是這個例子使用存儲過程的原因。以下是存儲過程所完成的工作:

  • 驗證傳遞的數據,保證所有 4 個參數都有值;
  • 生成用作主鍵的唯一 ID;
  • 將新產品插入 Products 表,在合適的列中存儲生成的主鍵和傳遞的數據。

這就是存儲過程執行的基本形式。對於具體的 DBMS,可能包括以下的執行選擇。

  • 參數可選,具有不提供參數時的預設值。
  • 不按次序給出參數,以“參數=值”的方式給出參數值。
  • 輸出參數,允許存儲過程在正執行的應用程式中更新所用的參數。
  • SELECT 語句檢索數據。
  • 返回代碼,允許存儲過程返回一個值到正在執行的應用程式。

四、創建存儲過程

正如所述,存儲過程的編寫很重要。為了獲得感性認識,我們來看一個簡單的存儲過程例子,它對郵件發送清單中具有郵件地址的顧客進行計數。

下麵是該過程的 Oracle 版本:

CREATE PROCEDURE MailingListCount (
  ListCount OUT INTEGER
)
IS
v_rows INTEGER;
BEGIN
    SELECT COUNT(*) INTO v_rows
    FROM Customers
    WHERE NOT cust_email IS NULL;
    ListCount := v_rows;
END;

這個存儲過程有一個名為 ListCount 的參數。此參數從存儲過程返回一個值而不是傳遞一個值給存儲過程。

關鍵字 OUT 用來指示這種行為。Oracle 支持 IN(傳遞值給存儲過程)、OUT(從存儲過程返回值,如這裡)、INOUT(既傳遞值給存儲過程也從存儲過程傳回值)類型的參數。

存儲過程的代碼括在 BEGINEND 語句中,這裡執行一條簡單的 SELECT 語句,它檢索具有郵件地址的顧客。然後用檢索出的行數設置 ListCount(要傳遞的輸出參數)。

調用 Oracle 例子可以像下麵這樣:

var ReturnValue NUMBER
EXEC MailingListCount(:ReturnValue);
SELECT ReturnValue;

這段代碼聲明瞭一個變數來保存存儲過程返回的任何值,然後執行存儲過程,再使用 SELECT 語句顯示返回的值。

下麵是該過程的 SQL Server 版本。

CREATE PROCEDURE MailingListCount
AS
DECLARE @cnt INTEGER
SELECT @cnt = COUNT(*)
FROM Customers
WHERE NOT cust_email IS NULL;
RETURN @cnt;

此存儲過程沒有參數。調用程式檢索 SQL Server 的返回代碼提供的值。

其中用 DECLARE 語句聲明瞭一個名為 @cnt 的局部變數(SQL Server 中所有局部變數名都以 @ 起頭);

然後在 SELECT 語句中使用這個變數,讓它包含 COUNT() 函數返回的值;最後,用 RETURN @cnt 語句將計數返回給調用程式。

調用 SQL Server 例子可以像下麵這樣:

DECLARE @ReturnValue INT
EXECUTE @ReturnValue=MailingListCount;
SELECT @ReturnValue;

這段代碼聲明瞭一個變數來保存存儲過程返回的任何值,然後執行存儲過程,再使用 SELECT 語句顯示返回的值。

下麵是另一個例子,這次在 Orders 表中插入一個新訂單。此程式僅適用於 SQL Server,但它說明瞭存儲過程的某些用途和技術:

CREATE PROCEDURE NewOrder @cust_id CHAR(10)
AS
-- 為訂單號聲明一個變數
DECLARE @order_num INTEGER
-- 獲取當前最大訂單號
SELECT @order_num=MAX(order_num)
FROM Orders
-- 決定下一個訂單號
SELECT @order_num=@order_num+1
-- 插入新訂單
INSERT INTO Orders(order_num, order_date, cust_id)
VALUES(@order_num, GETDATE(), @cust_id)
-- 返回訂單號
RETURN @order_num;

此存儲過程在 Orders 表中創建一個新訂單。

它只有一個參數,即下訂單顧客的 ID。訂單號和訂單日期這兩列在存儲過程中自動生成。

代碼首先聲明一個局部變數來存儲訂單號。接著,檢索當前最大訂單號(使用 MAX() 函數)並增加 1(使用 SELECT 語句)。

然後用 INSERT 語句插入由新生成的訂單號、當前系統日期(用 GETDATE() 函數檢索)和傳遞的顧客 ID 組成的訂單。

最後,用 RETURN @order_num 返回訂單號(處理訂單物品需要它)。

請註意,此代碼加了註釋,在編寫存儲過程時應該多加註釋。

說明:註釋代碼

應該註釋所有代碼,存儲過程也不例外。增加註釋不影響性能,因此不存在缺陷(除了增加編寫時間外)。

註釋代碼的好處很多,包括使別人(以及你自己)更容易地理解和更安全地修改代碼。

對代碼進行註釋的標準方式是在之前放置 --(兩個連字元)。

有的 DBMS 還支持其他的註釋語法,不過所有 DBMS 都支持 --,因此在註釋代碼時最好都使用這種語法。

下麵是相同 SQL Server 代碼的一個很不同的版本:

CREATE PROCEDURE NewOrder @cust_id CHAR(10)
AS
-- 插入新訂單
INSERT INTO Orders(cust_id)
VALUES(@cust_id)
-- 返回訂單號
SELECT order_num = @@IDENTITY;

此存儲過程也在 Orders 表中創建一個新訂單。這次由 DBMS 生成訂單號。

大多數 DBMS 都支持這種功能;SQL Server 中稱這些自動增量的列為標識欄位(identity field),而其他 DBMS 稱之為自動編號(auto number)或序列(sequence)。

傳遞給此過程的參數也是一個,即下訂單的顧客 ID。

訂單號和訂單日期沒有給出,DBMS 對日期使用預設值(GETDATE() 函數),訂單號自動生成。

怎樣才能得到這個自動生成的 ID?在 SQL Server 上可在全局變數 @@IDENTITY 中得到,它返回到調用程式(這裡使用 SELECT 語句)。

可以看到,藉助存儲過程,可以有多種方法完成相同的工作。不過,所選擇的方法受所用 DBMS 特性的制約。

五、小結

本文介紹了什麼是存儲過程,為什麼使用存儲過程。我們介紹了執行和創建存儲過程的語法,使用存儲過程的一些方法。

存儲過程是個相當重要的主題,一篇文章無法全部涉及。

各種 DBMS 對存儲過程的實現不一,你使用的 DBMS 可能提供了一些這裡提到的功能,也有其他未提及的功能,更詳細的介紹請參閱具體的 DBMS 文檔。

原文鏈接:https://www.developerastrid.com/sql/sql-stored-procedures/

(完)


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Termius是微軟的一款SSH終端工具,它支持多平臺。而且操作界面十分ha好看且簡潔,今天分享給大家❤️ 軟體下載 關註下方公眾號,回覆termius獲取下載地址 軟體功能介紹 Termius Mac破解版是一款非常好用而且漂亮的SSH客戶端,能快速遠程式控制制伺服器,可以定製自己喜歡的主題,支持FT ...
  • 一、概述 Impala 直接針對存儲在 HDFS、HBase或 Amazon Simple Storage Service (S3)中的 Apache Hadoop 數據提供快速的互動式 SQL 查詢。Impala是一個基於Hive、分散式、大規模並行處理(MPP:Massively Paralle ...
  • 軟硬體環境 軟體 版本 操作系統 Redhat 7 及以上版本,麒麟V10 DM 資料庫 DM 8.0 及以上版本 CPU 架構 x86、ARM、龍芯、飛騰等國內外主流 CPU DM-Oracle 環境準備 在DM伺服器中完成Dblink環境準備工作。 配置Oracle oci客戶端 在Oracle ...
  • 導讀: 隨著全球數據量的不斷增長,越來越多的業務需要支撐高併發、高可用、可擴展、以及海量的數據存儲,在這種情況下,適應各種場景的數據存儲技術也不斷的產生和發展。與此同時,各種資料庫之間的同步與轉化的需求也不斷增多,數據集成成為大數據領域的熱門方向,於是SeaTunnel應運而生。SeaTunnel是 ...
  • hive 存儲格式有很多,但常用的一般是 TextFile、ORC、Parquet 格式,在我們單位最多的也是這三種 hive 預設的文件存儲格式是 TextFile。 除 TextFile 外的其他格式的表不能直接從本地文件導入數據,要先導入到 TextFile 格式的表中,再從表中用 inser ...
  • 本文介紹 SQL 所涉及的幾個數據處理特性:約束、索引和觸發器。約束是實施引用完整性的重要部分,索引可改善數據檢索的性能,觸發器可以用來執行運行前後的處理。 一、約束 SQL 已經改進過多個版本,成為非常完善和強大的語言。許多強有力的特性給用戶提供了高級的數據處理技術,如約束。 關聯表和引用完整性已 ...
  • 本文介紹什麼是 SQL 游標,為什麼使用游標,如何使用游標。你使用的 DBMS 可能會提供某種形式的游標,以及這裡沒有提及的功能。更詳細的內容請參閱具體的 DBMS 文檔。 一、游標 SQL 檢索操作返回一組稱為結果集的行,這組返回的行都是與 SQL 語句相匹配的行(零行到多行)。 簡單地使用 SE ...
  • 本文介紹什麼是 SQL 事務處理,如何利用 COMMIT 和 ROLLBACK 語句對何時寫數據、何時撤銷進行明確的管理;還學習瞭如何使用保留點,更好地控制回退操作。 一、事務處理 使用事務處理(transaction processing),通過確保成批的 SQL 操作要麼完全執行,要麼完全不執行 ...
一周排行
    -Advertisement-
    Play Games
  • 概述:在C#中,++i和i++都是自增運算符,其中++i先增加值再返回,而i++先返回值再增加。應用場景根據需求選擇,首碼適合先增後用,尾碼適合先用後增。詳細示例提供清晰的代碼演示這兩者的操作時機和實際應用。 在C#中,++i 和 i++ 都是自增運算符,但它們在操作上有細微的差異,主要體現在操作的 ...
  • 上次發佈了:Taurus.MVC 性能壓力測試(ap 壓測 和 linux 下wrk 壓測):.NET Core 版本,今天計劃準備壓測一下 .NET 版本,來測試並記錄一下 Taurus.MVC 框架在 .NET 版本的性能,以便後續持續優化改進。 為了方便對比,本文章的電腦環境和測試思路,儘量和... ...
  • .NET WebAPI作為一種構建RESTful服務的強大工具,為開發者提供了便捷的方式來定義、處理HTTP請求並返迴響應。在設計API介面時,正確地接收和解析客戶端發送的數據至關重要。.NET WebAPI提供了一系列特性,如[FromRoute]、[FromQuery]和[FromBody],用 ...
  • 原因:我之所以想做這個項目,是因為在之前查找關於C#/WPF相關資料時,我發現講解圖像濾鏡的資源非常稀缺。此外,我註意到許多現有的開源庫主要基於CPU進行圖像渲染。這種方式在處理大量圖像時,會導致CPU的渲染負擔過重。因此,我將在下文中介紹如何通過GPU渲染來有效實現圖像的各種濾鏡效果。 生成的效果 ...
  • 引言 上一章我們介紹了在xUnit單元測試中用xUnit.DependencyInject來使用依賴註入,上一章我們的Sample.Repository倉儲層有一個批量註入的介面沒有做單元測試,今天用這個示例來演示一下如何用Bogus創建模擬數據 ,和 EFCore 的種子數據生成 Bogus 的優 ...
  • 一、前言 在自己的項目中,涉及到實時心率曲線的繪製,項目上的曲線繪製,一般很難找到能直接用的第三方庫,而且有些還是定製化的功能,所以還是自己繪製比較方便。很多人一聽到自己畫就害怕,感覺很難,今天就分享一個完整的實時心率數據繪製心率曲線圖的例子;之前的博客也分享給DrawingVisual繪製曲線的方 ...
  • 如果你在自定義的 Main 方法中直接使用 App 類並啟動應用程式,但發現 App.xaml 中定義的資源沒有被正確載入,那麼問題可能在於如何正確配置 App.xaml 與你的 App 類的交互。 確保 App.xaml 文件中的 x:Class 屬性正確指向你的 App 類。這樣,當你創建 Ap ...
  • 一:背景 1. 講故事 上個月有個朋友在微信上找到我,說他們的軟體在客戶那邊隔幾天就要崩潰一次,一直都沒有找到原因,讓我幫忙看下怎麼回事,確實工控類的軟體環境複雜難搞,朋友手上有一個崩潰的dump,剛好丟給我來分析一下。 二:WinDbg分析 1. 程式為什麼會崩潰 windbg 有一個厲害之處在於 ...
  • 前言 .NET生態中有許多依賴註入容器。在大多數情況下,微軟提供的內置容器在易用性和性能方面都非常優秀。外加ASP.NET Core預設使用內置容器,使用很方便。 但是筆者在使用中一直有一個頭疼的問題:服務工廠無法提供請求的服務類型相關的信息。這在一般情況下並沒有影響,但是內置容器支持註冊開放泛型服 ...
  • 一、前言 在項目開發過程中,DataGrid是經常使用到的一個數據展示控制項,而通常表格的最後一列是作為操作列存在,比如會有編輯、刪除等功能按鈕。但WPF的原始DataGrid中,預設只支持固定左側列,這跟大家習慣性操作列放最後不符,今天就來介紹一種簡單的方式實現固定右側列。(這裡的實現方式參考的大佬 ...