文出處: http://www.cnblogs.com/wy123/p/5958047.html
最近發現還有不少做開發的小伙伴,在寫存儲過程的時候,在參考已有的不同的寫法時,往往很迷茫,
不知道各種寫法孰優孰劣,該選用那種寫法,以及各種寫法優缺點,本文以一個簡單的查詢存儲過程為例,簡單說一下各種寫法的區別,以及該用那種寫法
專業DBA以及熟悉數據庫的同學請無視。
廢話不多,上代碼說明,先造一個測試表待用,簡單說明一下這個表的情況
類似訂單表,訂單表有訂單ID,客戶ID,訂單創建時間等,查詢條件是常用的訂單ID,客戶ID,以及訂單創建時間
create table SaleOrder
(
id int identity(1,1),
OrderNumber int ,
CustomerId varchar(20) ,
OrderDate datetime ,
Remark varchar(200)
)
GOdeclare @i int=0while @i<100000begin
insert into SaleOrder values (@i,CONCAT('C',cast(RAND()*1000 as int)),GETDATE()-RAND()*100,NEWID()) set @i=@i+1endcreate index idx_OrderNumber on SaleOrder(OrderNumber)create index idx_CustomerId on SaleOrder(CustomerId)create index idx_OrderDate on SaleOrder(OrderDate)
生成的測試數據大概就是這個樣子的
下面演示說明幾種常見的寫法以及每種寫法潛在的問題
第一種常見的寫法:拼湊字符串,用EXEC的方式執行這個拼湊出來的字符串,不推薦
create proc pr_getOrederInfo_1
( @p_OrderNumber int , @p_CustomerId varchar(20) , @p_OrderDateBegin datetime , @p_OrderDateEnd datetime)asbegin
set nocount on; declare @strSql nvarchar(max); set @strSql='SELECT [id]
,[OrderNumber]
,[CustomerId]
,[OrderDate]
,[Remark]
FROM [dbo].[SaleOrder] where 1=1 '; /*
這種寫法的特點在于將查詢SQL拼湊成一個字符串,最后以EXEC的方式執行這個SQL字符串 */
if(@p_OrderNumber is not null) set @strSql=@strSql + ' and OrderNumber=' + @p_OrderNumber
if(@p_CustomerId is not null) set @strSql=@strSql + ' and CustomerId='+ ''''+ @p_CustomerId + ''''
if(@p_OrderDateBegin is not null) set @strSql=@strSql + ' and OrderDate >=' + '''' + cast(@p_OrderDateBegin as varchar(10)) + ''''
if(@p_OrderDateEnd is not null) set @strSql=@strSql + ' and OrderDate <=' + '''' + cast(@p_OrderDateEnd as varchar(10)) + ''''
print @strSql
exec(@strSql);end
假如我們查詢CustomerId為88,在2016-10-1至2016-10-3這段時間內的訂單信息,如下,帶入參數執行
exec pr_getOrederInfo_1 @p_OrderNumber=null , @p_CustomerId='C88' , @p_OrderDateBegin='2016-10-1' , @p_OrderDateEnd='2016-10-3'
首先說明,這種方式執行查詢是完全沒有問題的如下截圖,結果也查出來了(當然結果也是沒問題的)
我們把執行的SQL打印出來,執行的SQL語句本身就是就是存儲過程中拼湊出來的字符串,這么一個查詢SQL字符串
SELECT [id]
,[OrderNumber]
,[CustomerId]
,[OrderDate]
,[Remark]FROM [dbo].[SaleOrder] where 1=1
and CustomerId='C88'
and OrderDate >='2016-10-1'
and OrderDate <='2016-10-3'
那么這種存儲過程的有什么問題,或者直接一點說,這種方式有什么不好的地方
其一,繞不過轉移符(以及注入問題)
在拼湊字符串時,把所有的參數都當成字符串處理,當查詢條件本身包含特殊字符的時候,比如 ' 符號,
或者其他需要轉義的字符???,你拼湊的SQL就被打斷了
舉個不恰當的例子,比如字符串中 @p_CustomerId中包含 ' 符號,直接就把你拼SQL的節湊給打亂了
拼湊的SQL就變成了這個樣子了,語法就不通過,更別提執行
SELECT [id]
,[OrderNumber]
,[CustomerId]
,[OrderDate]
,[Remark]
FROM [dbo].[SaleOrder]
where 1=1 and CustomerId='C'88'
一方面需要處理轉移符,另一方面需要要防止SQL注入
其二,參數不同就必須重新編譯
這種拼湊SQL的方式,如果每次查詢的參數不同,拼湊出來的SQL字符串也不一樣,
如果熟悉SQL Server的同學一定知道,只要你執行的SQL文本不一樣,
比如
第一次是執行查詢 *** where CustomerId='C88' ,
第二次是執行查詢 *** where CustomerId='C99' ,因為兩次執行的SQL文本不同
每次執行之前必然需要對其進行編譯,編譯的話就需要CPU,內存資源
如果存在大批量的SQL編譯,無疑要消耗更多的CPU資源(當然需要內存資源)
第二種常見的寫法:對所有查詢條件用OR的方式加在where條件中,非常不推薦
create proc pr_getOrederInfo_2
( @p_OrderNumber int , @p_CustomerId varchar(20) , @p_OrderDateBegin datetime , @p_OrderDateEnd datetime)asbegin
set nocount on; declare @strSql nvarchar(max); SELECT [id]
,[OrderNumber]
,[CustomerId]
,[OrderDate]
,[Remark]
FROM [dbo].[SaleOrder]
where 1=1
and (@p_OrderNumber is null or OrderNumber=@p_OrderNumber) and (@p_CustomerId is null or CustomerId=@p_CustomerId) /*
這是另外一種類似的奇葩的寫法,下面會重點關注
and OrderNumber=ISNULL( @p_OrderNumber,OrderNumber)
and CustomerId=ISNULL( @p_CustomerId,CustomerId) */
and (@p_OrderDateBegin is null or OrderDate >=@p_OrderDateBegin) and (@p_OrderDateEnd is null or OrderDate <=@p_OrderDateEnd)
end
首先看這種方式的執行結果,帶入同樣的參數,跟上面的結果一樣,查詢(結果)本身是沒有任何問題的
這種寫法寫起來避免了拼湊字符串的處理,看起來很簡潔,寫起來也很快,稀里嘩啦一個存儲過程就寫好了,
發布到生產環境之后就相當于埋了一顆雷,隨時引爆。
因為一條低效而又頻繁執行的SQL,拖垮一臺服務器也是司空見慣
但是呢,問題非常多,也非常非常不推薦,甚至比第一種方式更糟糕。
分析一下這種處理方式的邏輯:
這種處理方式,因為不確定查詢的時候到底有沒有傳入參數,也就數說不能確定某一個查詢條件是否生效,
于是就采用類似 and (@p_OrderNumber is null or OrderNumber=@p_OrderNumber)這種方式,來處理參數,
這樣的話
如果@p_OrderNumber為null,or的前者(@p_OrderNumber is null)成立,后者不成立,查詢條件不生效
如果@p_OrderNumber為非null,or的后者(OrderNumber=@p_OrderNumber)成立而前者不成立,查詢條件生效
總之來說,不管參數是否為空,都可以有效地拼湊到查詢條件中去。
避免了拼SQL字符串,既做到讓參數非空的時候生效,有做到參數為空的時候不生效,看起來不錯,是真的嗎?
那么這種存儲過程的有什么問題?
1,可能會抑制索引的情況
為什么說可能會抑制到索引的時候?上面提到過,SQL在執行之前是需要編譯的,
因為在編譯的時候并不知道查詢條件是否傳入了值,有可能為null,有可能是一個具體的值
SQL Server為了保險起見,采用了全表掃描的方式,舉個簡單的例子
如果我直接帶入CustomerId=‘C88’,再來看執行計劃,結果跟上面一樣,但是執行計劃是完全不一樣的,這就是所謂的抑制到索引的使用。
2,非常非常致命的邏輯錯誤
/*
這是另外一種類似的奇葩的寫法,需要重點關注,真的就能滿足“不管參數是否為空都滿足”
and OrderNumber=ISNULL( @p_OrderNumber,OrderNumber)
and CustomerId=ISNULL( @p_CustomerId,CustomerId)
*/
對于如下這種寫法:OrderNumber=ISNULL( @p_OrderNumber,OrderNumber),
一部分人非常推崇,認為這種方式簡單、清晰,我也是醉了,有可能產生非常嚴重的邏輯錯誤
如果參數為null,就轉換成這種語義 where 1=1 and OrderNumber=OrderNumber
目的是查詢參數為null,查詢條件不生效,讓這個查詢條件恒成立,恒成立嗎,不一定,某些情況下就會有嚴重的語義錯誤
博主發現這個問題也是因為某些實際系統中的bug,折騰了好久才發現這個嚴重的邏輯錯誤 http://www.cnblogs.com/wy123/p/5580821.html
對于這種寫法,
不管是第一點說的抑制索引的問題,數據量大的時候是非常嚴重的,上述寫法會造成全表掃描,有索引頁用不上,至于全表掃描的壞處就不說了
還是第二點說的造成的邏輯錯誤,都是非常致命的
所以這種方式是最最不推薦的。
第三種常見的寫法:參數化SQL,推薦
create proc pr_getOrederInfo_3
( @p_OrderNumber int , @p_CustomerId varchar(20) , @p_OrderDateBegin datetime , @p_OrderDateEnd datetime)asbegin
set nocount on;
DECLARE @Parm NVARCHAR(MAX)=N'',
@sqlcommand NVARCHAR(MAX)=N''
SET @sqlcommand='SELECT [id]
,[OrderNumber]
,[CustomerId]
,[OrderDate]
,[Remark]
FROM [dbo].[SaleOrder]
where 1=1 '
IF(@p_OrderNumber IS NOT NULL) SET @sqlcommand=CONCAT(@sqlcommand,' AND OrderNumber=@p_OrderNumber') IF(@p_CustomerId IS NOT NULL) SET @sqlcommand=CONCAT(@sqlcommand,' AND CustomerId=@p_CustomerId') IF(@p_OrderDateBegin IS NOT NULL) SET @sqlcommand=CONCAT(@sqlcommand,' AND OrderDate>=@p_OrderDateBegin ') IF(@p_OrderDateEnd IS NOT NULL) SET @sqlcommand=CONCAT(@sqlcommand,' AND OrderDate<=@p_OrderDateEnd ') SET @Parm='@p_OrderNumber int,
@p_CustomerId varchar(20),
@p_OrderDateBegin datetime,
@p_OrderDateEnd datetime '
PRINT @sqlcommand
EXEC sp_executesql @sqlcommand,@Parm, @p_OrderNumber=@p_OrderNumber, @p_CustomerId=@p_CustomerId, @p_OrderDateBegin=@p_OrderDateBegin, @p_OrderDateEnd=@p_OrderDateEnd
end
首先我們用同樣的參數來執行一下查詢,當然沒問題,結果跟上面是一樣的。
所謂的參數化SQL,就是用變量當做占位符,通過 EXEC sp_executesql執行的時候將參數傳遞進去SQL中,在需要填入數值或數據的地方,使用參數 (Parameter) 來給值,
這樣的話,
第一,既能避免第一種寫法中的SQL注入問題(包括轉移符的處理),
因為參數是運行時傳遞進去SQL的,而不是編譯時傳遞進去的,傳遞的參數是什么就按照什么執行,參數本身不參與編譯
第二,保證執行計劃的重用,因為使用占位符來拼湊SQL的,SQL參數的值不同并導致最終執行的SQL文本不同
同上面,參數本身不參與編譯,如果查詢條件一樣(SQL語句就一樣),而參數不一樣,并不會影響要編譯的SQL文本信息
第三,還有就是避免了第二種情況(and (@p_CustomerId is null or CustomerId=@p_CustomerId)
或者 and OrderNumber=ISNULL( @p_OrderNumber,OrderNumber))
這種寫法,查詢條件有就是有,沒有就是沒有,不會丟給SQL查詢引擎一個模棱兩個的結果,
避免了對索引的抑制行為,是一種比較好的處理查詢條件的方式。
缺點,對于這種方式,也有一點不好的地方,就是拼湊的字符串處理過程中,
調試具體的SQL語句的時候,參數是直接拼湊在SQL文本中的,不能直接執行,要手動將占位參數替換成具體的參數值
總結:
以上總結了三種在開發中比較常見的存儲過程的寫法,每種存儲過程的寫法可能在不同的公司都用應用,
是不是有人挑個最簡單最快捷(第二種)寫法,寫完不是完事了,而是埋雷了。
不是太熟悉SQL Server的同學可能會有點迷茫,有很多種寫法,究竟要用哪種寫法這些寫法之間有什么區別。
本文通過一個簡單的示例,說了常見的幾種寫法之間的區別,每種方式存在的問題,以及孰優孰劣,請小伙伴們明辨。
數據庫大神請無視,謝謝。
dbForge Studio 2019-2022 for SQL Server是針對SQL Server數據庫而開發的一款強大的集成開發環境,主要用于SQL Server管理,管理,開發,數據報告和分析。可以幫助SQL開發人員管理數據庫,在流行的源代碼控制系統中進行版本控制的數據庫更改,加快日常任務的速度以及進行復雜的數據庫更改。
在 SQL 編輯器中編寫代碼需要花費大量時間,而您可以通過以下方式提高工作效率:
● 代碼自動完成 — 通過幾個按鍵即可創建完整的 SQL 語句。
● SQL 格式化 — 遵循常見的編碼風格(標準)。
● T-SQL 分析器 — 使用可配置的規則提高代碼質量。
● SQL 代碼片段 — 存儲和重用重復代碼片段,節省時間。
● 快速對象信息 — 在數據庫對象上顯示提示,提供相關信息。
● 代碼導航 — 快速跳轉到變量聲明、對象編輯器等地方,按 F12 鍵即可。
● 表是任何數據庫中的關鍵對象,也是最難配置的對象之一。精心設計的 SQL 表設計器允許您:在可視化編輯器中快速設置表屬性。
● 編輯創建表的腳本。
● 當引入復雜更改時重新構建表。
● 在修改 SQL 數據庫對象之前預覽更改。
● 允許你在不編寫代碼的情況下可視化、創建和編輯 SQL Server 數據庫。將數據庫拖放到圖表上,即可清晰顯示其基本對象及其關聯。數據庫設計器功能包括:數據庫圖表和縮放。
● 打印大型圖表。
● 虛擬連接。
● 可視化數據庫編輯器。
● 嘗試一下模式比較工具,你將會在數據庫結構上的任何重大更改中使用此工具。我們的工具將幫助您:同步具有復雜對象依賴關系的數據庫模式。
● 提前檢測開發錯誤。
● 創建模式快照以捕捉數據庫結構。
● 檢測生產數據庫上的偏移。
● 創建針對不同 SQL Server 版本的部署腳本。
● 一個方便的用于文檔化 SQL Server 數據庫的工具。文檔生成工具允許您:獲取數據庫架構概覽
● 包括自描述的 SQL 對象
● 查看對象間和數據庫間的依賴關系
● 自定義文檔樣式
● 生成 HTML 和 PDF 格式的文檔
支持 10 多種常用數據格式,提供多種高級選項,針對重復場景提供模板,支持可定制的常規命令行導入和導出。
將源代碼控制系統整合到您的數據庫開發和部署流程中。通過 dbForge Studio,SQL 數據庫的版本控制變得順暢而無縫。該功能允許用戶:
● 將數據庫鏈接到最流行的源代碼控制系統
● 對工作文件夾進行源代碼控制
● 在方便的界面中可視化執行所有源代碼控制任務
● 跟蹤變更歷史記錄
● 解決沖突
● 這是一個用于監視和審查 SQL Server 性能和活動的出色工具。通過這個工具,分析和管理 SQL Server 上的任何變化變得更加輕松和舒適。該工具允許用戶:監視 SQL Server 和數據庫活動,如 CPU 和內存工作負載、死鎖、讀/寫和 IO 延遲、等待任務、批處理請求等等
● 獲取與數據輸入/輸出相關的統計信息
● 查看數據庫指標
● 使用等待統計信息分析與 SQL Server 和查詢相關的資源
● 按照消耗時間對最復雜和耗時的查詢進行排序
● 接收有關活動用戶連接的寶貴信息
● 監視存儲位置、大小和備份日期
用于分析 SQL 索引的狀態并解決索引碎片化問題。索引管理器允許您快速收集索引碎片化統計信息,檢測需要維護的數據庫。您可以在可視化模式下立即重建和重組 SQL 索引,或生成 SQL 腳本以供將來使用。
作為服務器端邏輯的必備 SQL 數據庫開發工具。它集成在存儲過程編輯器中。通過單擊數據庫資源管理器樹開始調試。
一款直觀且便捷的工具,用于實現自動化單元測試。該工具基于開源的 tSQLt 框架,因此 SQL 開發人員可以從在普通 T-SQL 中編寫單元測試中受益。單元測試豐富的功能使得開發穩定可靠的代碼成為可能,可以在單元級別進行適當的回歸測試。
下載安裝包,雙擊安裝程序,打開第一個安裝界面
點擊安裝后進入安裝目錄選擇,這里選擇D盤。
然后進入文件關聯的選擇,大家可以根據自己的需要進行選擇。
接著安裝模式進行選擇,這里按照推薦選擇第一項
安裝過程如下圖
安裝本地鏡像過程如下圖:
安裝成功界面如下:
四、使用說明
打開軟件,首先創建數據庫鏈接
數據庫屬性配置如下圖:
點擊測試提示成功
打開數據庫,可以展示表、視圖等的數量
點擊編輯表,打開數據表設計器界面
數據表索引管理界面
切換到Data標簽頁查詢數據表數據
可以自定義查詢條件,如下圖:
查詢條件會展示在查詢界面下方
數據庫新增age列,聯動sql直接變化
數據導出支持格式有十多種,基本滿足需求
導出成功界面如下
查看導出數據
查詢效果如下:
軟件獲取:私信回復【數據庫工具】獲取
、SQL Server2016安裝
1、處理器、內存和操作系統要求:
內存 | 最小值: Express 版本:512 MB 所有其他版本:1 GB 建議: Express 版本:1 GB 所有其他版本:至少 4 GB 并且應該隨著數據庫大小的增加而增加,以便確保最佳的性能。 |
處理器速度 | 最低要求:x64 處理器:1.4 GHz 建議:2.0 GHz 或更快 |
處理器類型 | x64 處理器:AMD Opteron、AMD Athlon 64、支持 Intel EM64T 的 Intel Xeon、支持 EM64T 的 Intel Pentium IV |
SQL Server Enterprise | Windows Server 2012、 Windows Server 2012 R2 |
SQL Server Business Intelligence | Windows Server 2012、 Windows Server 2012 R2 |
SQL Server Standard SQL Server Developer SQL Server Express | Windows 8、Windows 8.1 Windows 10 Windows Server 2012、 Windows Server 2012 R2 |
SQL Server Web | Windows Server 2012、 Windows Server 2012 R2 |
2、SQL Server2016下載地址:
https://www.microsoft.com/zh-cn/evalcenter/evaluate-sql-server-2016
3、安裝需要的組件:
(1)NET Framework
默認在安裝sqlserver2016時會自動安裝
下載地址:https://support.microsoft.com/zh-cn/kb/3045560
(2)Oracle JRE7
http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html
作者:shy
*請認真填寫需求信息,我們會在24小時內與您取得聯系。