目錄
(一)前言
(二)正文
1.?物化視圖(索引視圖)與查詢重寫的基本概念
2. 創(chuàng)建測試環(huán)境
(1)建表
(2)寫數(shù)據(jù)
3.?索引視圖創(chuàng)建
(1)創(chuàng)建語法
(2)為索引視圖創(chuàng)建索引
?4. 查詢重寫
5.?為什么查詢會被重寫
?6.?索引視圖什么時候更新
7.?改變基于視圖的查詢
(一)前言
之前做ORACLE DBA的時候,在參與業(yè)務(wù)涉及中常會遇到物化視圖這個概念,同樣在SQL SERVER中也有類似于ORACLE的物化視圖的功能,只是名稱變成了索引視圖。本文我們將采用SQL SERVER來談一談索引視圖(物化視圖)以及基于此的查詢重寫。
(二)正文
1.?物化視圖(索引視圖)與查詢重寫的基本概念
與普通視圖比,物化視圖就是直接將數(shù)據(jù)存儲起來了。而物化視圖的作用之一就是可以實(shí)現(xiàn)查詢重寫。SQL Server中的索引視圖就具有查詢重寫的功能,所謂的查詢重寫,就是如果符合條件的數(shù)據(jù)在索引視圖上,并且查詢列都包含在在索引視圖上,此時可以直接通過查詢索引視圖來替代基于原始表的查詢。
2. 創(chuàng)建測試環(huán)境
(1)建表
創(chuàng)建兩張表,一張表頭TESTZYY,一張明細(xì)TESTLMN,僅僅作為DEMO使用
--創(chuàng)建兩張表,一張表頭TESTZYY,一張明細(xì)TESTLMN,僅僅作為DEMO使用
CREATE TABLE TESTZYY
(
HeadId INT PRIMARY KEY ,
HeadInfo VARCHAR(50) ,
DataStatus TINYINT ,
CreateDate Datetime
)
GO;
CREATE TABLE TESTLMN
(
HeadId INT ,
DetailId INT identity(1,1) PRIMARY KEY ,
DetailInfo VARCHAR(50)
)
GO;
(2)寫數(shù)據(jù)
給上一步中創(chuàng)建的兩個表各插入20萬條數(shù)據(jù)。
--寫入數(shù)據(jù)
DECLARE @i int = 0
WHILE @i<200000
BEGIN
INSERT INTO TESTZYY values (@i,NEWID(),RAND()*10,GETDATE()-RAND()*100)
INSERT INTO TESTLMN(HeadId,DetailInfo) VALUES (@i,NEWID())
SET @i=@i+1
END
GO
?
3.?索引視圖創(chuàng)建
那么如何創(chuàng)建索引視圖呢?語法上跟創(chuàng)建普通視圖差別不大,但是不允許出現(xiàn)select *,表名上要加上Scheme,如下會做簡單說明。
(1)創(chuàng)建語法
CREATE VIEW V_IndexViewTest WITH SCHEMABINDING
AS
SELECT H.HeadId,H.CreateDate,H.DataStatus,D.DetailId,D.DetailInfo
FROM dbo.TESTZYY H INNER JOIN
dbo.TESTLMN D ON H.HeadId = D.HeadId
WHERE H.DataStatus = 0
GO;
TIPS:
創(chuàng)建時候一定要用INNER JOIN,否則在后續(xù)創(chuàng)建聚集索引時候會有報錯!?
?
(2)為索引視圖創(chuàng)建索引
索引視圖要求創(chuàng)建的第一個列為唯一聚集索引,所以如下,創(chuàng)建一個唯一的聚集索引。
create unique clustered index idx_headid
on V_IndexViewTest(detailid);
對于其他索引,可以跟在表上創(chuàng)建索引一樣
create index idx_createdate on V_IndexViewTest(Createdate);
?4. 查詢重寫
上面說了,查詢重寫就是將基于原始表的查詢語句,直接在索引視圖上查詢實(shí)現(xiàn),那么就來看一下查詢重寫是什么樣子的?
下面來觀察這么一個查詢,SQL很明顯地是基于原始表做的查詢,跟普通查詢并無二致,但是觀察執(zhí)行計(jì)劃就會發(fā)現(xiàn):這個執(zhí)行計(jì)劃走了一個索引查找,首先很清楚,TESTZYY上的CreateDate是沒有索引的,這里走的索引就是V_IndexViewTest上的CreatDate列上的索引,也就是在索引視圖上創(chuàng)建的第二個索引。
select H.headid,H.createdate,D.detailid,D.detailinfo
from dbo.TESTZYY H inner join
dbo.TESTLMN D on H.headid = D.headid
where H.datastatus = 0
and H.createdate > '2022-10-01 00:00:00.000'
and H.createdate < '2022-10-02 00:00:00.000'
?如果,查詢語句這么寫,如下,在查詢條件中增加了一個索引視圖中沒有的列,此時查詢就不會被重寫,直接走的是基于原始表的查詢,跟普通查詢并無二致。
其實(shí)原理不難理解,因?yàn)橐晥D中并不包含HeadInfo這個列,如果在查詢列上加上這個字段,視圖中是沒有這個字段的,那只能基于原始表做查詢了。
select H.headid,H.createdate,H.headinfo,D.detailid,D.detailinfo
from dbo.TESTZYY H inner join
dbo.TESTLMN D on H.headid = D.headid
where H.datastatus = 0
and H.createdate > '2022-10-01 00:00:00.000'
and H.createdate < '2022-10-02 00:00:00.000'
5.?為什么查詢會被重寫
上面我們看到了,對于合適的查詢,查詢是會被重寫的,也就是查詢直接基于索引視圖來實(shí)現(xiàn),那么為什么會直接基于視圖來實(shí)現(xiàn)呢?
還是處于性能上的考慮,因?yàn)樗饕晥D在創(chuàng)建唯一的聚集索引之后,視圖就“固化了”原始表的結(jié)果集,此時的視圖與普通視圖最大的區(qū)別就是,視圖中直接存儲了數(shù)據(jù)本身,而非一個查詢,此時的視圖中的數(shù)據(jù)集,相當(dāng)于基于原表的一個“子集”,因?yàn)槭亲蛹?,這個結(jié)果集必然小于原始表,那么同樣的查詢字段和查詢條件,不但可以減少表與表之間的鏈接操作,且結(jié)果集更小,從這個視圖上查詢,因此同等條件下可以更快地返回結(jié)果,所以查詢重寫也就不難理解了。
結(jié)合上述結(jié)論,此時只要查詢字段和查詢條件一樣,基于原始表的查詢和直接查詢索引視圖是一樣的,如下截圖?:
select H.headid,H.createdate,D.detailid,D.detailinfo
from dbo.TESTZYY H inner join
dbo.TESTLMN D on H.headid = D.headid
where H.datastatus = 0 and H.createdate > '2022-10-01 00:00:00.000'
and H.createdate < '2022-10-02 00:00:00.000';
select headid,createdate,detailid,detailinfo
from dbo.V_IndexViewTest
where datastatus = 0 and createdate > '2022-10-01 00:00:00.000'
and createdate < '2022-10-02 00:00:00.000';
?
?6.?索引視圖什么時候更新
上面說了查詢重寫,如果條件允許,基于原始表的查詢會直接從索引視圖上來實(shí)現(xiàn)??赡苡腥藭环判模吘箶?shù)據(jù)都是基于物理表做增刪改的,而索引視圖中的數(shù)據(jù)又是物理存在的,那就就會有一個擔(dān)心,基于視圖的查詢會不會不準(zhǔn)確?畢竟是我好好的一個查詢,你默認(rèn)給我定位到索引視圖上,查詢結(jié)果會跟原始表查詢一致嗎?
那么就要求證一下,索引視圖中的數(shù)據(jù)是如何更新的。我們做這么一個測試,在基表,也就是TESTLMN中查詢一條數(shù)據(jù),看看到底在執(zhí)行計(jì)劃中發(fā)生了什么 可以明顯地看到,不僅僅是往TESTLMN中寫入了一條數(shù)據(jù),同時,基于索引視圖的查詢也往索引視圖中寫入了一條數(shù)據(jù),因此可以放心地使用索引視圖而不必?fù)?dān)心索引視圖中的數(shù)據(jù)和基表的數(shù)據(jù)不一致的問題。
但是要注意的就是,此時的寫,是寫入基表的同事,也寫入了索引視圖,對寫入的影響是肯定有一些的,如果對寫入效率要求非常高,就要謹(jǐn)慎一點(diǎn)了。其實(shí)索引視圖也是一種冗余寫來實(shí)現(xiàn)查詢效率的提高的。
insert into dbo.TESTLMN(HeadId,DetailInfo) values (666,newid());
7.?改變基于視圖的查詢
上面說了,某些基于視圖的查詢,是直接定位到視圖,從視圖中查詢結(jié)果返回的,如下圖
?
?
?
但是如果真的不想從視圖中查詢,我就是想對比一下原始表和基于視圖查詢的(效率上的)區(qū)別,該怎么辦?這個也好辦,可以通過查詢提示,將查詢來基于原始表實(shí)現(xiàn),也就是展開這個索引視圖了OPTION (EXPAND VIEWS)這個查詢提示就是將視圖展開,從原始表進(jìn)行查詢,默認(rèn)情況下是不展開的。如截圖,可以強(qiáng)制展開索引視圖,從原始表查詢?:
select headid,createdate,detailid,detailinfo
from dbo.V_IndexViewTest
where datastatus = 0 and createdate > '2022-10-01 00:00:00.000'
and createdate < '2022-10-02 00:00:00.000' option(expand views);
那么效率對比呢?如下截圖,粗看起來,這個效率差別還是挺大的,可見,SQL Server默認(rèn)選擇下,載效率上還是有一定的考慮的
這里從索引視圖查詢,一是減少了表之間的join,而是索引視圖的結(jié)果集更小,從中篩選符合條件的數(shù)據(jù)效率就會更好一些。所以,默認(rèn)情況下是會從視圖查詢來對SQL進(jìn)行查詢重寫的。索引視圖的查詢提示:with(no expand) 強(qiáng)制不展開,OPTION (EXPAND VIEWS)強(qiáng)制展開。
總結(jié):文章來源:http://www.zghlxwxcb.cn/news/detail-448035.html
本文粗淺地分析了SQL Server 中的索引視圖以及索引視圖帶來的查詢重寫功能,通過索引視圖固化基表的結(jié)果集,
可以在一定程度上提高查詢效率,尤其是在超級大的多表join的時候,直接將原始結(jié)果存為一個索引視圖,
通過對索引視圖查詢來減少表之間的join和IO來提高效率,不失為一種優(yōu)化選擇。
需要注意的是,SQL Server的索引視圖限制非常多,具體可以參考鏈接叢書或者M(jìn)SND,并不是所有的情況都可以使用索引視圖來實(shí)現(xiàn)。文章來源地址http://www.zghlxwxcb.cn/news/detail-448035.html
到了這里,關(guān)于淺談SQL Server索引視圖(物化視圖)以及索引視圖與查詢重寫的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!