SqlServer數據庫索引 學習記錄

什麼是索引?

數據庫中的索引是:
某個表中一列 或 多列值的集合和相應的指向表中物理標識這些值的數據頁的 邏輯指針清單。

數據庫索引有哪幾種?

聚集索引:
一張表只能有一個聚集索引,通常主鍵默認是聚集索引,也可以設置非主鍵列爲聚集索引,設置聚集索引後,數據存儲將按照索引列有規則的排列。

非聚集索引(NONCLUSTERED):
一張表可有多個,表中各行的物理順序與鍵值的邏輯(索引)順序不匹配。



單表索引的數目一般不要超過3個,最多不要超過5個。
一張表最好不要超過5個索引,一個索引最多有16個索引列。
每當表數據變化(增、刪、改)的時候,每個索引都需要按照規則更新索引位置。



創建索引的標準:
用語頻繁搜索的列;用語對數據進行排序的列。

注意:如果表中僅有幾行,或列中只包含幾個不同的值,不推薦創建索引,因爲SQL Server 在小型表中用索引搜索數據所花的時間比逐行搜索更長。

哪些情況不要使用索引?

1)重複值較多的列不要建立索引,比如用戶表的性別字段,最多三種值(男、女、未知)。
2)text、image、byte[]列 不要建立索引
3)更新頻繁的列不要建立索引,因爲如果該列有索引,那麼在更新值的時候同時需要更新索引,這樣會導致查詢變快,更新變慢。

索引的一些使用規則?

查詢網址(包含一些數據庫性能優化):https://www.cnblogs.com/lihuali/p/5899255.html

這裏記錄一些自己學習的知識點。

1.
比如like '張%' 就是符合SARG(符合掃描參數)標準,而like '%張' 就不符合該標準。

通配符 % 在字符串首字符的使用會導致 索引無法使用,雖然實際應用中很難避免這樣用,但還是應該對這種現象有所瞭解,至少知道此種用法性能是很低下的。

2.
不同類型的索引效能是不一樣的,應儘可能先使用效能高的。

比如:(數字類型的索引查找效率)高於(字符串類型),定長字符串char,nchar的索引效率高於變長字符串varchar,nvarchar的索引。

應該將
where username='張三' and age>20
改進爲
where age>20 and username='張三'

注意:此處,SQL的查詢分析優化功能可以做到自動重排條件順序,但還是建議預先手工排列好。

3.
例:表stuff 有 200000行,id_no上有非羣集索引,請看下面這個SQL:
select count(*) from stuff where id_no in(′0′,′1′) (23秒)

我們期望它會根據每個or子句分別查找,再將結果相加,這樣可以利用id_no上的索引;

但實際上,它卻採用了"OR策略",即先取出滿足每個or子句的 行,存入臨時數據庫的工作表中,再建立唯一索引以去掉重複行,最後從這個臨時表中計算結果。

因此,實際過程沒有利用id_no 上索引,並且完成時間還要 受tempdb數據庫性能的影響。

實踐證明,表的行數越多,工作表的性能就越差,當stuff有620000行時,執行時間會非常長!如果確定不同的條件不會產生大量重複值,還不如將or子句分開:

select count(*) from stuff where id_no=′0′
select count(*) from stuff where id_no=′1′

得到兩個結果,再用union作一次加法合算。因爲每句都使用了索引,執行時間會比較短,

select count(*) from stuff where id_no=′0′
union
select count(*) from stuff where id_no=′1′

從實踐效果來看,使用union在通常情況下比用or的效率要高的多,而exist關鍵字和in關鍵字在用法上類似,性能上也類似,都會產生全表掃描,效率比較低下,根據未經驗證的說法,exist可能比in要快些。

like關鍵字支持通配符匹配,但這種匹配特別耗時。
例如:select * from customer where zipcode like “21_ _ _”,即使在zipcode字段上已建立了索 引,在這種情況下也可能還是採用全表掃描方式。
如果把語句改 爲:select * from customer where zipcode >“21000”,在執行查詢時就會利用索引,大大提高速度。但這種變通是有限制的,不應引起業務意義上的損失,對於郵政編碼而言,zipcode like “21_ _ _” 和 zipcode >“21000” 意義是完全一致的。

4.
order by按 聚集索引列 排序效率最高
排序是較耗時的操作,應儘量簡化或避免對大型表進行排序,如縮小排序的列的範圍,只在有索引的列上排序等等。
我們來看:(gid是主鍵,fariqi是聚合索引列)
select top 10000 gid,fariqi,reader,title from tgongwen
用時:196 毫秒。 掃描計數 1,邏輯讀 289 次,物理讀 1 次,預讀 1527 次。

select top 10000 gid,fariqi,reader,title from tgongwen order by gid asc
用時:4720毫秒。 掃描計數 1,邏輯讀 41956 次,物理讀 0 次,預讀 1287 次。

select top 10000 gid,fariqi,reader,title from tgongwen order by gid desc
用時:4736毫秒。 掃描計數 1,邏輯讀 55350 次,物理讀 10 次,預讀 775 次。

select top 10000 gid,fariqi,reader,title from tgongwen order by fariqi asc
用時:173毫秒。 掃描計數 1,邏輯讀 290 次,物理讀 0 次,預讀 0 次。

select top 10000 gid,fariqi,reader,title from tgongwen order by fariqi desc
用時:156毫秒。 掃描計數 1,邏輯讀 289 次,物理讀 0 次,預讀 0 次。

同時,按照某個字段進行排序的時候,無論是正序還是倒序,速度是基本相當的。

5.

節省數據查詢系統開銷方面的措施?

(1)使用TOP儘量減少取出的數據量。

(2)字段提取要按照“需多少、提多少”的原則,避免“select *”
字段大小越大,數目越多,select所耗費的資源就越多,比如取int類型的字段就會比取char的快很多。
我們每少提取一個字段,數據的提取速度就會有相應的提升。提升的幅度根據捨棄的字段的大小來判斷

(3)count(*) 與 count(字段) 方法比較
用 count(*)和用 count(主鍵)的速度是相當的,而count(*)卻比其他任何除主鍵以外的字段彙總速度要快,而且字段越長,彙總速度就越慢。
如果用 count(*), SQL SERVER會自動查找最小字段來彙總。當然,如果您直接寫count(主鍵)將會來的更直接些。

(4)有嵌套查詢時,儘可能在內層過濾掉數據
如果一個列同時在主查詢和where子句中出現,很可能當主查詢中的列值改變之後,子查詢必須重新查詢一次。
而且查詢嵌套層次越多,效率越低,因此應當儘量避免子查詢。如果子查詢不可避免,那麼要在子查詢中過濾掉儘可能多的行

(5)多表關聯查詢時,需注意表順序,並儘可能早的過濾掉數據
在使用Join進行多表關聯查詢時候,應該使用系統開銷最小的方案。連接條件要充份考慮帶有索引的表、行數多的表,並注意優化表順序;說的簡單一點,就是儘可能早的將之後要做關聯的數據量降下來。

Sql語句集中:

--索引操作學習網址:https://www.cnblogs.com/knowledgesea/p/3672099.html

--建一張無主鍵表B
--CREATE TABLE B(
--			Id			varchar(36) NOT NULL,
--			IndexCol1	varchar(50) NULL,
--			IndexCol2	varchar(50) NULL,
--			IndexCol3	varchar(50) NULL
--)

--設置表B的主鍵爲Id,並設置主鍵列爲聚集索引 PK_ID
--ALTER TABLE B ADD CONSTRAINT PK_ID PRIMARY KEY(Id)

--設置B表的IndexCol1字段列爲非聚集索引 non_index_col1
--CREATE NONCLUSTERED INDEX non_index_col1 ON B(IndexCol1)

--查看索引 non_index_col1 是否存在(其它表可能也有叫這個名字的索引)
--SELECT * FROM SYSINDEXES WHERE NAME='non_index_col1'
--IF EXISTS (SELECT * FROM SYSINDEXES WHERE NAME='non_index_col1')
--PRINT '存在'

--插入5條數據
--INSERT INTO B(Id,IndexCol1,IndexCol2,IndexCol3) VALUES
--(NEWID(), '第1行第1列', '第1行第2列', '第1行第3列'),
--(NEWID(), '第2行第1列', '第2行第2列', '第2行第3列'),
--(NEWID(), '第3行第1列', '第3行第2列', '第3行第3列'),
--(NEWID(), '第4行第1列', '第4行第2列', '第4行第3列'),
--(NEWID(), '第5行第1列', '第5行第2列', '第5行第3列')

--刪除2行記錄
--DELETE FROM B WHERE
--	IndexCol1='第2行第1列' OR
--	IndexCol1='第4行第1列'

--查看錶的索引情況,當碎片顯示比較高時,就可以重建索引,消除碎片了
--本例插入,刪除的記錄不多,所以碎片率很低
--DBCC showcontig('B')

--重建索引
--DBCC DBREINDEX('B')

--創建好索引後,普通查詢語句 默認按IndexCol1列對應的索引non_index_col1進行查詢。
--SELECT * FROM B WHERE IndexCol1='第1行第1列'
--Go


 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章