數(shù)據(jù)庫(kù)實(shí)驗(yàn)總結(jié)【一】
試驗(yàn)內(nèi)容
1、 數(shù)據(jù)表的建立
基本表《簡(jiǎn)單的》帶有主鍵
帶有外碼約束的(外碼來(lái)自其他表或者本表)
2、 數(shù)據(jù)表的修改
添加刪除列
修改列屬性類型
添加刪除約束(約束名)
元組的添加,修改,刪除
刪除數(shù)據(jù)表
試驗(yàn)過(guò)程
1、create table student
(
sno char(9) primary key , /*sno是主碼 列級(jí)完整性約束條件*/
sname char(20) unique, /*sname取唯一值*/
ssex char(2),
sage smallint, /*類型為smallint*/
sdept char(20) /*所在系*/
);
create table course
(
cno char(4) primary key, /*列級(jí)完整性約束條件,cno是主碼*/
cname char(40),
cpno char(4), /*cpno的含義是先行課*/
ccredit smallint,
foreign key (cpno) references course(cno)
/*表級(jí)完整性約束條件,cpno是外碼,被參照表是course,被參照列是cno*/
);
create table sc
(
sno char(9),
cno char(4),
grade smallint,
primary key (sno,cno),
/*主碼有兩個(gè)屬性構(gòu)成,必須作為表級(jí)完整性進(jìn)行定義*/
foreign key (sno) references student(sno),
/*表級(jí)完整性約束條件,sno是外碼,被參照表是student*/
foreign key (cno) references course(cno),
/*表級(jí)完整性約束條件,cno是外碼,被參照表示course*/
);
例1、create table s
(
cno varchar(3), /*變長(zhǎng)的字符串,輸入2個(gè)字符就是兩個(gè)字符不會(huì)補(bǔ)空格*/
sname varchar(20),
status int,
city varchar(20),
constraint pk_sno primary key(sno), /*約束條件的名字為pk_sno*/
);
create table p
(
pno varchar(3),
pname varchar(20),
color varchar(3),
weight int,
constraint pk_pno primary key (pno), /*約束條件的名字是pk_pno*/
);
create table j
(
jno varchar(3),
jname varchar(20),
city varchar(20),
constraint pk_jno primary key(jno) /*約束條件的名字為pk_jno*/
);
例2、create table spj
(
sno varchar(3), /*第一個(gè)表中的主碼*/
pno varchar(3),
jno varchar(3),
qty int, /*數(shù)量*/
constraint pk_spj primary key(sno,pno,jno), /*主碼由3個(gè)屬性組成*/
foreign key(sno) references s(sno),
/*表級(jí)完整性約束條件,sno是外碼,被參照表是s*/
foreign key(pno) references p(pno),
/*表級(jí)完整性約束條件,pno是外碼,被參照表是p*/
foreign key(jno) references j(jno),
/*表級(jí)完整性約束條件,jno是外碼,被參照表是j*/
);
2、數(shù)據(jù)表的更改
在s表中添加一個(gè)concat 列
alter table s add concat varchar(20)
在s表中刪除concat 列
alter table s drop column concat
更改s表 concat列的屬性 把長(zhǎng)度由20改為30
alter table s alter column concat varchar(30)
聯(lián)系方式 名字為concat 修改屬性為唯一的 屬性名為con_concat
alter table s add constraint con_concat unique(concat)
刪除約束關(guān)系con_concat
alter table s drop constraint con_concat
/*插入一個(gè)元組*/
insert into s valus(‘s1’,’精益’,20,’天津’) /*20不能寫(xiě)成’20’*/
試驗(yàn)中的問(wèn)題的排除與總結(jié):
1、在創(chuàng)建spj時(shí)
有三個(gè)實(shí)體所以從3個(gè)實(shí)體中取主碼,還有一個(gè)數(shù)量屬性也要寫(xiě)上
主碼由那3個(gè)主碼確定
2、更改一個(gè)數(shù)據(jù)庫(kù)中數(shù)據(jù)表時(shí)一定要先使該數(shù)據(jù)庫(kù)處于正在使用狀態(tài)
3、constraint
是可選關(guān)鍵字,表示 primary key、not null、unique、foreign key 或 check 約束定義的開(kāi)始。約束是特殊屬性,用于強(qiáng)制數(shù)據(jù)完整性并可以為表及其列創(chuàng)建索引。
4、--go可以不加但是要注意順序 注:go --注釋 提示錯(cuò)誤
5、注意添加一個(gè)空元素用 null
附 sql備份
--創(chuàng)建一個(gè)數(shù)據(jù)庫(kù) student
create database student
go
--在數(shù)據(jù)庫(kù)student中創(chuàng)建表student course sc 注意順序
use student
----------------------------------------------------------------
create table student
(
sno char(9) primary key, /*sno是主碼 列級(jí)完整性約束條件*/
sname char(10) unique, /*sname取唯一值*/
ssex char(2),
sage smallint, /*類型為smallint*/
sdept char(20) /*所在系*/
); /*;要加*/
-----------
數(shù)據(jù)庫(kù)實(shí)驗(yàn)總結(jié)【二】
我在sql server 索引基礎(chǔ)知識(shí)系列中,第一篇就講了記錄數(shù)據(jù)的基本格式。那里主要講解的是,數(shù)據(jù)庫(kù)的最小讀存單元:數(shù)據(jù)頁(yè)。一個(gè)數(shù)據(jù)頁(yè)是8k大小。
對(duì)于數(shù)據(jù)庫(kù)來(lái)說(shuō),它不會(huì)每次有一個(gè)數(shù)據(jù)頁(yè)變化后,就存到硬盤。而是變化達(dá)到一定數(shù)量級(jí)后才會(huì)作這個(gè)操作。 這時(shí)候,數(shù)據(jù)庫(kù)并不是以數(shù)據(jù)頁(yè)來(lái)作為操作單元,而是以64k的數(shù)據(jù)(8個(gè)數(shù)據(jù)頁(yè),一個(gè)區(qū))作為操作單元。
區(qū)是管理空間的基本單位。一個(gè)區(qū)是八個(gè)物理上連續(xù)的頁(yè)(即 64 kb)。這意味著 sql server 數(shù)據(jù)庫(kù)中每 mb 有 16 個(gè)區(qū)。
為了使空間分配更有效,sql server 不會(huì)將所有區(qū)分配給包含少量數(shù)據(jù)的表。sql server 有兩種類型的區(qū):
統(tǒng)一區(qū),由單個(gè)對(duì)象所有。區(qū)中的所有 8 頁(yè)只能由所屬對(duì)象使用。
混合區(qū),最多可由八個(gè)對(duì)象共享。區(qū)中八頁(yè)的每頁(yè)可由不同的對(duì)象所有。
通常從混合區(qū)向新表或索引分配頁(yè)。當(dāng)表或索引增長(zhǎng)到 8 頁(yè)時(shí),將變成使用統(tǒng)一區(qū)進(jìn)行后續(xù)分配。如果對(duì)現(xiàn)有表創(chuàng)建索引,并且該表包含的行足以在索引中生成 8 頁(yè),則對(duì)該索引的所有分配都使用統(tǒng)一區(qū)進(jìn)行。
為何會(huì)這樣呢?
其實(shí)很簡(jiǎn)單:
讀或?qū)?8kb 的時(shí)間與讀或?qū)?64 kb的時(shí)間幾乎相同。
在 8 kb 到 64 kb 范圍之內(nèi),單個(gè)磁盤 i/o 傳輸操作所花的時(shí)間主要是磁盤取數(shù)臂和讀/寫(xiě)磁頭運(yùn)動(dòng)的時(shí)間。
因此,從數(shù)學(xué)上來(lái)講,當(dāng)需要傳輸 64 kb 以上的 sql 數(shù)據(jù)時(shí),
盡可能地執(zhí)行 64 kb 磁盤傳輸是有益的,即分成數(shù)個(gè)64k的操作。
因?yàn)?64 kb 傳輸基本上與 8 kb 傳輸一樣快,而每次傳輸?shù)?sql server 數(shù)據(jù)是 8 kb 傳輸?shù)?8 倍。
我們通過(guò)一個(gè)實(shí)例來(lái)看 有and 操作符時(shí)候的最常見(jiàn)的一種情況。我們有下面一個(gè)表,
create table [dbo].[member]( [member_no] [dbo].[numeric_id] identity(1,1) not null, [lastname] [dbo].[shortstring] not null, [firstname] [dbo].[shortstring] not null, [middleinitial] [dbo].[letter] null, [street] [dbo].[shortstring] not null, [city] [dbo].[shortstring] not null, [state_prov] [dbo].[statecode] not null, [country] [dbo].[countrycode] not null, [mail_code] [dbo].[mailcode] not null, [phone_no] [dbo].[phonenumber] null, [photograph] [image] null, [issue_dt] [datetime] not null default (getdate()), [expr_dt] [datetime] not null default (dateadd(year,1,getdate())), [region_no] [dbo].[numeric_id] not null, [corp_no] [dbo].[numeric_id] null, [prev_balance] [money] null default (0), [curr_balance] [money] null default (0), [member_code] [dbo].[status_code] not null default (' '))
這個(gè)表具備下面的四個(gè)索引:
索引名 細(xì)節(jié) 索引的列
member_corporation_link nonclustered located on primary corp_no
member_ident clustered, unique, primary key located on primary member_no
member_region_link nonclustered located on primary region_no
memberfirstname nonclustered located on primary firstname
當(dāng)我們執(zhí)行下面的sql查詢時(shí)候,
select m.member_no, m.firstname, m.region_nofrom dbo.member as mwhere m.firstname like 'k%' and m.region_no > 6 and m.member_no < 5000go
sql server 會(huì)根據(jù)索引方式,優(yōu)化成下面方式來(lái)執(zhí)行。
select a.member_no,a.firstname,b.region_nofrom(select m.member_no, m.firstname from dbo.member as m where m.firstname like 'k%' and m.member_no < 5000) a , -- 這個(gè)查詢可以直接使用 memberfirstname 非聚集索引,而且這個(gè)非聚集索引覆蓋了所有查詢列-- 實(shí)際執(zhí)行時(shí),只需要 邏輯讀取 3 次
(select m.member_no, m.region_no from dbo.member as mwhere m.region_no > 6) b
-- 這個(gè)查詢可以直接使用 member_region_link 非聚集索引,而且這個(gè)非聚集索引覆蓋了所有查詢列-- 實(shí)際執(zhí)行時(shí),只需要 邏輯讀取 10 次
where a.member_no = b.member_no
不信,你可以看這兩個(gè)sql 的執(zhí)行計(jì)劃,以及邏輯讀信息,都是一樣的。
其實(shí)上面的sql,如果優(yōu)化成下面的方式,實(shí)際的邏輯讀消耗也是一樣的。為何sql server 不會(huì)優(yōu)化成下面的方式。是因?yàn)?and 操作符優(yōu)化的另外一個(gè)原則。
1/26 的數(shù)據(jù)和 1/6 的數(shù)據(jù)找交集的速度要比 1/52 的數(shù)據(jù)和 1/3 的數(shù)據(jù)找交集速度要慢。
select a.member_no,a.firstname,b.region_nofrom(select m.member_no, m.firstname from dbo.member as mwhere m.firstname like 'k%' -- 1/26 數(shù)據(jù)) a,
(select m.member_no, m.region_no from dbo.member as mwhere m.region_no > 6 and m.member_no < 5000-- 1/3 * 1/ 2 數(shù)據(jù)) bwhere a.member_no = b.member_no
當(dāng)然,我們要學(xué)習(xí)sql 如何優(yōu)化的話,就會(huì)用到查詢語(yǔ)句中的一個(gè)功能,指定查詢使用哪個(gè)索引來(lái)進(jìn)行。
比如下面的查詢語(yǔ)句
select m.member_no, m.firstname, m.region_nofrom dbo.member as m with (index (0))where m.firstname like 'k%' and m.region_no > 6 and m.member_no < 5000go
select m.member_no, m.firstname, m.region_nofrom dbo.member as m with (index (1))where m.firstname like 'k%' and m.region_no > 6 and m.member_no < 5000goselect m.member_no, m.firstname, m.region_nofrom dbo.member as m with (index (membercovering3))where m.firstname like 'k%' and m.region_no > 6 and m.member_no < 5000goselect m.member_no, m.firstname, m.region_nofrom dbo.member as m with (index (memberfirstname, member_region_link))where m.firstname like 'k%' and m.region_no > 6 and m.member_no < 5000go
這里 index 計(jì)算符可以是 0 ,1, 指定的一個(gè)或者多個(gè)索引名字。對(duì)于 0 ,1 的意義如下:
如果存在聚集索引,則 index(0) 強(qiáng)制執(zhí)行聚集索引掃描,index(1) 強(qiáng)制執(zhí)行聚集索引掃描或查找(使用性能最高的一種)。
如果不存在聚集索引,則 index(0) 強(qiáng)制執(zhí)行表掃描,index(1) 被解釋為錯(cuò)誤。
總結(jié)知識(shí)點(diǎn):
簡(jiǎn)單來(lái)說(shuō),我們可以這么理解:sql server 對(duì)于每一條查詢語(yǔ)句。會(huì)根據(jù)實(shí)際索引情況(sysindexes 系統(tǒng)表中存儲(chǔ)這些信息),分析每種組合可能的成本。然后選擇它認(rèn)為成本最小的一種。作為它實(shí)際執(zhí)行的計(jì)劃。
成本代價(jià)計(jì)算的一個(gè)主要組成部分是邏輯i/o的數(shù)量,特別是對(duì)于單表的查詢。
and 操作要滿足所有條件,這樣,經(jīng)常會(huì)要求對(duì)幾個(gè)數(shù)據(jù)集作交集。數(shù)據(jù)集越小,數(shù)據(jù)集的交集計(jì)算越節(jié)省成本。
的項(xiàng)目中,竟然出現(xiàn)了濫用聚集索引的問(wèn)題??磥?lái)沒(méi)有培訓(xùn)最最基礎(chǔ)的索引的意義,代價(jià),使用場(chǎng)景,是一個(gè)非常大的失誤。這篇博客就是從這個(gè)角度來(lái)羅列索引的基礎(chǔ)知識(shí)。
使用索引的意義
索引在數(shù)據(jù)庫(kù)中的作用類似于目錄在書(shū)籍中的作用,用來(lái)提高查找信息的速度。
使用索引查找數(shù)據(jù),無(wú)需對(duì)整表進(jìn)行掃描,可以快速找到所需數(shù)據(jù)。
使用索引的代價(jià)
索引需要占用數(shù)據(jù)表以外的物理存儲(chǔ)空間。
創(chuàng)建索引和維護(hù)索引要花費(fèi)一定的時(shí)間。
當(dāng)對(duì)表進(jìn)行更新操作時(shí),索引需要被重建,這樣降低了數(shù)據(jù)的維護(hù)速度。
創(chuàng)建索引的列
主鍵
外鍵或在表聯(lián)接操作中經(jīng)常用到的列
在經(jīng)常查詢的字段上最好建立索引
不創(chuàng)建索引的列
很少在查詢中被引用
包含較少的惟一值
定義為 text、ntext 或者 image 數(shù)據(jù)類型的列
heaps是staging data的很好選擇,當(dāng)它沒(méi)有任何index時(shí)
excellent for high performance data loading (parallel bulk load and parallel index creation after load)
excellent as a partition to a partitioned view or a partitioned table
聚集索引提高性能的方法,在前面幾篇博客中分別提到過(guò),下面只是一個(gè)簡(jiǎn)單的大綱,細(xì)節(jié)請(qǐng)參看前面幾篇博客。
何時(shí)創(chuàng)建聚集索引?
clustered index會(huì)提高大多數(shù)table的性能,尤其是當(dāng)它滿足以下條件時(shí):
獨(dú)特, 狹窄, 靜止: 最重要的條件
持續(xù)增長(zhǎng)的,最好是只向上增加。例如:
identity
date, identity
guid (only when using newsequentialid() function)
聚集索引唯一性(獨(dú)特型的問(wèn)題)
由于聚集索引的b+樹(shù)結(jié)構(gòu)的葉子節(jié)點(diǎn)必須指向具體數(shù)據(jù)。如果你要建立聚集索引的列不唯一,并且你指定的創(chuàng)建的聚集索引是非唯一的聚集索引,則會(huì)有以下情況:
如果未使用 unique 屬性創(chuàng)建聚集索引,數(shù)據(jù)庫(kù)引擎 將向表自動(dòng)添加一個(gè)四字節(jié) uniqueifier 列。必要時(shí),數(shù)據(jù)庫(kù)引擎 將向行自動(dòng)添加一個(gè) uniqueifier 值,使每個(gè)鍵唯一。此列和列值供內(nèi)部使用,用戶不能查看或訪問(wèn)。
試驗(yàn)內(nèi)容
1、 數(shù)據(jù)表的建立
基本表《簡(jiǎn)單的》帶有主鍵
帶有外碼約束的(外碼來(lái)自其他表或者本表)
2、 數(shù)據(jù)表的修改
添加刪除列
修改列屬性類型
添加刪除約束(約束名)
元組的添加,修改,刪除
刪除數(shù)據(jù)表
試驗(yàn)過(guò)程
1、create table student
(
sno char(9) primary key , /*sno是主碼 列級(jí)完整性約束條件*/
sname char(20) unique, /*sname取唯一值*/
ssex char(2),
sage smallint, /*類型為smallint*/
sdept char(20) /*所在系*/
);
create table course
(
cno char(4) primary key, /*列級(jí)完整性約束條件,cno是主碼*/
cname char(40),
cpno char(4), /*cpno的含義是先行課*/
ccredit smallint,
foreign key (cpno) references course(cno)
/*表級(jí)完整性約束條件,cpno是外碼,被參照表是course,被參照列是cno*/
);
create table sc
(
sno char(9),
cno char(4),
grade smallint,
primary key (sno,cno),
/*主碼有兩個(gè)屬性構(gòu)成,必須作為表級(jí)完整性進(jìn)行定義*/
foreign key (sno) references student(sno),
/*表級(jí)完整性約束條件,sno是外碼,被參照表是student*/
foreign key (cno) references course(cno),
/*表級(jí)完整性約束條件,cno是外碼,被參照表示course*/
);
例1、create table s
(
cno varchar(3), /*變長(zhǎng)的字符串,輸入2個(gè)字符就是兩個(gè)字符不會(huì)補(bǔ)空格*/
sname varchar(20),
status int,
city varchar(20),
constraint pk_sno primary key(sno), /*約束條件的名字為pk_sno*/
);
create table p
(
pno varchar(3),
pname varchar(20),
color varchar(3),
weight int,
constraint pk_pno primary key (pno), /*約束條件的名字是pk_pno*/
);
create table j
(
jno varchar(3),
jname varchar(20),
city varchar(20),
constraint pk_jno primary key(jno) /*約束條件的名字為pk_jno*/
);
例2、create table spj
(
sno varchar(3), /*第一個(gè)表中的主碼*/
pno varchar(3),
jno varchar(3),
qty int, /*數(shù)量*/
constraint pk_spj primary key(sno,pno,jno), /*主碼由3個(gè)屬性組成*/
foreign key(sno) references s(sno),
/*表級(jí)完整性約束條件,sno是外碼,被參照表是s*/
foreign key(pno) references p(pno),
/*表級(jí)完整性約束條件,pno是外碼,被參照表是p*/
foreign key(jno) references j(jno),
/*表級(jí)完整性約束條件,jno是外碼,被參照表是j*/
);
2、數(shù)據(jù)表的更改
在s表中添加一個(gè)concat 列
alter table s add concat varchar(20)
在s表中刪除concat 列
alter table s drop column concat
更改s表 concat列的屬性 把長(zhǎng)度由20改為30
alter table s alter column concat varchar(30)
聯(lián)系方式 名字為concat 修改屬性為唯一的 屬性名為con_concat
alter table s add constraint con_concat unique(concat)
刪除約束關(guān)系con_concat
alter table s drop constraint con_concat
/*插入一個(gè)元組*/
insert into s valus(‘s1’,’精益’,20,’天津’) /*20不能寫(xiě)成’20’*/
試驗(yàn)中的問(wèn)題的排除與總結(jié):
1、在創(chuàng)建spj時(shí)
有三個(gè)實(shí)體所以從3個(gè)實(shí)體中取主碼,還有一個(gè)數(shù)量屬性也要寫(xiě)上
主碼由那3個(gè)主碼確定
2、更改一個(gè)數(shù)據(jù)庫(kù)中數(shù)據(jù)表時(shí)一定要先使該數(shù)據(jù)庫(kù)處于正在使用狀態(tài)
3、constraint
是可選關(guān)鍵字,表示 primary key、not null、unique、foreign key 或 check 約束定義的開(kāi)始。約束是特殊屬性,用于強(qiáng)制數(shù)據(jù)完整性并可以為表及其列創(chuàng)建索引。
4、--go可以不加但是要注意順序 注:go --注釋 提示錯(cuò)誤
5、注意添加一個(gè)空元素用 null
附 sql備份
--創(chuàng)建一個(gè)數(shù)據(jù)庫(kù) student
create database student
go
--在數(shù)據(jù)庫(kù)student中創(chuàng)建表student course sc 注意順序
use student
----------------------------------------------------------------
create table student
(
sno char(9) primary key, /*sno是主碼 列級(jí)完整性約束條件*/
sname char(10) unique, /*sname取唯一值*/
ssex char(2),
sage smallint, /*類型為smallint*/
sdept char(20) /*所在系*/
); /*;要加*/
-----------
數(shù)據(jù)庫(kù)實(shí)驗(yàn)總結(jié)【二】
我在sql server 索引基礎(chǔ)知識(shí)系列中,第一篇就講了記錄數(shù)據(jù)的基本格式。那里主要講解的是,數(shù)據(jù)庫(kù)的最小讀存單元:數(shù)據(jù)頁(yè)。一個(gè)數(shù)據(jù)頁(yè)是8k大小。
對(duì)于數(shù)據(jù)庫(kù)來(lái)說(shuō),它不會(huì)每次有一個(gè)數(shù)據(jù)頁(yè)變化后,就存到硬盤。而是變化達(dá)到一定數(shù)量級(jí)后才會(huì)作這個(gè)操作。 這時(shí)候,數(shù)據(jù)庫(kù)并不是以數(shù)據(jù)頁(yè)來(lái)作為操作單元,而是以64k的數(shù)據(jù)(8個(gè)數(shù)據(jù)頁(yè),一個(gè)區(qū))作為操作單元。
區(qū)是管理空間的基本單位。一個(gè)區(qū)是八個(gè)物理上連續(xù)的頁(yè)(即 64 kb)。這意味著 sql server 數(shù)據(jù)庫(kù)中每 mb 有 16 個(gè)區(qū)。
為了使空間分配更有效,sql server 不會(huì)將所有區(qū)分配給包含少量數(shù)據(jù)的表。sql server 有兩種類型的區(qū):
統(tǒng)一區(qū),由單個(gè)對(duì)象所有。區(qū)中的所有 8 頁(yè)只能由所屬對(duì)象使用。
混合區(qū),最多可由八個(gè)對(duì)象共享。區(qū)中八頁(yè)的每頁(yè)可由不同的對(duì)象所有。
通常從混合區(qū)向新表或索引分配頁(yè)。當(dāng)表或索引增長(zhǎng)到 8 頁(yè)時(shí),將變成使用統(tǒng)一區(qū)進(jìn)行后續(xù)分配。如果對(duì)現(xiàn)有表創(chuàng)建索引,并且該表包含的行足以在索引中生成 8 頁(yè),則對(duì)該索引的所有分配都使用統(tǒng)一區(qū)進(jìn)行。
為何會(huì)這樣呢?
其實(shí)很簡(jiǎn)單:
讀或?qū)?8kb 的時(shí)間與讀或?qū)?64 kb的時(shí)間幾乎相同。
在 8 kb 到 64 kb 范圍之內(nèi),單個(gè)磁盤 i/o 傳輸操作所花的時(shí)間主要是磁盤取數(shù)臂和讀/寫(xiě)磁頭運(yùn)動(dòng)的時(shí)間。
因此,從數(shù)學(xué)上來(lái)講,當(dāng)需要傳輸 64 kb 以上的 sql 數(shù)據(jù)時(shí),
盡可能地執(zhí)行 64 kb 磁盤傳輸是有益的,即分成數(shù)個(gè)64k的操作。
因?yàn)?64 kb 傳輸基本上與 8 kb 傳輸一樣快,而每次傳輸?shù)?sql server 數(shù)據(jù)是 8 kb 傳輸?shù)?8 倍。
我們通過(guò)一個(gè)實(shí)例來(lái)看 有and 操作符時(shí)候的最常見(jiàn)的一種情況。我們有下面一個(gè)表,
create table [dbo].[member]( [member_no] [dbo].[numeric_id] identity(1,1) not null, [lastname] [dbo].[shortstring] not null, [firstname] [dbo].[shortstring] not null, [middleinitial] [dbo].[letter] null, [street] [dbo].[shortstring] not null, [city] [dbo].[shortstring] not null, [state_prov] [dbo].[statecode] not null, [country] [dbo].[countrycode] not null, [mail_code] [dbo].[mailcode] not null, [phone_no] [dbo].[phonenumber] null, [photograph] [image] null, [issue_dt] [datetime] not null default (getdate()), [expr_dt] [datetime] not null default (dateadd(year,1,getdate())), [region_no] [dbo].[numeric_id] not null, [corp_no] [dbo].[numeric_id] null, [prev_balance] [money] null default (0), [curr_balance] [money] null default (0), [member_code] [dbo].[status_code] not null default (' '))
這個(gè)表具備下面的四個(gè)索引:
索引名 細(xì)節(jié) 索引的列
member_corporation_link nonclustered located on primary corp_no
member_ident clustered, unique, primary key located on primary member_no
member_region_link nonclustered located on primary region_no
memberfirstname nonclustered located on primary firstname
當(dāng)我們執(zhí)行下面的sql查詢時(shí)候,
select m.member_no, m.firstname, m.region_nofrom dbo.member as mwhere m.firstname like 'k%' and m.region_no > 6 and m.member_no < 5000go
sql server 會(huì)根據(jù)索引方式,優(yōu)化成下面方式來(lái)執(zhí)行。
select a.member_no,a.firstname,b.region_nofrom(select m.member_no, m.firstname from dbo.member as m where m.firstname like 'k%' and m.member_no < 5000) a , -- 這個(gè)查詢可以直接使用 memberfirstname 非聚集索引,而且這個(gè)非聚集索引覆蓋了所有查詢列-- 實(shí)際執(zhí)行時(shí),只需要 邏輯讀取 3 次
(select m.member_no, m.region_no from dbo.member as mwhere m.region_no > 6) b
-- 這個(gè)查詢可以直接使用 member_region_link 非聚集索引,而且這個(gè)非聚集索引覆蓋了所有查詢列-- 實(shí)際執(zhí)行時(shí),只需要 邏輯讀取 10 次
where a.member_no = b.member_no
不信,你可以看這兩個(gè)sql 的執(zhí)行計(jì)劃,以及邏輯讀信息,都是一樣的。
其實(shí)上面的sql,如果優(yōu)化成下面的方式,實(shí)際的邏輯讀消耗也是一樣的。為何sql server 不會(huì)優(yōu)化成下面的方式。是因?yàn)?and 操作符優(yōu)化的另外一個(gè)原則。
1/26 的數(shù)據(jù)和 1/6 的數(shù)據(jù)找交集的速度要比 1/52 的數(shù)據(jù)和 1/3 的數(shù)據(jù)找交集速度要慢。
select a.member_no,a.firstname,b.region_nofrom(select m.member_no, m.firstname from dbo.member as mwhere m.firstname like 'k%' -- 1/26 數(shù)據(jù)) a,
(select m.member_no, m.region_no from dbo.member as mwhere m.region_no > 6 and m.member_no < 5000-- 1/3 * 1/ 2 數(shù)據(jù)) bwhere a.member_no = b.member_no
當(dāng)然,我們要學(xué)習(xí)sql 如何優(yōu)化的話,就會(huì)用到查詢語(yǔ)句中的一個(gè)功能,指定查詢使用哪個(gè)索引來(lái)進(jìn)行。
比如下面的查詢語(yǔ)句
select m.member_no, m.firstname, m.region_nofrom dbo.member as m with (index (0))where m.firstname like 'k%' and m.region_no > 6 and m.member_no < 5000go
select m.member_no, m.firstname, m.region_nofrom dbo.member as m with (index (1))where m.firstname like 'k%' and m.region_no > 6 and m.member_no < 5000goselect m.member_no, m.firstname, m.region_nofrom dbo.member as m with (index (membercovering3))where m.firstname like 'k%' and m.region_no > 6 and m.member_no < 5000goselect m.member_no, m.firstname, m.region_nofrom dbo.member as m with (index (memberfirstname, member_region_link))where m.firstname like 'k%' and m.region_no > 6 and m.member_no < 5000go
這里 index 計(jì)算符可以是 0 ,1, 指定的一個(gè)或者多個(gè)索引名字。對(duì)于 0 ,1 的意義如下:
如果存在聚集索引,則 index(0) 強(qiáng)制執(zhí)行聚集索引掃描,index(1) 強(qiáng)制執(zhí)行聚集索引掃描或查找(使用性能最高的一種)。
如果不存在聚集索引,則 index(0) 強(qiáng)制執(zhí)行表掃描,index(1) 被解釋為錯(cuò)誤。
總結(jié)知識(shí)點(diǎn):
簡(jiǎn)單來(lái)說(shuō),我們可以這么理解:sql server 對(duì)于每一條查詢語(yǔ)句。會(huì)根據(jù)實(shí)際索引情況(sysindexes 系統(tǒng)表中存儲(chǔ)這些信息),分析每種組合可能的成本。然后選擇它認(rèn)為成本最小的一種。作為它實(shí)際執(zhí)行的計(jì)劃。
成本代價(jià)計(jì)算的一個(gè)主要組成部分是邏輯i/o的數(shù)量,特別是對(duì)于單表的查詢。
and 操作要滿足所有條件,這樣,經(jīng)常會(huì)要求對(duì)幾個(gè)數(shù)據(jù)集作交集。數(shù)據(jù)集越小,數(shù)據(jù)集的交集計(jì)算越節(jié)省成本。
的項(xiàng)目中,竟然出現(xiàn)了濫用聚集索引的問(wèn)題??磥?lái)沒(méi)有培訓(xùn)最最基礎(chǔ)的索引的意義,代價(jià),使用場(chǎng)景,是一個(gè)非常大的失誤。這篇博客就是從這個(gè)角度來(lái)羅列索引的基礎(chǔ)知識(shí)。
使用索引的意義
索引在數(shù)據(jù)庫(kù)中的作用類似于目錄在書(shū)籍中的作用,用來(lái)提高查找信息的速度。
使用索引查找數(shù)據(jù),無(wú)需對(duì)整表進(jìn)行掃描,可以快速找到所需數(shù)據(jù)。
使用索引的代價(jià)
索引需要占用數(shù)據(jù)表以外的物理存儲(chǔ)空間。
創(chuàng)建索引和維護(hù)索引要花費(fèi)一定的時(shí)間。
當(dāng)對(duì)表進(jìn)行更新操作時(shí),索引需要被重建,這樣降低了數(shù)據(jù)的維護(hù)速度。
創(chuàng)建索引的列
主鍵
外鍵或在表聯(lián)接操作中經(jīng)常用到的列
在經(jīng)常查詢的字段上最好建立索引
不創(chuàng)建索引的列
很少在查詢中被引用
包含較少的惟一值
定義為 text、ntext 或者 image 數(shù)據(jù)類型的列
heaps是staging data的很好選擇,當(dāng)它沒(méi)有任何index時(shí)
excellent for high performance data loading (parallel bulk load and parallel index creation after load)
excellent as a partition to a partitioned view or a partitioned table
聚集索引提高性能的方法,在前面幾篇博客中分別提到過(guò),下面只是一個(gè)簡(jiǎn)單的大綱,細(xì)節(jié)請(qǐng)參看前面幾篇博客。
何時(shí)創(chuàng)建聚集索引?
clustered index會(huì)提高大多數(shù)table的性能,尤其是當(dāng)它滿足以下條件時(shí):
獨(dú)特, 狹窄, 靜止: 最重要的條件
持續(xù)增長(zhǎng)的,最好是只向上增加。例如:
identity
date, identity
guid (only when using newsequentialid() function)
聚集索引唯一性(獨(dú)特型的問(wèn)題)
由于聚集索引的b+樹(shù)結(jié)構(gòu)的葉子節(jié)點(diǎn)必須指向具體數(shù)據(jù)。如果你要建立聚集索引的列不唯一,并且你指定的創(chuàng)建的聚集索引是非唯一的聚集索引,則會(huì)有以下情況:
如果未使用 unique 屬性創(chuàng)建聚集索引,數(shù)據(jù)庫(kù)引擎 將向表自動(dòng)添加一個(gè)四字節(jié) uniqueifier 列。必要時(shí),數(shù)據(jù)庫(kù)引擎 將向行自動(dòng)添加一個(gè) uniqueifier 值,使每個(gè)鍵唯一。此列和列值供內(nèi)部使用,用戶不能查看或訪問(wèn)。

