sql之T-SQL

下面就T-SQL的幾個方面來分別講解一下。sql

一、變量數據庫

 要動態的寫sql語句,就不能沒有變量。編程

聲明變量並賦值:c#

1 declare @i as int;--定義一個 int 類型的 變量 (as能夠省略)
2 print @i;--這注意:沒有賦值以前,程序不會報錯,並且輸出一個 空
3 set @i=3;
4 print @i;

在sql server 2008以後就能夠對變量 在聲明的同時進行賦值緩存

1 declare @a int=3;
2 print @a;

在變量的使用過程當中,必定要注意nvarcahr 和nchar的區別。安全

1 declare @s nvarchar(20);
2 set @s='Hello';
3 set @s=@s+' World!';
4 print @s;--輸出的是 Hello  World!
5 
6 declare @s2 nchar(20);
7 set @s2='Hello';
8 set @s2=@s2+' World!';
9 print @s2;--輸出的是 Hello。

爲何使用nchar倒是輸出的 Hello,由於:nchar是固定長度,即便長度沒有達到最大,可是其他長度用 空來代替了,因此 至關因而滿的,因此在進行字符串的相加 是不會起做用的。服務器

 

在查詢中賦值:架構

1 declare @now datetime;
2 select @now=GETDATE();
3 print @now;
4 declare @orderNum int;
5 select @orderNum = COUNT(1) from [Sales.Orders];--這條查詢語句只是用來 對 變量進行賦值的,不會返回查詢結果的.
6 print @orderNum; 

主要做用:是將查詢結果保存在 變量裏,爲了下面的使用。ide

其實也可使用 set 賦值的方式 實現 上面的做用函數

1 set @orderNum =(select COUNT(1) from [Sales.Orders])
2 print @orderNum

二、批處理

 使用「go」,go前面的 全部的 語句處於在一個 批裏面。不一樣批 的變量是不能互相調用。

三、流程控制

1)條件控制

1 declare @minute int ;
2 set @minute =DATEPART(minute,getdate());
3 if @minute>20
4    begin--一條語句能夠將  begin end 省略
5       print '立刻睡覺';
6    end
7 else
8    print '繼續快樂玩耍';
if條件控制

2)循環控制

 1 --高斯問題
 2 declare @sum int,@i int;
 3 set @sum=0;
 4 set @i=0;
 5 while @i<100
 6    begin
 7    set @i=@i+1;
 8    set @sum=@sum+@i;
 9    end
10 print @sum;
while循環控制

 在sql server 裏面的 continue 和 break 和c#裏面的 使用是相同的。

四、遊標

什麼是遊標:

 不是基於集合的操做,而是將集合中的數據逐條取出來,逐條進行操做。

何時使用遊標:

 默認的狀況下是使用集合的方式 查詢,若是要使用 遊標  必須是在 能有讓人信服的 理由下才考慮使用。

通常不使用遊標的緣由:

 一、使用遊標,嚴重違背了關係模型,關係模型是基於集合考慮的。

二、逐條對記錄進行操做會帶來性能上的開銷。給定一個集合,對集合進行一系列的遊標代碼的操做,必定會帶來性能上的開銷,而且這種使用遊標的 方式 性能比 集合 慢了好幾倍。
三、使用遊標 要寫不少代碼。可是使用集合的,就只 把須要的數據查詢出來,不描述怎麼獲取他們。遊標寫不少代碼,可讀性差,維護差。

存在的意義:

 說了這麼多的 弊端,那是遊標還有存在的意義嗎?固然:當須要對查詢出來的數據  逐條進行 處理的 時候就要使用遊標.

使用遊標的步驟:

一、在查詢中聲明遊標

二、打開遊標

三、從第一個值開始將值賦值到對應的變量裏面。

四、循環遍歷遊標,將賦值變量拿過來進行操做。

五、關閉遊標

六、釋放遊標

 1 --一、首先在查詢基礎上聲明遊標
 2 declare c cursor
 3    for
 4    select shipperid,companyname 
 5    from [Sales.Shippers];
 6 
 7 --二、打開遊標
 8 open c;
 9 
10 --三、從第一個遊標開始把 值 賦值到 對應的變量裏面
11 declare @id int,@name nvarchar(20);
12 
13 --每次取出來一條數據,  添加到指定的 變量。
14 fetch next from c into @id,@name;
15 
16 --四、循環遍歷 遊標,將賦值的變量 拿過來進行相應的操做
17 while @@FETCH_STATUS=0--等於0表明  遊標沒有超過最後一行
18     begin
19        --相應的操做處理
20        print @name;
21        
22        --嘗試 讀取下一條 數據
23        fetch next from c into @id,@name;
24     end
25 
26 --五、關閉遊標
27 close c;
28 
29 --六、釋放遊標
30 deallocate c;
使用遊標過程

總結:

一、使用遊標,要很是謹慎,由於性能消耗很大,不肯定的時候絕對不能使用

二、遊標的惟一 好處:就是能夠對查詢數據,進行 逐條操做。這也正是它適應的場合。

五、臨時表

使用場景:

當要將一些 數據 放到表裏面 保存,可是又不想 建立一張數據表(由於通常 公司只有DBA 纔有權限建立表),  或者我指向讓當前 數據 只有當前 會話可見 ,甚至 只要當前 批 可見。

臨時表的種類:

sql server三種臨時表:局部臨時表、全局臨時表、表變量

下面對三種臨時表進行分別講解:

局部臨時表:

一、建立過程和使用方式 都普通表是 同樣,加上「#」 就表明是臨時表.

二、只對 建立 他的 會話 是可見的,而且存儲 在 系統數據庫的 tempdb 數據的 臨時表 裏面。當前會話(進程)結束後,臨時表會自動被刪除

三、在 sql server 裏 系統建立的臨時表 都會加 後綴名,就是爲了防止 不一樣進程之間 建立相同的表名的 表名,保證惟一性。

普通建立臨時表的方法:

1 create table #partTable
2 (
3   num int
4 );
5 
6 insert into #partTable (num) values (1),(2),(3);
7 go;--再也不 一個 批 裏面 也能使用 ,只要是在同一個 進程裏面
8 select * from #partTable;

在查詢過程當中建立臨時表,並將查詢出的數據插入到臨時表裏面

1 select * into #table from [Sales.Shippers]
2 select * from #table;  

全局臨時表:

一、在 表名 前面 添加兩個 「#」 表明是 全局臨時表

 二、注意:對全部的 會話(進程) 都是可見的, 可是 當前進程 若是關閉或者  全局臨時表 長時間沒有被使用,那麼就會被刪除

1 create table ##allTable
2 (
3   num int
4 );
5 insert into ##allTable (num) values(1),(2),(3),(4);
6 select * from ##allTable

表變量:

注意:它也會在  tempdb數據庫 裏面建立一個 對應物理 臨時表,只不過,他只對當前操做他的"批"可見,並且 當前 批 執行完成 以後就會 刪除 臨時表.(因此必定要注意:表 變量 並非 存在內存中,他也會 建立一張物理數據表)

 

性能考慮:當 只有幾行數據的時候,固然是 表變量 性能好;;可是若是是大量數據,應該使用臨時表

 1 declare @tableVariable table
 2 (
 3   num int
 4 ) 
 5 
 6 insert into @tableVariable (num) values (1),(2),(3);
 7 select * from @tableVariable;
 8 go;
 9 --再也不同一 批 裏面是不能訪問到  表 變量
10 select * from @tableVariable;

 

最重要的一點:表變量同變量同樣,當事務回滾以後,變量的值不會回滾。同理表變量也不會回滾。

 1 --回滾中 變量的不會回滾的  特殊狀況
 2 declare @num int;
 3 set @num =1;
 4 begin transaction;
 5 set @num=12;
 6 print @num;
 7 rollback;
 8 --注意:事務回滾,若是變量在 事務裏面 改變,回滾的時候 變量是不會回滾的.
 9 print @num;
10 
11 --同理:表變量也是如此的
12 declare @tableVariable2 table
13 (
14   num int
15 );
16 insert into @tableVariable2 (num) values(1),(2),(3);
17 begin transaction;
18 delete from @tableVariable2 where num =1;
19 rollback;
20 --表變量是不會 回滾的
21 select * from @tableVariable2;
事務回滾表變量不會回滾

六、動態sql

什麼是動態sql

首先靜態sql就是普通的靜態查詢語句。

動態sql:就是使用 exec來執行字符串sql語句。

1 declare @sql nvarchar(100);
2 set @sql ='select * from [Sales.Shippers]';
3 exec(@sql)

 

缺點:不能防止sql注入漏洞攻擊。(什麼是sql注入漏洞你們應該都懂的吧。就不作介紹了)

 

爲解決 上面的 sql注入漏洞攻擊 因此又出現了 第二種動態sql :sp_executesql存儲過程:

一、安全,由於他 支持 輸入和輸出參數的設置

二、性能比 exec 要好:由於它的 參數化 有助於 重用 緩存過 的執行計劃. 執行計劃:就是sql server 處理 sql 語句時 生成的指令.  若是 要想要 複用  緩存 中執行計劃.必須保證 sql 字符串語句 相同.因此要 由於 使用  參數化 的sql語句 只要替換 參數就能夠,因此 sql語句 不變化 能夠複用.

 1 declare @sql nvarchar(100);
 2 set @sql='select * from [Sales.Shippers] where companyname=@name';
 3 declare @name nvarchar(20);
 4 --set @name='順豐';
 5 set @name='順豐;select * from [Sales.Shippers]';--即便這樣,想要進行sql 注入漏洞攻擊,不可能,由於 在sql 語句 把整個 @name裏面的 值  做爲一個 字符串 來使用的,就是執行 companyname 和 整個字符串的對比
 6 
 7 exec sp_executesql 
 8   --下面兩個是很是重要的
 9   @stmt=@sql,--動態執行的 sql語句
10   @params=N'@name as nvarchar(20)',--參數的類型
11   @name=@name;--參數賦值
sp_executesql

七、例程

例程是什麼:

爲了 計算結果 或者 執行任務 而 封裝的代碼 的一種編程現象.提到例程,你們可能不知道,可是提到下面的他的三個種類,就全都知道了。

例程的種類:

用戶自定義函數、存儲過程、觸發器

最經常使用的是存儲過程,下面先對存儲過程進行介紹。

存儲過程:

建立存儲過程:

1 --存儲過程:最經常使用的方法
2 create procedure MyDemoPro
3 (
4    --存儲過程當中 要使用到 的參數
5    @orderid int
6 )
7 as
8 --下面是執行的 sql 語句
9 select * from [Sales.Orders] where orderid=@orderid;

執行存儲過程:

exec MyDemoPro @orderid=10;
--能夠簡寫成:exec MyDemoPro 10;

要搞懂存儲過程,就必須搞懂他的三個參數類型:

傳入參數、傳出參數、return參數。

傳入參數:

就是普通的參數;上面使用的那中就是 參入參數

傳出參數:

output 定義的參數:能夠  傳出 供用戶使用的

 1 create procedure OrderCount
 2 (
 3    @count int output
 4 )
 5 as
 6 select @count=COUNT(*) from [Sales.Orders];
 7 go;
 8 
 9 --執行 ,必定以聲明一個變量 ,賦值給  傳出參數
10 declare @outCount int ;
11 exec OrderCount  @count=@outCount output;
12 print @outCount;
傳出參數

return參數:

特殊的參數:和 c#裏面的不同,這裏只用來,表示 操做結果的正確或錯誤,只能返回數字

 1 alter procedure ReturnProc
 2 (
 3    @username nvarchar(100)
 4 )
 5 as
 6   declare @usernameLen int;
 7   set @usernameLen=LEN(@username);
 8   
 9   if @usernameLen>=5
10   return '1';
11   else
12   return '0';
13 
14 
15 declare @result int;
16 --如何爲 return 參數 賦值
17 exec  @result = ReturnProc @username='wanglihong';
18 print @result;
returns參數

 

若是將一個返回參數設置成'asd',就會報錯以下:

 

自定義函數:

一、能夠直接返回一個值  

二、分兩種:

標量函數(返回值爲一個 值)

表函數(返回值是一張表)(存在與 可編程性 裏面的函數裏面)

三、實際開發中不多使用。

 1 create function GetMinute
 2 (
 3    @date datetime
 4 )
 5 --設置返回值:
 6 returns int
 7 as
 8   begin
 9      declare @minute int;
10      set @minute =datepart(minute,@date);
11      return @minute;
12   end
13 
14 --使用自定義函數
15 select dbo.GetMinute(GETDATE());
自定義函數

 

觸發器:

特殊的存儲過程。主要做用:檢索。注意:觸發器必須依附於事件,只有當事件發生時候,出發觸發器,運行觸發器代碼。(sql  server 中和 觸發器相對應兩個事件:數據操做事件和數據定義事件,從而對應下面的連個觸發器)

種類分爲兩種:

  DML觸發器(修改觸發器:對錶的數據修改:如:update等)

  DLL觸發器(架構觸發器:對數據庫的架構進行修改:如建立表)

DML觸發器:

分爲兩種:

  after觸發器(對錶操做)

  instead of 觸發器(對視圖進行操做)

注意:在觸發器的代碼裏,只能訪問到 inserted 和deleted 兩張表.對數據進行更新的是 先刪除而後在插入執行的。 inserted表包含 insert 和update操做 以後新數據的行。deleted表含有 delete和update 操做 以後舊數據的 行。

對於after觸發器是常用,因此這裏只對 after作介紹:

與之關聯的事件執行完成以後纔出發after觸發器。

爲shipper(貨運公司)表建立一張日誌表:

1 create table Ship_Log
2 (
3    id int identity(1,1) primary key,
4    op_date datetime default getdate(),
5    op_uaer nvarchar(50),
6    op      nvarchar(50),
7    shipname nvarchar(50),
8    shipphone nvarchar(50)
9 )

 

爲表 dbo.Sales.Shipper建立觸發器

1 create trigger ship_log_trigger
2 on [Sales.Shippers] after insert
3 as
4    --當對上面的表進行 增刪改的時候執行 觸發器的下面的代碼
5    insert into Ship_Log (op_uaer,op,shipname,shipphone)
6    select user_name()--返回當前操做的用戶名
7    ,'insert',companyname,phone  from inserted;
觸發器

 

向表 dbo.Sales.Shipper 中插入數據觸發觸發器:

1 insert into [Sales.Shippers] (companyname,phone) values('shits','12345678')

 

查詢日誌表:

select * from Ship_Log;

 

查詢結果:

已經將日誌插入進去了。

DLL觸發器

通常用不到。

分爲兩種:

對數據庫的觸發(例如:建立表)

對服務器的觸發(例如:建立數據庫)

八、標識

標識:就是有時咱們會將主鍵設置爲標識列(自動增長列),而後查詢標識的時候就是查到最新增長的標識列的值。

分爲兩種:

一、全局範圍的:@@identity

二、當前 表 範圍的 :SCOPE_IDENTITY();最經常使用

注意:若是想一張表含有觸發器的表中插入數據的話,查詢到的結果就是不一樣(由於向表中插入數據以後,觸發器還會再向日誌表中插入數據,因此全局標識查到的是日誌表中的標識,而 SCOPE_IDENTITY()查到的 插入數據表裏的 標識)

1 insert into [Sales.Shippers] (companyname,phone) values('asd','12345');
2 select @@identity;--整個數據庫中全部的  最新最新增長的 標識列
3 
4 select SCOPE_IDENTITY();--得到 當前操做的表的最新增長的 標識列的值

若是想沒有觸發器的 表 插入數據,兩個就查詢的標識列的值相同

相關文章
相關標籤/搜索