一.寫在前面的話
最近一直在思考一個問題,什么才能讓我們不顯得浮躁,真正的靜下心來,用心去感受,用心去回答每個人的問題,用心去幫助別人。現實的生活,往往讓我們顯得精疲力盡,然后我們仔細想過沒用,其實支持我們一直走下去的其實是心里的那一份希冀,希望未來會更好,用一個逼格比較高的詞語,或許那就是理想。那么,有人會說,什么是理想,因為生活的重擔已將我們所謂的理想磨滅了。但是,那又何妨了,而今,我已經被打敗過了,我用曾經的飛蛾撲火,換來今天手心里握着的一把余溫尚存的灰燼。值得慶幸的是,我依然沒有忘記,這把灰燼的名字叫做理想。順勢而為的同時對事物執着而堅定,向着目標努力,理想也許也就不遠了,所以理想還是要有的,萬一實現了呢?

人的活動如果沒有理想的鼓舞,就會變得空虛而渺小。
二.表的約束
在我們創建表的時候,為了保證數據的正確性和完整性,通常我們需要對數據進行校驗,也就是我們這里說到的表的約束。其中常用的表的約束包括:
(1)主鍵的約束。
(2)唯一的約束。
(3)非空的約束。
(4)檢查的約束。
(5)外鍵的約束
這里我們首先創建一張班級表,同時建立對應的約束:
其中班級的id和班級名稱分別定義了主鍵約束和唯一約束,主鍵約束保證了表中只存在唯一一列,唯一約束確保該列字段值不能重復,必須唯一。
接着我們定義一張學生表,也建立相應的約束。

通過建表我們可以看到,其中學生的編號(studid)定義為identity,意思是字段自增長並且從100開始增長,每次增長1。其中性別(gender)存在一個檢查約束,保證插入到數據表里面的數據只存在(‘男’,‘女’)兩種方式,避免其他的臟數據插入。
再說一下比較重要的一個約束——外鍵約束。外鍵約束,表示一個表中的數據依靠另一個表,其中存在關聯關系。正如上面的兩張表一樣,一個學生表中可以定義一個班級iD,來區別這個學生隸屬於哪個班級。那么可以這樣定義:
也就是說,只有在班級表里面存在的班級編號(classid),在學生表里面才可以出現。避免學生找不到班級的情況。這樣以后班級表和學生表通過外鍵classid,建立了一種關系。假如我們要刪除班級表時,系統會報錯,因為在學生表(Students)里面存在外鍵關聯。故必須先刪除學生表再刪除班級表。

三.事務(Transaction)
1.所謂事務,即作為單個工作單元而執行的一系列操作。也就是完成某一件事情,需要包含很多步驟,所有的步驟完成以后,那么這個事情才算完成,其中任何一步出現問題,這個事情就不可能完成。這里所說的一系列步驟,看成一個整體,那么就是一個事務。
(1)隱式事務
隱式事務是隱藏的,也就是每執行一次DML操作,就直接提交到數據庫保存。我們先看看貨運公司表的數據:
1 SELECT * FROM sales.Shippers

比如我們的刪除操作,針對貨運公司表(sales.shippers),我們刪除shipperid為6號的公司:
1 DELETE FROM Sales.Shippers WHERE shipperid=6
刪除以后,我們對比之前的記錄,發現確實將6號貨運公司刪除掉了,這就是隱式事務,默認已經提交了,影響是永久的。

(2)顯示事務
顯示事務,就是明確的指出事務的起始邊界。可以通過回滾操作(rollback)和提交來處理事務的結果。顯示事務的好處就是可以確保執行的完整性,比如銀行的轉賬,要么成功,要么失敗,采取回滾操作,回到原來的狀態。
定義事務的邊界,事務開始用begin transaction——》操作(包含增刪改)——》提交(commit)或者回滾(rollback)。
接着我們同樣采取刪除操作,刪除貨運公司表里面貨運公司號為5,6的公司。那么我們要保證5,6號公司要么全部刪除,要么就都不刪除,所以需要用到事務。sql如下:
1 BEGIN TRANSACTION; 2 3 DELETE FROM Sales.Shippers WHERE shipperid=4; 4 DELETE FROM Sales.Shippers WHERE shipperid=5; 5 6 ROLLBACK;
我們先開始一個事務,然后進行刪除操作,最后執行rollback操作,數據庫表回到以前的狀態,雖然進行了刪除操作,但是rollback回滾,讓數據回到以前狀態。

假如我們執行的是commit操作的話,那么數據就無法回滾。因為已提交到數據庫保存。
1 BEGIN TRANSACTION; 2 3 DELETE FROM Sales.Shippers WHERE shipperid=4; 4 DELETE FROM Sales.Shippers WHERE shipperid=5; 5 6 COMMIT;

2.事務的屬性(ACID)
(1)原子性。事務務必是原子工作單元,這就意味着事務中進行的操作,要么全部執行,那么全都不執行。
(2)一致性。也就是要么都更新,要么都不更新。比如我們要將貨運公司編號為1,2的公司名稱更改為abc,xyz,那么我們在事務中可以這樣執行:
1 BEGIN TRANSACTION; 2 3 UPDATE Sales.Shippers SET companyname='abc' WHERE shipperid=1; 4 UPDATE Sales.Shippers SET companyname='XYZ' WHERE shipperid=2;
我們再新建一個查詢窗口,查詢一下更新的結果:

那么我們提交以后,就可以查詢出來結果,並且確實已經將1,2號貨運公司的名稱更改了。
1 BEGIN TRANSACTION; 2 3 UPDATE Sales.Shippers SET companyname='abc' WHERE shipperid=1; 4 UPDATE Sales.Shippers SET companyname='XYZ' WHERE shipperid=2; 5 6 COMMIT;

這就反應了事務的一致性,只有兩個都更新完了,才能執行其他的操作。
(3)隔離性。其實上面的也反映了一種隔離性,也就是說隔離性,讓我們看不到事務內部如何一步一步的更新的,只有事務提交以后,才能看到統一的結果。類似於上面的更新1,2號貨運公司公司名稱,只有當他們都更新完了,我們才看到更新的結果。
(4)持久性。提交到數據庫保存數據,對數據庫是持久性的,影響永久的。
四.鎖定(lock)和阻塞
鎖是事務獲取的一種控制資源,用於保護數據資源,防止其他事務對數據進行沖突或不兼容的訪問。
(1)排他鎖
自己獨自占有資源,別的事務無法訪問。
(2)共享鎖
共享資源,都可以訪問,每個人各自享用數據資源。
為了說明鎖的應用場景,我們新建三個查詢窗口,進程號分別為52,53,54:

在52窗口中,我們新建一個事務來執行,對產品表中產品編號為2的產品的單價增加一塊錢。
1 BEGIN TRANSACTION; 2 3 UPDATE Production.Products 4 SET unitprice=unitprice+1 5 WHERE productid=2;
為了更新這一行,那么會話必定會獲得一個排他鎖,這樣才能保證更新的完成。那么此時我們試着在53號窗口,進行同一行記錄的查詢結果為怎樣了?

在此處就體現了排他鎖和共享鎖。要得到有關鎖的執行信息,我們可以在54號窗口中,進行相關的查詢,即動態管理視圖(sys.dm_tran_locks)。
1 SELECT 2 request_session_id, --進程ID 3 resource_type, --資源類型 4 resource_database_id, 5 DB_NAME(request_session_id), 6 resource_description, 7 resource_associated_entity_id, 8 request_mode, 9 request_status 10 11 FROM sys.dm_tran_locks

為了獲得更多與阻塞相關的鏈接信息,那么我們可以在54號窗口中接着再查詢sys.dm_exec_connections動態管理視圖。
1 SELECT 2 session_id, 3 connect_time, 4 last_read, 5 last_write, 6 most_recent_sql_handle 7 FROM sys.dm_exec_connections 8 WHERE session_id IN(52,53);
執行結果中,我們可以看到鏈接時間,以及最后讀、寫的時間,還有執行sql的編號。

為了更加細致的看出結果,我們還可以查出系統執行的sql語句。
1 UPDATE Production.Products SET unitprice=unitprice+1 WHERE productid=2;
查詢結果中,我們可以看到系統執行的sql語句:

有時候我們為了排除某一阻塞,我們可以查詢出哪些進程被堵塞了,那么我們就可以通過相應的操作,將阻塞解除。利用sql可以這樣查詢:
1 SELECT 2 session_id, 3 blocking_session_id, 4 command, 5 sql_handle, 6 database_id, 7 wait_type, 8 wait_time, 9 wait_resource 10 FROM sys.dm_exec_requests 11 WHERE blocking_session_id>0;
查詢結果可以看出53號進程被52號進程給堵塞住了,同時可看到等待的時間,以及等待的資源。

這樣52號進程一直在等待,為了使進程釋放出來,不一直等下去,那么我們可以設置超時,超過響應設置的時間,則結束會話。設置超時可以這樣設置:
1 SET LOCK_TIMEOUT 5000; 2 3 SELECT * FROM Production.Products 4 WHERE productid=2;
設置以后,那么超過請求時間,則結束回話。

其中設置(set locktimeout -1)為無限等待。默認為-1;同時有另一種結束回話的用法kill。可以直接結束掉相應的進程號。
例如我們結束掉52號進程,(kiil 52)。
1 KILL 52;
今天先學習到這里,下次接着學習鎖所產生的隔離級別。
希望各位大牛給出指導,不當之處虛心接受學習!謝謝!
