1. 程式人生 > >學習筆記-SQL語言基礎及資料庫基本原理

學習筆記-SQL語言基礎及資料庫基本原理

  資料庫基本儲存原理

 基本儲存單元——

資料庫檔案儲存是以頁為儲存單元的,一個頁是8K8192Byte),一個頁就可以存放N行資料。我們表裡的資料都是存放在頁上的,這種叫資料頁。還有一種頁存放索引資料的,叫索引頁。

同時,頁也是IO讀取的最小單元(物理IO上不是按行讀取),也是所有權的最小單位。如果一頁中包含了表A的一行資料,這頁就只能儲存表A的行資料了。或是一頁中包含了索引B的條目,那這頁也僅僅只能儲存索引B的條目了。每頁中除去儲存資料之外,還儲存一些頁頭資訊以及行偏移以便SQL Server知道具體每一行在頁中的儲存位置。

資料庫的基本物理儲存單元是頁,一個表由很多個頁組成,那這些頁又是如何組織的呢?我們一般都會對錶建立索引,這些索引又是如何儲存的呢?不要走開,請看下文。

 /索引的儲存結構

如下圖,是一個B樹(二叉搜尋樹)的示例,都是小的元素放左邊,大的元素放右邊,依次構造的,比如要查詢元素9,從根節點開始,只要比較三次就找到他了,查詢效率是非常高的。

B+樹和B-樹都是B樹的變種,或者說是更加高階的複雜版本(關於B樹的資料,有興趣可以自己去學習,這裡只是拋磚引玉)。B+樹和B-樹是資料庫中廣泛應用的索引儲存結構,它可以極大的提高資料查詢的效率。前面說了資料庫儲存的基本單元是頁,因此,索引樹上的節點就是頁了。

因此不難看出,索引的主要優點和目的就是為了提高查詢效率。

 

為了保證資料的查詢效率,當新增、修改、刪除資料的時候,都需要維護這顆索引樹,就可能會出現分裂、合併節點(頁)的情況(這是樹的結構所決定的,想要更好理解這一點,可以嘗試自己程式碼實現一下

B-B+樹)。好!重點來了!

索引的缺點:

  • 當新增、修改、刪除資料的時候,需要維護索引樹,有一定的效能影響;
  • 同上面,在頻繁的樹維護過程中,B樹的頁拆分、合併會造成大量的索引碎片,又會極大的印象查詢效率 ,因此索引還需要維護;
  • 非聚集索引需要額外的儲存空間,不過這個一般問題都不是很大,但是需要注意的一個問題;

 聚集索引

聚集索引決定了表資料的物理儲存順序,也就是說表的物理儲存是根據聚集索引結構進行順序儲存的,因此一個表只能有一個聚集索引。如下圖,就是一個聚集索引的樹結構:

  • 所有資料都在葉子節點的頁上,在葉子節點(資料頁)之間有一個鏈指標,這是
    B+樹的特點;
  • 非葉子節點都是索引頁,儲存的就是聚集索引欄位的值;
  • 表的物理儲存就是依據聚集索引的結構的,一個表只能有一個聚集索引;

聚集索引的所有的資料都儲存在葉子節點上,資料查詢的複雜度都是一樣的(樹的深度),按照聚集索引列查詢資料效率是非常高的。上面說了,聚集索引決定了表的物理儲存結構,那如果沒有建立聚集索引,會如何呢?——表內的所有頁都無序存放,是一個無序的堆結構。堆資料的查詢就會造成表掃描,效能是非常低的。

因此聚集索引的的重要性不言而喻,一般來說,大多會對主鍵建立聚集索引,大多數普通情況這麼做也可以。但實際應用應該尊從一個原則就是頻繁使用的、排序的欄位上建立聚集索引

 非聚集索引

除了聚集索引以外的其他索引,都稱之為非聚集索引,非聚集索引一般都是為了優化特定的查詢效率而建立的。非聚集索引也是B樹(B+樹和B-樹)的結構,與非聚集索引的儲存結構唯一不一樣的,就是非聚集索引中不儲存真正的資料行,因為在聚集索引中已經存放了所有資料,非聚集索引只包含一個指向資料行的指標即可。

  • 非聚集索引的建立會單獨建立索引檔案來儲存索引結構,會佔用一定儲存空間,就是用空間換時間;
  • 非聚集索引的目的很單純:提高特定條件的查詢效率,一個表有可能根據多種查詢需求建立多個非聚集索引;

https://images.cnblogs.com/cnblogs_com/changbluesky/WindowsLiveWriter/SQLServer6IndexandTSQLTuning_DD36/image_4.png

資料查詢SQL簡單來看,分為兩個部分:SELECT****  Where ****,因此索引的建立也是根據這兩部分來決定的。根據這兩點,有兩種主要的索引形式:複合索引和覆蓋索引,在實際使用中,根據具體情況可能都會用到,只要能提高查詢效率就是好索引。

覆蓋索引:就是在索引中包含的資料列(非索引列,SELECT需要的列),這樣在使用該索引查詢資料時就不會再進行鍵查詢(也叫書籤查詢)了。

複合索引:主要針對Where中有多個條件的情況,索引包含多個數據列。在使用複合索引時,應注意多個索引鍵的順序問題,這個是會影響查詢效率的,一般的原則是唯一性高的放前面,還有就是SQl語句中Where條件的順序應該和索引順序一致。

 

 索引碎片

前面說過了,索引在使用一段時間後(主要是新增、修改、刪除資料,如果該頁已經儲存滿了,就要進行頁的拆分,頻繁的拆分,會產生較多的索引碎片)會產生索引碎片,這就造成了索引頁在磁碟上儲存的不連續。會造成磁碟的訪問使用的是隨機的i/o,而不是順序的i/o讀取,這樣訪問索引頁會變得更慢。如果碎片過多,資料庫是可會能不使用該索引的(她嫌棄你太慢了,資料庫會選擇一個更優的執行計劃)。

解決這個問題主要是兩種方法:

第一種是預防:設定頁的填充因子

意思就是在頁上設定一段空白區域,在新增資料的時候,可以使用這段空白區域,可以一定的避免頁的拆分,從而減少索引碎片的產生。

填充因子就是用來描述這種頁中填充資料的一個比例,一般預設是100%填充的。如果我們修改填充因子為80%,那麼頁在儲存資料時,就會剩餘20%的剩餘空間,這樣在下次插入的時候就不會拆分頁了。 那麼是不是我們可以把填充因子設定低一點,留更多的剩餘空間,不是很好嘛?當然也不好,填充因子設定的低,會需要分配更多的儲存空間,葉子節點的深度會增加,這樣是會影響查詢效率的,因此,這是要根據實際情況而定的。

那麼一般我們是怎麼設定填充因子的呢,主要根據表的讀寫比例而定的。如果讀的多,填充因子可以設定高一點,如100%,讀寫各一半,可以80~90%;修改多可以設定50~70%

第二種是索引修復:定期對索引進行檢查、維護,寫一段SQL檢查索引的碎片比例,如果碎片過多,進行碎片修復或重建,定期執行即可。具體可以參考本文末尾的相關參考資料。

https://images0.cnblogs.com/blog/151257/201308/13102931-26c04edc675741899452a9ab514d1072.png

 索引使用總結

  • 建立索引的的欄位儘量小,最好是數值,比如整形int等;
  • 對於頻繁修改的欄位,儘量不要建立索引,維護索引的成本很高,而且更容易產生索引碎片;
  • 定期的索引維護,如索引碎片的修復等;
  • 不要建立或維護不必要的重複索引,會增加修改資料(新增、修改、刪除資料)的成本;
  • 使用唯一性高的欄位建立索引,切不可在性別這樣的低唯一性的欄位上建立索引;
  • SQL語句中,儘量不要在Where條件中使用函式、運算子或表示式計算,會造成索引無法正常使用;
  • 應儘量避免在 where 子句中對欄位進行 null 值判斷,否則將導致引擎放棄使用索引而進行全表掃描;
  • 應儘量避免在 where 子句中使用!=<>操作符,否則將導致引擎放棄使用索引而進行全表掃描;

  事務與鎖

事務就是作為一個邏輯工作單元的SQL語句,如果任何一個語句操作失敗那麼整個操作就被失敗,以後操作就會回滾到操作前狀態,或者是上個節點。為了確保要麼執行,要麼不執行,就可以使用事務。而鎖是實現事務的關鍵,鎖可以保證事務的完整性和併發性。

這部分的理論性太強了,不如看看下文章末尾的參考資料更好(部落格園的高質量文章還是相當多的),簡單總結一下就好了!

和在.NET中的鎖用途類似,資料庫中的鎖也是為了解決在併發訪問時出現各種衝突的一種機制。

 

  題目答案解析:

1. 索引的作用?和它的優點缺點是什麼?

索引就一種特殊的查詢表,資料庫的搜尋引擎可以利用它加速對資料的檢索。索引很類似與現實生活中書的目錄,不需要查詢整本書內容就可以找到想要的資料。缺點是它減慢了資料錄入的速度,同時也增加了資料庫的尺寸大小。

2. 介紹儲存過程基本概念和 她的優缺點

儲存過程是一個預編譯的SQL語句,他的優點是允許模組化的設計,也就是說只需建立一次,在該程式中就可以呼叫多次。例如某次操作需要執行多次SQL,就可以把這個SQL做一個儲存過程,因為儲存過程是預編譯的,所以使用儲存過程比單純SQL語句執行要快。缺點是可移植性差,互動性差。

3. 使用索引有哪些需要注意的地方?

  • 建立索引的的欄位儘量小,最好是數值,比如整形int等;
  • 對於頻繁修改的欄位,儘量不要建立索引,維護索引的成本很高,而且更容易產生索引碎片;
  • 定期的索引維護,如索引碎片的修復等;
  • 不要建立或維護不必要的重複索引,會增加修改資料(新增、修改、刪除資料)的成本;
  • 使用唯一性高的欄位建立索引,切不可在性別這樣的低唯一性的欄位上建立索引;
  • SQL語句中,儘量不要在Where條件中使用函式、運算子或表示式計算,會造成索引無法正常使用;
  • 應儘量避免在 where 子句中對欄位進行 null 值判斷,否則將導致引擎放棄使用索引而進行全表掃描;
  • 應儘量避免在 where 子句中使用!=<>操作符,否則將導致引擎放棄使用索引而進行全表掃描;

4. 索引碎片是如何產生的?有什麼危害?又該如何處理?

索引在使用一段時間後(主要是新增、修改、刪除資料,如果該頁已經儲存滿了,就要進行頁的拆分,頻繁的拆分,會產生較多的索引碎片)會產生索引碎片。

索引碎片會嚴重印象資料的查詢效率,如果碎片太多,索引可能不會被使用。

碎片的處理方式主要有兩種:

第一種是預防:設定頁的填充因子

意思就是在頁上設定一段空白區域,在新增資料的時候,可以使用這段空白區域,可以一定的避免頁的拆分,從而減少索引碎片的產生。

填充因子就是用來描述這種頁中填充資料的一個比例,一般預設是100%填充的。如果我們修改填充因子為80%,那麼頁在儲存資料時,就會剩餘20%的剩餘空間,這樣在下次插入的時候就不會拆分頁了。 那麼是不是我們可以把填充因子設定低一點,留更多的剩餘空間,不是很好嘛?當然也不好,填充因子設定的低,會需要分配更多的儲存空間,葉子節點的深度會增加,這樣是會影響查詢效率的,因此,這是要根據實際情況而定的。

那麼一般我們是怎麼設定填充因子的呢,主要根據表的讀寫比例而定的。如果讀的多,填充因子可以設定高一點,如100%,讀寫各一半,可以80~90%;修改多可以設定50~70%

第二種是索引修復:定期對索引進行檢查、維護,寫一段SQL檢查索引的碎片比例,如果碎片過多,進行碎片修復或重建,定期執行即可。具體可以參考本文末尾的相關參考資料。

5. 鎖的目的是什麼?

主要解決多個使用者同時對資料庫的併發操作時會帶來以下資料不一致的問題:

  • 丟失更新,同時修改一條資料
  • 讀髒,A修改了資料後,B讀取後A又取消了修改,B讀髒
  • 不可重複讀,A使用者讀取資料,隨後B使用者讀取該資料並修改,此時A使用者再讀取資料時發現前後兩次的值不一致
  • 還有一種是幻讀,這個情況好像不多。

併發控制的主要方法是封鎖,鎖就是在一段時間內禁止使用者做某些操作以避免產生資料不一致

6. 鎖的粒度有哪些?

  • 資料庫鎖:鎖定整個資料庫,這通常發生在整個資料庫模式改變的時候。
  • 表鎖:鎖定整個表,這包含了與該表相關聯的所有資料相關的物件,包括實際的資料行(每一行)以及與該表相關聯的所有索引中的鍵。
  • 區段鎖:鎖定整個區段,因為一個區段由8頁組成,所以區段鎖定是指鎖定控制了區段、控制了該區段內8個數據或索引頁以及這8頁中的所有資料行。
  • 頁鎖:鎖定該頁中的所有資料或索引鍵。
  • 行或行識別符號:雖然從技術上將,鎖是放在行識別符號上的,但是本質上,它鎖定了整個資料行。

7. 什麼是事務?什麼是鎖?

事務就是被繫結在一起作為一個邏輯工作單元的SQL語句分組,如果任何一個語句操作失敗那麼整個操作就被失敗,以後操作就會回滾到操作前狀態,或者是上個節點。為了確保要麼執行,要麼不執行,就可以使用事務。要將所有組語句作為事務考慮,就需要通過ACID測試,即原子性,一致性,隔離性和永續性。 
鎖是實現事務的關鍵,鎖可以保證事務的完整性和併發性。

8. 檢視的作用,檢視可以更改麼?

檢視是虛擬的表,與包含資料的表不一樣,檢視只包含使用時動態檢索資料的查詢;不包含任何列或資料。使用檢視可以簡化複雜的sql操作,隱藏具體的細節,保護資料;檢視建立後,可以使用與表相同的方式利用它們。

檢視的目的在於簡化檢索,保護資料,並不用於更新。

9. 什麼是觸發器(trigger)? 觸發器有什麼作用?

觸發器是資料庫中由一定時間觸發的特殊的儲存過程,他不是由程式掉用也不是手工啟動的。觸發器的執行可以由對一個表的insert,delete, update等操作來觸發,觸發器經常用於加強資料的完整性約束和業務規則等等。

10. SQL裡面IN比較快還是EXISTS比較快?

這個題不能一概而論,要根據具體情況來看。IN適合於外表大而內表小的情況;EXISTS適合於外表小而內表大的情況。

如果查詢語句使用了not in,那麼對內外表都進行全表掃描,沒有用到索引;而not exists的子查詢依然能用到表上的索引。所以無論哪個表大,用not exists都比not in 要快。參考資料:http://www.cnblogs.com/seasons1987/archive/2013/07/03/3169356.html

11. 維護資料庫的完整性和一致性,你喜歡用觸發器還是自寫業務邏輯?為什麼?

儘可能使用約束,如check、主鍵、外來鍵、非空欄位等來約束。這樣做效率最高,也最方便。其次是使用觸發器,這種方法可以保證,無論什麼業務系統訪問資料庫都可以保證資料的完整新和一致性。最後考慮的是自寫業務邏輯,但這樣做麻煩,程式設計複雜,效率低下。

文章來源:http://www.cnblogs.com/anding

自己總結:

!= <> 都是不等於 用法一致 不包含空值

交叉聯接返回左表中的所有行,左表中的每一行與右表中的所有行組合。交叉聯接也稱作笛卡爾積。 

如果不加任何限制條件,將得到的是資料的笛卡兒積

 

INNER JOIN 內連線=join   隱式  用比較運算子號  a.i=b.id

Left join 左連線 

Right join 右連線

Full join全連線

Cross join 交叉連線   隱式 a.id=1

 

SQL LIKE子句使用萬用字元運算子比較相似的值。:

百分號 (%):百分號代表零個,一個或多個字元

下劃線 (_):下劃線表示單個數字或字元

查詢以 "g" 開始的: like ‘g%’

查詢以 "g" 結尾的: like ‘%g’

第一個字元之後是 "eorge" 的人: LIKE '_eorge'

"C" 開頭,然後是一個任意字元,然後是 "r",然後是任意字元,然後是 "er":

LIKE 'C_r_er'

以 "A" 或 "L" 或 "N" 開頭的人:: '[ALN]%'

不以 "A" 或 "L" 或 "N" 開頭的人: LIKE '[!ALN]%'

 

 Sqlserver不支援limit

 

視窗函式OVER()指定一組行,開窗函式計算從視窗函式輸出的結果集中各行的值。

語法結構:排名函式 ( ) OVER ( [ <partition_by子句> ] <order_by子句> ) 

ROW_NUMBER():為每一組的行記錄按順序生成一個唯一的行號。

查詢各科成績前2名的記錄

  1. 先從科目倒序 再成績倒序
  2. 子查詢查詢各科前兩名的人

 

什麼是索引?

索引是對資料庫表中一列或多列的值進行排序的一種結構,使用索引可快速訪問資料庫表中的特定資訊。

舉個例子,索引就像我們查字典時用的按拼音或筆畫或偏旁部首

 

索引的儲存是以B+樹(注意和二叉樹的區別)結構來儲存的,又稱索引樹

  • 索引節點;
  • 葉子節點

  索引節點按照層級關係,有時又可以分為根節點和中間節點,其本質是一樣的,都只包含下一層節點的入口值和入口指標;

葉子節點就不同了,它包含資料,這個資料可能是表中真實的資料行,也有可能是索引列值和行書籤,前者對應於聚集索引,後者對應於非聚集索引。

 

 

 

 

聚集索引

根節點包含下一層中間節點的節點指標(頁號),節點值(主鍵值)

中間節點包含下一層頁節點的節點指標(頁號),節點值(主鍵值)

總結:

聚集索引的根節點和中間節點是索引頁,都只包含下一層的入口指標和入口值(位於儲存位置的第一個主鍵值);

聚集索引的葉節點就是資料頁。

 

 

 

資料頁裡面的資料按照主鍵值順序存放

 

總結:

聚集索引就是把資料按主鍵順序儲存;

因為一張表中的資料只能有一個物理順序,所以一張表只能有一個主鍵/聚集索引。

非聚集索引

 

根節點包含下一層中間節點的頁號,非聚集索引的鍵值以及聚集索引的鍵值

中間頁節點同樣包含了下一層(葉節點)的頁號以及聚集、非聚集鍵值

葉節點包含聚集、非聚集索引鍵值以及一個KeyHasValue

 

總結:

  • 非聚集索引的根節點和中間節點是索引頁,都只含下一層級的入口指標和入口值(位於儲存位置的第一個鍵值);
  • 非聚集索引的葉節點也是索引頁,也儲存有聚集索引和非聚集索引的鍵值;
  • 非聚集索引中的每個索引行(不論是根節點、中間節點還是葉節點)都包含非聚集鍵值和行定位符(本例為聚集索引鍵值),此定位符指向聚集索引或堆(沒有聚集索引的表)中包含該鍵值的資料行。

  非聚集索引行中的行定位器可以是指向行的指標,也可以是行的聚集索引鍵,具體根據如下情況而定:

  • 如果表是堆(意味著該表沒有聚集索引),則行定位器是指向行的指標。該指標由檔案識別符號(ID)、頁碼和頁上的行數生成。整個指標稱為行ID (RID)
  • 如果表有聚集索引或索引檢視上有聚集索引,則行定位器是行的聚集索引鍵(本例即為EmployeeId)。如果聚集索引不是唯一的索引,SQL Server將新增在內部生成的值(稱為唯一值)以使所有重複鍵唯一。此四位元組的值對於使用者不可見。僅當需要使聚集鍵唯一以用於非聚集索引中時,才新增該值。SQL Server通過使用儲存在非聚集索引的葉行內的聚集索引鍵搜尋聚集索引來檢索資料行。

 

非聚集索引作用:

非聚集索引一般都是為了優化特定的查詢效率

索引覆蓋

  • 索引覆蓋和非聚集索引的根節點和中間節點一樣,都是索引頁,都只包含下一層的入口指標和入口值。
  • 索引覆蓋的葉節點卻稍有不同,多了一列DepartmentCode,此列即為索引覆蓋列,而且此列只在葉節點出現,如果查詢時,只需返回鍵值列和索引覆蓋列,則只需索引查詢,肯本無需訪問資料頁,不僅提高了效能,而且節省佔用空間。

 

 

如果表有聚集索引(區段結構),那麼書籤就是從非聚集索引找到聚集索引後,利用聚集索引定位到資料。此處的書籤就是聚集索引。如果表沒有聚集索引(堆結構)。那麼掃描非聚集索引後,通過RID定位到資料,那麼此處書籤就是RID

  所謂的書籤查詢,就是通過聚集索引,然後利用聚集索引或RID定位到資料。

書籤查詢(RID查詢、鍵查詢)

 

覆蓋索引又可以稱為索引覆蓋。
  解釋一: 就是select的資料列只用從索引中就能夠取得,不必從資料表中讀取,換句話說查詢列要被所使用的索引覆蓋。
  解釋二: 索引是高效找到行的一個方法,當能通過檢索索引就可以讀取想要的資料,那就不需要再到資料表中讀取行了。如果一個索引包含了(或覆蓋了)滿足查詢語句中欄位與條件的資料就叫做覆蓋索引。
  解釋三: 是非聚集組合索引的一種形式,它包括在查詢裡的SelectJoinWhere子句用到的所有列(即建立索引的欄位正好是覆蓋查詢語句[select子句]與查詢條件[Where子句]中所涉及的欄位,也即,索引包含了查詢正在查詢的所有資料)

 

 

 

BEGIN TRAN:設定起始點。

COMMIT TRAN:使事務成為資料庫中永久的、不可逆轉的一部分。

ROLLBACK TRAN:本質上說想要忘記它曾經發生過。

SAVE TRAN:建立一個特定標記符,只允許部分回滾。

begin catch

end catch 異常

 

B的欄位是從表A獲取,表A的欄位改變了,表B的相應欄位也需要改變,這就叫一致性

 

避免事務中的使用者互動:儘量不要在事務中要求使用者響應,因為事務持有的任何鎖只有在事務提交或回滾後才會釋放,等待使用者響應的時間,容易導致阻塞或死鎖。

由一個基表定義的檢視,只含有基表的主鍵或候補鍵,並且檢視中沒有用表示式或函式定義的屬性,才允許更新。