如果本地臨時表由存儲過程創(chuàng)建或由多個用戶同時執(zhí)行的應用程序創(chuàng)建(其實可看作是不同的鏈接,不同的會話),則數(shù)據(jù)庫引擎必須能夠區(qū)分由不同用戶創(chuàng)建的表。為此,數(shù)據(jù)庫引擎在內(nèi)部為每個本地臨時表的表名追加一個數(shù)字后綴。存儲在 tempdb 的 sysobjects 表中的臨時表,其全名由 CREATE TABLE 語句中指定的表名和系統(tǒng)生成的數(shù)字后綴組成。為了允許追加后綴,為本地臨時表指定的 table_name 不能超過 116 個字符。
對于本地臨時表來說,需要注意在不同情形下應用本地臨時表其刪除的實際。如假設數(shù)據(jù)庫在執(zhí)行一個存儲過程的時候建立了本地臨時表。那么此時這個本地臨時表并不是在會話終止的時候自動刪除,而是在這個存儲過程執(zhí)行完畢后就會刪除。這是什意思呢?也就是說,用戶發(fā)起的某個會話,為了執(zhí)行一個特殊的作業(yè)(如用戶的這個會話調(diào)用了某個存儲過程)。此時其實就是會話再創(chuàng)建一個子會話的過程。在這種情況下需要注意的是,子會話創(chuàng)建的本地臨時表只在子會話內(nèi)部有效。當這個子會話終止的時候(存儲過程執(zhí)行完畢),此時這個臨時表就會自動刪除。即對于調(diào)用這個子會話的會話來說,這個其子會話的創(chuàng)建的臨時表對于其也是無效的,因為臨時表已經(jīng)在子會話關閉的時候自動刪除。做一個形象的比喻。即現(xiàn)在做父親的去叫兒子造一座房子。當兒子死亡的時候,這座房子也會消失。對于這種情況,數(shù)據(jù)庫管理員需要注意。父會話只能夠引用子會話從臨時表中傳遞出來的數(shù)據(jù)。也就是說,父會話要訪問子會話創(chuàng)建的臨時表的數(shù)據(jù),只有一種手段。即先讓子會話對臨時表中的數(shù)據(jù)進行查詢或者操作,然后把結構回傳給父會話。父會話是不能夠直接訪問子會話所創(chuàng)建的臨時表。當然這個限制是專門針對本地臨時表而言的。對于全局臨時表來說,本身就是所有用戶都可以訪問,為此就沒有這個限制。
全局臨時表
以兩個井號 (##) 開頭的那些表名。在所有連接上都能看到全局臨時表或者這樣說只要這個全局臨時表存在,那么用戶創(chuàng)建會話后對所有的用戶都是可見的。如果在創(chuàng)建全局臨時表的連接斷開前沒有顯式地除去這些表,那么只要所有其它任務停止引用它們,這些表即被除去。當創(chuàng)建全局臨時表的連接斷開后,新的任務不能再引用它們(換句話說舊的任務還何以引用)。當前的語句一執(zhí)行完,任務與表之間的關聯(lián)即被除去;因此通常情況下,只要創(chuàng)建全局臨時表的連接斷開,全局臨時表即被除去。
當然勿論是全局的還是本地表,只要是能訪問的都能用DROP TABLE 來強制地刪除臨時表。
當創(chuàng)建本地或全局臨時表時,CREATE TABLE 語法支持除 FOREIGN KEY 約束以外的其他所有約束定義。如果臨時表中指定了 FOREIGN KEY 約束,則該語句將返回一條表明已跳過此約束的警告消息。此表仍將創(chuàng)建,但不使用 FOREIGN KEY 約束。在 FOREIGN KEY 約束中不能引用臨時表。
以一個實際的例子來談談普通表、本地臨時表、全局臨時表三個表的差異。如現(xiàn)在有一個保存員工信息的表user。這個表是一個普通表,只要其建立就不會自動刪除,任何好在數(shù)據(jù)庫中有使用這個表(具有訪問權限)的用戶都可以訪問這個表,除非這個表被所有者刪除或者更改了權限。在用戶A(具有訪問權限)訪問這個表的過程中,數(shù)據(jù)庫可能會根據(jù)需要生成一張本地臨時表#user。此時只有這個會話才可以訪問這個本地臨時表。當這個用戶的會話中斷之后,這個本地臨時表也會被自動刪除。不過根據(jù)需要,數(shù)據(jù)庫也可能會建立全局臨時表##user(在名字上與本地臨時表不同)。此時數(shù)據(jù)庫中的任何用戶只要連接到了數(shù)據(jù)庫就可以訪問這個全局臨時表(訪問權限上的不同)。當這個創(chuàng)建臨時表會話的用戶中斷數(shù)據(jù)庫連接時,這個臨時表是否會刪除是一個未知數(shù),這要看當時的實際情況(在可用性上不同)。如果此時還有其他用戶連接在這個表上的話,那么這個全局臨時表就不會被刪除。只有在中斷連接時,沒有其他用戶在訪問這個表時,即某個用戶(不一定是創(chuàng)建這張全局臨時表的用戶)斷開連接并且所有其他的會話不再使用這個表時才會被刪除。
可見無論是全局臨時表還是本地臨時表,其跟普通表相比,最重要的一個差異就是其會根據(jù)需要自動創(chuàng)建。當不再需要時其又會自動刪除。這也正是臨時表的魅力所在,其可以在數(shù)據(jù)處理的過程中,減少很多中間表格。
臨時表對日志與鎖的影響
日志文件是數(shù)據(jù)庫中很重要的一個工具。無論是SQL Server數(shù)據(jù)庫還是Oracle數(shù)據(jù)庫,都有日志這個工具。如憑借重做日志工具,數(shù)據(jù)庫管理員可以在數(shù)據(jù)庫故障的時候借此來恢復數(shù)據(jù),將數(shù)據(jù)恢復到故障的那個點上。但是在使用臨時表的時候,需要注意一點,就是臨時表不會有日志文件。即對臨時表進行的DML等操作不會形成日志文件。這個特性即有好處,也有壞處。好處是對于臨時表的更改不會保存到日志文件中。也就是說,如果數(shù)據(jù)庫發(fā)生了故障,則保存在臨時表中的數(shù)據(jù)是不能夠恢復的。為此數(shù)據(jù)庫管理員不得不重新執(zhí)行某些作業(yè)以重新生成臨時表中的數(shù)據(jù)。好處就是對于臨時表的DML操作速度會非常的塊。除了其他的原因導致其性能的提升外,在更改其內(nèi)容時不會生成日志信息也是一個重要的原因。為此對臨時表的操作不生成日志信息,這是一個雙刃劍。數(shù)據(jù)庫管理員在日常工作中,要盡量發(fā)揮其優(yōu)勢,減少其負面作用的影響。
另外,若采用臨時表這種處理機制的話,還需要注意其對鎖的影響。在介紹本地臨時表與全局臨時表差異的時候,筆者就介紹過,本地臨時表只對當前的會話有效。即使當前會話又創(chuàng)建了另外一個子會話,也只對子會話有效。當某個會話終止的時候,這臨時表就會自動被刪除。而對于普通表或者全局臨時表來說,可能同時多個會話都可以訪問這個表。這兩者有什么區(qū)別呢?若允許多個會話可以同時訪問某個表的話,那么這個表就可能會遇到鎖的情況。即某個用戶會話在對表中地記錄進行DML等操作時,為了保證數(shù)據(jù)的一致性,會對相關的記錄進行加鎖等措施。而采用本地臨時表的話,由于只有一個會話可以訪問臨時表中的數(shù)據(jù),所以即使這個會話更改臨時表中的數(shù)據(jù),也不會有鎖沖突的問題。故其在更改本地臨時表中的數(shù)據(jù)時,就不用為其加鎖。所以,對于本地臨時表的操作速度就要比其他表來的快。故在何時的情況下使用臨時表無疑可以提高數(shù)據(jù)庫的整體性能。如可以將一些操作在臨時表中完成,然后再將最后的結果更新到基本表中。
利用SQL的全局臨時表防止用戶重復登錄
在我們開發(fā)商務軟件的時候,常常會遇到這樣的一個問題:怎樣防止用戶重復登錄我們的系統(tǒng)?特別是對于銀行或是財務部門,更是要限制用戶以其工號身份多次登入。
可能會有人說在用戶信息表中加一字段判斷用戶工號登錄的狀態(tài),登錄后寫1,退出時寫0,且登錄時判斷其標志位是否為1,如是則不讓該用戶工號登錄。但是這樣那勢必會帶來新的問題:如發(fā)生象斷電之類不可預知的現(xiàn)象,系統(tǒng)是非正常退出,無法將標志位置為0,那么下次以該用戶工號登錄則不可登入.
在這個過程中,我們看到如果以用戶工號命名的全局臨時表不存在時過程會去創(chuàng)建一張并把out參數(shù)置為0,如果已經(jīng)存在則將out參數(shù)置為1。
這樣,我們在我們的應用程序中調(diào)用該過程時,如果取得的out參數(shù)為1時,我們可以毫不客氣地跳出一個message告訴用戶說”對不起,此工號正被使用!”
上面還涉及到一個OBJECT_ID ()函數(shù):
Syntax:
OBJECT_ID ( '[ database_name . [ schema_name ] . | schema_name . object_name' [ ,'object_type' ] )
一般語法:int object_id('objectname');
此方法返回數(shù)據(jù)庫對象標識號。
其中,參數(shù)objectname 表示要使用的對象,其數(shù)據(jù)類型為nchar或char(如果為char,系統(tǒng)將其轉換為nchar)
object_type:為可選參數(shù),其數(shù)據(jù)類型為nchar或char(如果為char,系統(tǒng)將其轉換為nchar),指明架構范圍的對象類型(object_name為字符串通過它,可以說明這個字符串究竟是說明對象,其列表見文章結尾)
ps:使用 OBJECT_ID 不能查詢非架構范圍內(nèi)的對象(如 DDL 觸發(fā)器)。對于在 sys.objects 目錄視圖中找不到的對象,需要通過查詢適當?shù)哪夸浺晥D來獲取該對象的標識號。例如,若要返回 DDL 觸發(fā)器的對象標識號,請使用 SELECT OBJECT_ID FROM sys.triggers WHERE name = 'DatabaseTriggerLog'。
返回類型為int,表示該對象在系統(tǒng)中的編號,如果找不到或發(fā)生錯誤一律返回NULL。
例子:
A.返回數(shù)據(jù)庫AdventureWorks中Production.WorkOrder表的標識號
B.存在性檢查
下列會確認資料表有物件的標識碼,藉此檢查指定的資料表是否存在。如果存在就刪除。
此方法一般用來判斷數(shù)據(jù)庫中本來用沒有此對象(procedures,views,functions等).
注意:
當該參數(shù)對系統(tǒng)函數(shù)可選時,則系統(tǒng)采用當前數(shù)據(jù)庫、主機、服務器用戶或數(shù)據(jù)庫用戶。內(nèi)置函數(shù)后面必須跟圓括號。
如果指定一個臨時表名,除非當前數(shù)據(jù)庫為tempdb(廢話),否則必須在臨時表名前面加上數(shù)據(jù)庫名,例如:
SELECT OBJECT_ID('tempdb..#mytemptable')
AF = 聚合函數(shù) (CLR) |
C = CHECK 約束 |
D = DEFAULT(約束或獨立) |
F = FOREIGN KEY 約束 |
FN = SQL 標量函數(shù) |
FS = 程序集 (CLR) 標量函數(shù) |
FT = 程序集 (CLR) 表值函數(shù) |
IF = SQL 內(nèi)聯(lián)表值函數(shù) |
IT = 內(nèi)部表 |
P = SQL 存儲過程 |
PC = 程序集 (CLR) 存儲過程 |
PG = 計劃指南 |
PK = PRIMARY KEY 約束 |
R = 規(guī)則(舊式,獨立) |
RF = 復制篩選過程 |
S = 系統(tǒng)基表 |
SN = 同義詞 |
SQ = 服務隊列 |
TA = 程序集 (CLR) DML 觸發(fā)器 |
TF = SQL 表值函數(shù) |
TR = SQL DML 觸發(fā)器 |
U = 表(用戶定義類型) |
UQ = UNIQUE 約束 |
V = 視圖 |
X = 擴展存儲過程 |
聯(lián)系客服