一旦成功地從表中檢索出數據,就須要進一步操縱這些數據,以得到有用或有意義的結果。這些要求包括:執行計算與數學運算、轉換數據、解析數值、組合值和聚合一個範圍內的值等。程序員
下表給出了T-SQL函數的類別和描述。算法
函數類別數據庫 |
做用express |
聚合函數編程 |
執行的操做是將多個值合併爲一個值。例如 COUNT、SUM、MIN 和 MAX。api |
配置函數數組 |
是一種標量函數,可返回有關配置設置的信息。緩存 |
轉換函數安全 |
將值從一種數據類型轉換爲另外一種。服務器 |
加密函數 |
支持加密、解密、數字簽名和數字簽名驗證。 |
遊標函數 |
返回有關遊標狀態的信息。 |
日期和時間函數 |
能夠更改日期和時間的值。 |
數學函數 |
執行三角、幾何和其餘數字運算。 |
元數據函數 |
返回數據庫和數據庫對象的屬性信息。 |
排名函數 |
是一種非肯定性函數,能夠返回分區中每一行的排名值。 |
行集函數 |
返回可在 Transact-SQL 語句中表引用所在位置使用的行集。 |
安全函數 |
返回有關用戶和角色的信息。 |
字符串函數 |
可更改 char、varchar、nchar、nvarchar、binary 和 varbinary 的值。 |
系統函數 |
對系統級的各類選項和對象進行操做或報告。 |
系通通計函數 |
返回有關 SQL Server 性能的信息。 |
文本和圖像函數 |
可更改 text 和 image 的值。 |
函數的組成
函數的目標是返回一個值。大多數函數都返回一個標量值(scalar value),標量值表明一個數據單元或一個簡單值。實際上,函數能夠返回任何數據類型,包括表、遊標等可返回完整的多行結果集的類型。本章不許備討論到這個深度,第12章將講解如何建立和使用用戶自定義函數,以返回更復雜的數據。
函數己經存在很長時間了,它的歷史比SQL還要長。在幾乎全部的編程語言中,函數調用的方式都是相同的:
Result=Function()
在T-SQL中,通常用SELECT語句來返回值。若是須要從查詢中返回一個值,就能夠把SELECT當成輸出運算符,而不用使用等號:
SELECT Function()
一個論點
對於SQL函數而言,參數表示輸入變量或者值的佔位符。函數能夠有任意個參數,有些參數是必須的,而有些參數是可選的。可選參數一般被置於以逗號隔開的參數表的末尾,以便於在函數調用中去除不須要的參數。
在SQL Server在線圖書或者在線幫助系統中,函數的可選參數用方括號表示。在下列的CONVERT()函數例子中,數據類型的length和style參數是可選的:
CONVERT (data-type [(length)], expression[,style])
可將它簡化爲以下形式,由於如今不討論如何使用數據類型:
CONVERT(date_type, expression[,style])
根據上面的定義,CONVERT()函數可接受2個或3個參數。所以,下列兩個例子都是正確的:
SELECT CONVERT(Varchar(20), GETDATE()) SELECT CONVERT(Varchar(20), GETDATE(), 101) |
這個函數的第一個參數是數據類型Varchar(20),第2個參數是另外一個函數GETDATE()。GETDATE()函數用datetime數據類型將返回當前的系統日期和時間。第2條語句中的第3個參數決定了日期的樣式。這個例子中的101指以mm/dd/yyyy格式返回日期。本章後面將詳細介紹GETDATE()函數。即便函數不帶參數或者不須要參數,調用這個函數時也須要寫上一對括號,例如GETDATE()函數。注意在書中使用函數名引用函數時,必定要包含括號,由於這是一種標準形式。
肯定性函數
因爲數據庫引擎的內部工做機制,SQL Server必須根據所謂的肯定性,將函數分紅兩個不一樣的組。這不是一種新時代的信仰,只和可否根據其輸入參數或執行對函數輸出結果進行預測有關。若是函數的輸出只與輸入參數的值相關,而與其餘外部因素無關,這個函數就是肯定性函數。若是函數的輸出基於環境條件,或者產生隨機或者依賴結果的算法,這個函數就是非肯定性的。例如,GETDATE()函數是非肯定性函數,由於它不會兩次返回相同的值。爲何要把看起來簡單的事弄得如此複雜呢?主要緣由是非肯定性函數與全局變量不能在一些數據庫編程對象中使用(如用戶自定義函數)。部分緣由是SQL Server緩存與預編譯可執行對象的方式。例如,即席查詢可使用任何函數,不過若是打算構建先進的、可重用的編程對象,理解這種區別很重要。
如下這些函數是肯定性的:
l AVG()(全部的聚合函數都是肯定性的)
l CAST()
l CONVERT()
l DATEADD()
l DATEDIFF()
l ASCII()
l CHAR()
l SUBSTRING()
如下這些函數與變量是非肯定性的:
l GETDATE()
l @@ERROR
l @@SERVICENAME
l CURSORSTATUS()
l RAND()
在函數中使用用戶變量
變量既可用於輸入,也可用於輸出。在T-SQL中,用戶變量以@符號開頭,用於聲明爲特定的數據類型。可使用SET或者SELECT語句給變量賦值。如下的例子用於將一個int類型的變量@MyNumber傳遞給SQRT()函數:
DECLARE @MyNumber int SET @MyNumber=144 SELECT SQRT(@MyNumber) |
結果是12,即144的平方根。
用SET給變量賦值
如下例子使用另外一個int型的變量@MyResult,來捕獲該函數的返回值。這個技術相似於過程式編程語言中的函數調用樣式,即把SET語句和一個表達式結合起來,給參數賦值:
DECLARE @MyNumber int, @MyResult int SET @MyNumber = 144 -- Assign the function result to the variable: SET @MyResult = SQRT(@MyNumber) -- Return the variable value SELECT @MyResult |
用SELECT給變量賦值
使用SELECT的另外一種形式也能夠得到一樣的結果。對變量要在賦值前要先聲明。使用SELECT語句來替代SET命令的主要優勢是,能夠在一個操做內同時給多個變量賦值。執行下面的SELECT語句,經過SELECT語句賦值的變量就能夠用於任何操做了。
DECLARE @MyNumber1 int, @MyNumber2 int, @MyResult1 int, @MyResult2 int SELECT @MyNumber1 = 144, @MyNumber2 = 121 -- Assign the function result to the variable: SELECT @MyResult1 = SQRT(@MyNumber1), @MyResult2 = SQRT(@MyNumber2) -- Return the variable value SELECT @MyResult1, @MyResult2 |
上面的例子首先聲明瞭4個變量,而後用兩個SELECT語句給這些變量賦值,而不是用4個SELECT語句給變量賦值。雖然這些技術在功能上是相同的,可是在服務器的資源耗費上,用一個SELECT語句給多個變量賦值通常比用多個SET命令的效率要高。將一個甚至多個值選進參數的限制是,對變量的賦值不能和數據檢索操做同時進行。這就是上面的例子使用SELECT語句來填充變量,而用另一個SELECT語句來檢索變量中數據的緣由。例如,下面的腳本就不能工做:
DECLARE @RestockName varchar(50) SELECT ProductId ,@RestockName = Name + ':' + ProductNumber FROM Production.Product |
這個腳本會產生以下錯誤:
消息141,級別15,狀態1,第2 行 向變量賦值的SELECT 語句不能與數據檢索操做結合使用。 |
在查詢中使用函數
函數常常和查詢表達式結合使用來修改列值。這隻需將列名做爲參數傳遞給函數便可,隨後函數將引用插入到SELECT查詢的列的列表中,以下所示:
SELECT Title, NationalIDNumber, YEAR(BirthDate) AS BirthYear FROM HumanResources.Employee |
在這個例子中,BirthDate列的值被做爲參數傳遞給YEAR()函數。函數的結果是別名爲BirthYear的列。
嵌套函數
咱們須要的功能經常不能僅由一個函數來實現。根據設計,函數應儘可能簡單,用於提供特定的功能。若是一個函數要執行許多不一樣的操做,就變得複雜和難以使用。所以,每一個函數一般僅執行一個操做,要實現全部的功能,能夠將一個函數的返回值傳遞給另外一個函數,這稱爲嵌套函數調用。
如下是一個簡單的例子:GETDATE()函數的做用是返回當前的日期與時間,但不能返回通過格式化的數據,由於這是CONVERT()函數的功能。要想同時使用這兩個函數,能夠把GETDATE()函數的輸出做爲CONVERT()函數的輸入參數。
SELECT CONVERT(Varchar(20), GETDATE(), 101) |
聚合函數
報表的典型用途是從所有數據中提取出表明一種趨勢的值或者彙總值,這就是聚合的意義。聚合函數回答數據使用者的以下問題:
上個月雞雛的總銷售量是多少?
19~24歲之間的巴西男性在食品調味品上的平均支出是多少?
上季度全部訂單中從訂購到運輸的最長時間是多少?
收發室裏仍在工做的最老的員工是誰?
聚合函數應用特定的聚合操做並返回一個標量值(單一值)。返回的數據類型對應於該列或者傳遞到函數中的值。聚合常常和分組、累積以及透視等表運算一塊兒使用,生成數據分析結果。第7章將詳細介紹這個主題,這裏僅討論簡單SELECT查詢中的一些經常使用函數。
聚合函數不只可用在SELECT查詢中,還能夠和標量輸入值一塊兒使用。那麼,這樣作的意義是什麼呢?在下列代碼中,將值15傳遞給下列聚合函數,每一個函數的返回值都相同:
SELECT AVG(15) SELECT SUM(15) SELECT MIN(15) SELECT MAX(15) |
它們都返回15。雖然,對同一個值求平均、求和、求最小值、求最大值,所得的結果仍是那個值。若是對一個值計數,又會產生什麼結果呢?
SELECT COUNT(15)
獲得的值是1,由於函數只計數了一個值。
如今作一些有意義的事。聚合函數只有在處理結果集合中的一組數據時纔有意義。每一個函數都處理某列的非空值。除非使用分組操做(詳見第7章),不然不能在同一個SELECT語句中既返回聚合的值,又返回常規的列值。
AVG()函數
AVG()函數用於返回一組數值中全部非空數值的平均值。例如,表6-2包含了體操成績。
表 6-2
體操運動員 |
項 目 |
成 績 |
Sara |
跳馬 |
9.25 |
Cassie |
跳馬 |
8.75 |
Delaney |
跳馬 |
9.25 |
Sammi |
跳馬 |
8.05 |
Erika |
跳馬 |
8.60 |
Sara |
平衡木 |
9.70 |
Cassie |
平衡木 |
9.00 |
Delaney |
平衡木 |
9.25 |
Sammi |
平衡木 |
8.95 |
Erika |
平衡木 |
8.85 |
對這些數據執行如下查詢:
SELECT AVG(Score)
結果是8.965。
若是有三個女孩沒有完成一些項目,在表中沒有記錄成績,則可用NULL來表示(見表6-3)。
表 6-3
體操運動員 |
項 目 |
成 績 |
Sara |
跳馬 |
9.25 |
Cassie |
跳馬 |
8.75 |
Delaney |
跳馬 |
NULL |
Sammi |
跳馬 |
8.05 |
Erika |
跳馬 |
8.60 |
Sara |
平衡木 |
9.70 |
Cassie |
平衡木 |
NULL |
Delaney |
平衡木 |
9.25 |
Sammi |
平衡木 |
NULL |
Erika |
平衡木 |
8.85 |
腳本:
create table #GymEvent(Player varchar(10),[Subject] nvarchar(5),Scoredecimal(4,2)) go insert into #GymEvent values('Sara','跳馬',9.25) insert into #GymEvent values('Cassie','跳馬',8.75) insert into #GymEvent values('Delaney','跳馬',NULL) insert into #GymEvent values('Sammi','跳馬',8.05) insert into #GymEvent values('Erika','跳馬',8.60) insert into #GymEvent values('Sara','平衡木',9.70) insert into #GymEvent values('Cassie','平衡木',NULL) insert into #GymEvent values('Delaney','平衡木',9.25) insert into #GymEvent values('Sammi','平衡木',NULL) insert into #GymEvent values('Erika','平衡木',8.85) go drop table #GymEvent |
在這種狀況下,計算平均值時只考慮實際的數值,NULL不參與運算,結果是8.921429。 可是,若是把缺乏的成績也算在內,即用數值0代替NULL,則會嚴重影響最終成績(6.245),她們能不能進入國家級的比賽就難說了。
COUNT()函數
COUNT()函數用於返回一個列內全部非空值的個數,這是一個整型值。好比,在上一個例子中,體操數據被保存在#GymEvent表中,要肯定Sammi參加的項目數,則能夠執行下列查詢:
SELECT COUNT(Score) FROM #GymEvent WHERE Player='Sammi'
結果是1,由於Sammi只參加了跳馬比賽,她的平衡木成績是NULL。
若是須要肯定表中的行數,不管這些行是否是NULL值,均可以使用如下語法:
SELECT COUNT (*) FROM #GymEvent
以Sammi爲例,COUNT(*)查詢以下所示:
SELECT COUNT(*) FROM #GymEvent WHERE Player='Sammi'
因爲COUNT(*)函數會忽略NULL值,因此這個查詢的結果是2。
MIN()與MAX()函數
MIN()函數用於返回一個列範圍內的最小非空值;MAX()函數用於返回最大值。這兩個函數能夠用於大多數的數據類型,返回的值根據對不一樣數據類型的排序規則而定。爲了說明這兩個函數,假設有一個表包含了兩列值,一列是整型值,另外一列是字符型值,如表6-4所示。
表 6-4
IntegerColumn(int類型) |
VarCharColumn(varChar類型) |
2 |
2 |
4 |
4 |
12 |
12 |
19 |
19 |
腳本:
create table #Temp(IntegerColumn int,VarCharColumn varchar(10)) go insert into #Temp values(2,'2') insert into #Temp values(4,'4') insert into #Temp values(12,'12') insert into #Temp values(19,'19') go drop table #Temp |
若是分別調用MIN()與MAX()函數將會返回什麼值呢?
select MIN(IntegerColumn),MAX(IntegerColumn) from #Temp select MIN(VarCharColumn),MAX(VarCharColumn) from #Temp |
由於VarCharColumn中值的存儲類型爲字符類型,而不是數字,因此結果以每一個字符的ASCII值爲順序從左到右排序。這就是12比其餘值小、而4比其餘值大的緣由。
SUM()函數
SUM()函數是最經常使用的聚合函數之一,它的功能很容易理解:和AVG()函數同樣,它用於數值數據類型,返回一個列範圍內全部非空值的總和。
配置變量
配置變量不是函數,不過它們的用法和系統函數相同。每一個全局變量都可以返回SQL Server執行環境的標量信息。如下是一些常見的例子。
@@ERROR變量
這個變量包含當前鏈接發生的最後一次錯誤的代碼。在執行的語句沒有錯誤時,@@ERROR變量的值是0。出現標準錯誤時,錯誤是由數據庫引擎引起的。全部的標準錯誤代碼與消息都保存在sys.messages系統視圖中,可使用以下腳本查詢:
SELECT * FROM sys.messages
定製錯誤能夠經過調用RAISERROR語句來手動引起,並調用sp_addmessage系統存儲過程將其添加到sysmessages表中。
如下是一個@@ERROR變量的簡單例子。先試着將一個數除以0,數據庫引擎會引起標準錯誤號爲8134的錯誤。注意查看Results選項卡中的查詢結果。在發生錯誤時,Management Studio的Messages選項卡將默認顯示在Results選項卡的上面:
SELECT 5 / 0 SELECT @@ERROR |
在成功檢索@@ERROR的值後,@@ERROR的值將返回0,由於@@ERROR只保存了上次執行的語句的錯誤代碼。若是但願檢索更多的錯誤信息,可使用以下腳本從sysmessages視圖中獲得:
SELECT 5 / 0 SELECT * FROM master.dbo.sysmessages WHERE error = @@ERROR |
本節的後面部份內容將說明如何經過使用錯誤函數來更高效地返回錯誤數據。
除了美國英語以外,SQL Server還默認安裝了其餘語言。每種語言專用的錯誤消息都有一個語言標識符(mslangid),對應於syslanguages表中的一種語言,以下圖所示。
error |
severity |
dlevel |
description |
msglangid |
8134 |
16 |
0 |
Divide by zero error encountered. |
1033 |
8134 |
16 |
0 |
Fehler aufgrund einer Division durch Null. |
1031 |
8134 |
16 |
0 |
Division par zéro. |
1036 |
8134 |
16 |
0 |
0 除算エラーが発生しました。 |
1041 |
8134 |
16 |
0 |
Error de división entre cero. |
3082 |
8134 |
16 |
0 |
Errore di divisione per zero. |
1040 |
8134 |
16 |
0 |
Обнаружена ошибка: деление на ноль. |
1049 |
8134 |
16 |
0 |
Erro de divisão por zero. |
1046 |
8134 |
16 |
0 |
發現除以零的錯誤。 |
1028 |
8134 |
16 |
0 |
0으로 나누기 오류가 발생했습니다. |
1042 |
8134 |
16 |
0 |
遇到以零做除數錯誤。 |
2052 |
屬性名mslangid被非正式地定義爲Microsoft Global Language Identifier。微軟公司用這個標識符來標識一種語言或語言和國家的組合,微軟公司把語言和國家的組合定義爲地區。例如,在隨SQL Server安裝的英語中,美國英語的mslangid是1033,英國英語的mslangid是2057。要檢索出全部已安裝的、支持的語言,能夠執行下面的查詢:
SELECT alias, name, msglangid FROM sys.syslanguages |
@@SERVICENAME變量
這個變量是用於執行和維護當前SQL Server實例的Windows服務名。它一般返回SQL Server默認實例MSSQLSERVER,但SQL Server的指定實例有惟一的服務名。例如在名爲WoodVista的計算機上有兩個SQL Server實例:默認實例和指定實例AughtEight。如在默認實例上檢索@@SERVICENAME全局變量的內容,將返回MSSQLSERVER,但在指定實例上檢索,會返回AUGHTEIGHT。
@@TOTAL_ERRORS變量
這個變量用於記錄從打開當前鏈接開始發生的總錯誤次數。和@@ERROR變量同樣,它對每一個用戶會話是惟一的,並將在鏈接關閉時被重置。
@@TOTAL_READ變量
這個變量記錄從打開當前鏈接時開始計算的磁盤讀取總數。DBA使用這個變量查看磁盤讀取活動的狀況。
@@VERSION變量
這個變量包含當前SQL Server實例的完整版本信息。
SELECT @@VERSION
好比,對於運行在Windows 7上的SQL Server 2008開發版實例,以上腳本可以返回以下信息:
Microsoft SQL Server 2008 (RTM) - 10.0.1600.22 (Intel X86) Jul 9 2008 14:43:34 Copyright (c) 1988-2008 Microsoft Corporation Enterprise Edition on Windows NT 6.1 <X86> (Build 7600: )
實際的版本號是一個簡單的整型值,它在微軟公司內部使用。而發行的產品可能有其餘的商標名。在本例中,SQL Server 2005的版本是9,SQL Server 2008的版本是10。Windows XP Professional顯示爲Windows NT 5.l版,而Vista顯示爲6.0版。構建號用於內部控制,反映beta版和預覽版以及正式發行後的補丁包的變化。
錯誤函數
前面學習瞭如何使用@@ERROR全局變量來檢索錯誤信息。而返回全部錯誤數據的更好方法是使用錯誤函數。這些函數返回的信息能夠存儲在錯誤跟蹤表中,以供錯誤審覈。錯誤函數嵌套在錯誤處理例程中。第11章將詳細討論錯誤處理,其實經過使用嵌套在TRY和END TRY語句中的代碼塊,後跟一個放在CATCH和END CATCH語句中的代碼塊就能夠實現錯誤處理。
--Try to do something BEGIN TRY SELECT 5 / 0 END TRY --If it causes an error, do this BEGIN CATCH PRINT ERROR_MESSAGE() END CATCH |
所謂的錯誤捕獲,其實就是這個意思。若是運行上面的示例,將不會出現可識別的錯誤,由於錯誤將被捕獲並在CATCH語句塊中進行處理。在編寫錯誤處理代碼時,SQL程序員必須把這些代碼放在會引起系統錯誤的catch代碼塊中。
下列幾個錯誤函數用於返回錯誤的特定信息:
函數 |
說明 |
ERROR_MESSAGE() |
返回錯誤的描述。 |
ERROR_NUMBER() |
返回錯誤號。 |
ERROR_SEVERITY() |
返回錯誤的嚴重級別。錯誤的嚴重級別是一個從0到25的整數。 |
ERROR_STATE() |
返回錯誤的狀態號。錯誤狀態是一個整數,能夠惟一地表示系統錯誤的緣由。 |
ERROR_LINE() |
返回例程中致使出錯的行號。 |
ERROR_PROCEDURE() |
返回發生錯誤的存儲過程名或觸發器名。 |
下表簡要描述了嚴重級別。
嚴 重 級 別 |
說 明 |
0~10 |
信息性消息。不會引起系統錯誤 |
11~16 |
用戶能夠更正的錯誤,例如違反了外鍵或主鍵規則 |
17 |
非致命的、不重要的資源錯誤 |
18 |
非致命的內部錯誤 |
19 |
致命的、不重要的資源錯誤 |
20 |
當前進程中的致命錯誤 |
21 |
全部進程中的致命數據庫錯誤 |
22 |
致命的表完整性錯誤 |
23 |
致命的數據庫完整性錯誤 |
24 |
致命的硬件錯誤 |
25 |
致命的系統錯誤 |
下面腳本使用T-SQL的內置錯誤處理功能,來捕獲和輸出遇到除0錯誤時返回的錯誤數據。SELECT命令的結果將顯示在Management Studio的消息選項卡中。
--Try to do something BEGIN TRY SELECT 5 / 0 END TRY --If it causes an error, do this BEGIN CATCH SELECT ERROR_MESSAGE(),ERROR_NUMBER(),ERROR_SEVERITY(), ERROR_STATE(),ERROR_LINE(),ERROR_PROCEDURE() END CATCH |
能夠看出,執行這個腳本會在消息選項卡中返回有關錯誤的更多詳細信息,而不只僅是錯誤號自己。
ERROR_PROCEDURE()函數不能返回過程名,由於錯誤是在ad-hoc查詢中生成的。
轉換函數
數據類型轉換能夠經過CAST()和CONVERT()函數來實現。大多數狀況下,這兩個函數是重疊的,它們反映了SQL語言的演化歷史。這兩個函數的功能類似,不過它們的語法不一樣。雖然並不是全部類型的值都能轉變爲其餘數據類型,但總的來講,任何能夠轉換的值均可以用簡單的函數實現轉換。
CAST()函數
CAST()函數的參數是一個表達式,它包括用AS關鍵字分隔的源值和目標數據類型。如下例子用於將文本字符串'123'轉換爲整型:
SELECT CAST('123' AS int)
返回值是整型值123。若是試圖將一個表明小數的字符串轉換爲整型值,又會出現什麼狀況呢?
SELECT CAST('123.4' AS int)
CAST()函數和CONVERT()函數都不能執行四捨五入或截斷操做。因爲123.4不能用int數據類型來表示,因此對這個函數調用將產生一個錯誤:
Server: Msg 245, Level 16, State 1, Line 1
Syntax error converting the varchar value
'123.4' to a column of data type int.
在將varchar 值'123.4' 轉換成數據類型int 時失敗。
要返回一個合法的數值,就必須使用能處理這個值的數據類型。對於這個例子,存在多個可用的數據類型。若是經過CAST()函數將這個值轉換爲decimal類型,須要首先定義decimal值的精度與小數位數。在本例中,精度與小數位數分別爲9與2。精度是總的數字位數,包括小數點左邊和右邊位數的總和。而小數位數是小數點右邊的位數。這表示本例可以支持的最大的整數值是9999999,而最小的小數是0.01。
SELECT CAST('123.4' AS decimal(9,2))
decimal數據類型在結果網格中將顯示有效小數位:123.40
精度和小數位數的默認值分別是18與0。若是在decimal類型中不提供這兩個值,SQL Server將截斷數字的小數部分,而不會產生錯誤。
SELECT CAST('123.4' AS decimal)
結果是一個整數值:123
在表的數據中轉換數據類型是很簡單的。下面的例子使用Product表,首先執行以下查詢:
SELECT ProductNumber, ProductLine, ProductModelID FROM Production.Product WHERE ProductSubcategoryID < 4 |
假定產品經理已經建立了一個系統,用於惟一地標識生產出來的每輛自行車,以便跟蹤其型號、類型和類別。他決定合併產品號、產品生產線標識符、產品型號標識符和一個順序號,爲生產出來的每輛自行車建立一個惟一的序列號。在這個過程的第一步,他要求提供包括除順序號以外的全部屬性的全部可能產品的根標識符。
若是使用下面的表達式,就不能獲得但願的結果,如圖6-2所示。
SELECT ProductNumber + '-' + ProductLine + '-' + ProductModelID AS BikeSerialNum FROM Production.Product WHERE ProductSubcategoryID < 4 |
消息245,級別16,狀態1,第1 行 在將nvarchar 值'BK-R93R-62-R -' 轉換成數據類型int 時失敗。 |
咱們沒有獲得但願的結果,而獲得了有點奇怪的錯誤消息:請把nvarchar值轉換爲int。由於以前咱們沒有要求進行任何轉換,因此這個錯誤很奇怪。這個查詢的問題在於咱們試圖利用第一個鏈接符來鏈接字符值ProductNumber,利用第二個鏈接符鏈接另外一個字符值ProductLine,最後鏈接的是ProductModelID字符值(它是一個整數)。
查詢引擎會把鏈接符當成一個數學運算符,而不是一個字符。無論結果是什麼,都須要更正這個表達式,以確保使用正確的數據類型。如下表達式執行了必要的類型轉換,返回如圖6-3所示的結果:
SELECT ProductNumber + '-' + ProductLine + '-' + CAST(ProductModelID AS char(4)) AS BikeSerialNum FROM Production.Product WHERE ProductSubcategoryID < 4 |
若是把整型值轉換爲字符類型就不會增長多餘的空格了。查詢引擎將把這些值用加號和鏈接符組合在一塊兒,進行字符串鏈接運算,而不是和前面的數值進行加法或者減法運算了。
CONVERT()函數
對於簡單類型轉換,CONVERT()函數和CAST()函數的功能相同,只是語法不一樣。CAST()函數通常更容易使用,其功能也更簡單。CONVERT()函數的優勢是能夠格式化日期和數值,它須要兩個參數:第1個是目標數據類型,第2個是源數據。如下的兩個例子和上一節的例子相似:
SELECT CONVERT(int, '123') SELECT CONVERT(decimal(9,2), '123.4') |
CONVERT()函數還具備一些改進的功能,它能夠返回通過格式化的字符串值,且能夠把日期值格式化成不少形式。有28種預約義的符合各類國際和特殊要求的日期與時間輸出格式。下表列出了這些日期格式。
若是 expression 爲 date 或 time 數據類型,則 style 能夠爲下表中顯示的值之一。其餘值做爲 0 進行處理。SQL Server 使用科威特算法來支持阿拉伯樣式的日期格式。
yy(1) |
yyyy |
標準 |
輸入/輸出 (3) |
- |
0 或 100 (1, 2) |
默認 |
mon dd yyyy hh:miAM(或 PM) |
1 |
101 |
美國 |
mm/dd/yyyy |
2 |
102 |
ANSI |
yy.mm.dd |
3 |
103 |
英國/法國 |
dd/mm/yyyy |
4 |
104 |
德國 |
dd.mm.yy |
5 |
105 |
意大利 |
dd-mm-yy |
6 |
106 (1) |
- |
dd mon yy |
7 |
107 (1) |
- |
mon dd, yy |
8 |
108 |
- |
hh:mi:ss |
- |
9 或 109 (1, 2) |
默認設置 + 毫秒 |
mon dd yyyy hh:mi:ss:mmmAM(或 PM) |
10 |
110 |
美國 |
mm-dd-yy |
11 |
111 |
日本 |
yy/mm/dd |
12 |
112 |
ISO |
yymmdd yyyymmdd |
- |
13 或 113 (1, 2) |
歐洲默認設置 + 毫秒 |
dd mon yyyy hh:mi:ss:mmm(24h) |
14 |
114 |
- |
hh:mi:ss:mmm(24h) |
- |
20 或 120 (2) |
ODBC 規範 |
yyyy-mm-dd hh:mi:ss(24h) |
- |
21 或 121 (2) |
ODBC 規範(帶毫秒) |
yyyy-mm-dd hh:mi:ss.mmm(24h) |
- |
126 (4) |
ISO8601 |
yyyy-mm-ddThh:mi:ss.mmm(無空格) |
- |
127(6, 7) |
帶時區 Z 的 ISO8601。 |
yyyy-mm-ddThh:mi:ss.mmmZ (無空格) |
- |
130 (1, 2) |
回曆 (5) |
dd mon yyyy hh:mi:ss:mmmAM |
- |
131 (2) |
回曆 (5) |
dd/mm/yy hh:mi:ss:mmmAM |
1. 這些樣式值將返回不肯定的結果。包括全部 (yy)(不帶世紀數位)樣式和一部分 (yyyy)(帶世紀數位)樣式。
2. 默認值(style 0 或 100、9 或 10九、13 或 11三、20 或 120 以及 21 或 121)始終返回世紀數位 (yyyy)。
3. 轉換爲 datetime 時輸入;轉換爲字符數據時輸出。
4. 爲用於 XML 而設計。對於從 datetime 或 smalldatetime 到字符數據的轉換,其輸出格式如上一個表所述。
5. 回曆是有多種變體的日曆系統。SQL Server 使用科威特算法。
a) 默認狀況下,SQL Server 基於截止年份 2049 年來解釋兩位數的年份。換言之,就是將兩位數的年份 49 解釋爲 2049,將兩位數的年份 50 解釋爲 1950。許多客戶端應用程序(如基於自動化對象的應用程序)都使用截止年份 2030 年。SQL Server 提供了「兩位數年份截止」配置選項,可經過此選項更改 SQL Server 使用的截止年份,從而對日期進行一致處理。建議您指定四位數年份。
6. 僅支持從字符數據轉換爲 datetime 或 smalldatetime。僅表示日期或時間成分的字符數據轉換爲 datetime 或 smalldatetime 數據類型時,未指定的時間成分設置爲00:00:00.000,未指定的日期成分設置爲 1900-01-01。
7. 使用可選的時間區域指示符 (Z) 更便於將具備時區信息的 XML datetime 值映射到沒有時區的 SQL Server datetime 值。Z 是時區 UTC-0 的指示符。其餘時區則以 + 或 - 方向的 HH:MM 偏移量來指示。例如:2006-12-12T23:45:12-08:00。
從 smalldatetime 轉換爲字符數據時,包含秒或毫秒的樣式將在這些位置上顯示零。使用相應的 char 或 varchar 數據類型長度從 datetime 或 smalldatetime 值轉換時,可截斷不須要的日期部分。
從樣式包含時間的字符數據轉換爲 datetimeoffset 時,將在結果末尾追加時區偏移量。
這個函數的第三個參數是可選的,該參數用於接收格式代碼整型值。表中的例子用於對DateTime數據類型進行轉換。在轉換SmallDateTime數據類型時,格式不變,但一些元素會顯示爲0,由於該數據類型不支持毫秒。如下的腳本例子將輸出格式化的日期:
SELECT 'Default Date:' + CONVERT(Varchar(50), GETDATE(), 100)
Default Date: Apr 25 2005 1:05PM
SELECT 'US Date:' + CONVERT(Varchar(50), GETDATE(), 101)
US Date: 04/25/2005
SELECT 'ANSI Date:' + CONVERT(Varchar(50), GETDATE(), 103)
ANSI Date: 2005.04.25
SELECT 'UK/French Date:' +CONVERT (Varchar(50), GETDATE(), 103)
UK/French Date: 25/04/2OO5
SELECT 'German Date:' + CONVERT(Varchar(50), GETDATE(), 104)
German Date: 25.04.2005
格式代碼0,1和2也可用於數字類型,它們對小數與千位分隔符格式產生影響。而不一樣的數據類型所受的影響是不同的。通常來講,使用格式代碼0(或者不指定這個參數的值),將返回該數據類型最慣用的格式。使用1或者2一般顯示更爲詳細或者更精確的值。如下例子使用格式代碼0:
DECLARE @Num Money SET @Num = 1234.56 SELECT CONVERT(varchar(50), @Num, 0) |
返回結果以下:
1234.56
使用值1則返回以下結果:
1,234.56
使用值2則返回以下結果:
1234.5600
如下例子和上例相同,可是使用Float類型:
DECLARE @Num float SET @Num = 1234.56 SELECT CONVERT(varchar(50), @Num, 2) |
使用值0不會改變所提供的格式,可是使用值1或2將返回以科學計數法表示的數字,後者使用了15位小數:
1.23456000000000e+003
STR()函數
這是一個將數字轉換爲字符串的快捷函數。這個函數有3個參數:數值、總長度和小數位數。若是數字的整數位數和小數位數(要加上小數點佔用的一個字符)的總和小於總長度,對結果中左邊的字符將用空格填充。在下面第1個例子中,包括小數點在內一共是5個字符。結果顯示在網格中,顯然左邊的空格被填充了。這個調用指定,總長度爲8個字符,小數位爲4位:
SELECT STR(123.4, 8, 4)
結果值的右邊以0填充:123.4000。
下面給函數傳遞了一個10字符的值,並指定結果包含8個字符,有4個小數位:
SELECT STR(123.456789, 8, 4)
只有將這個結果截斷才能符合要求。STR()函數對最後一位進行四捨五入:123.4568。如今,若是爲函數傳遞數字1,並指定結果包含6個字符,有4個小數位,STR()函數將用0補足右邊的空位:
SELECT STR(1, 6, 4)
1.0000
然而,若是指定的總長度大於整數位數、小數點和小數位數之和,結果值的左邊將用空格補齊:
SELECT STR(1, 6, 4)
1.0000
SELECT STR(1, 12, 4)
---------- 1.0000
遊標函數與變量
遊標能夠處理多行數據,在過程循環中一次訪問一行。和基於集合的高效操做相比,這個功能對系統資源的消耗更大。能夠用一個函數和兩個全局變量來管理遊標操做。
CURSOR_STATUS()函數
這個函數返回一個整型值,表示傳遞給這個函數的遊標類型變量的狀態。有不少不一樣類型的遊標會影響這個函數的操做。爲簡單起見,下表列出了這個函數的常見返回值。
返 回 值 |
說 明 |
1 |
遊標包含一行或多行(動態遊標包含0行或者多行) |
0 |
遊標不包含行 |
-1 |
遊標已關閉 |
-2 |
遊標未分配 |
-3 |
遊標不存在 |
@@CURSOR_ROWS全局變量
這個變量是一個整型值,表示在當前鏈接中打開的遊標中的行數。根據遊標類型,這個值也能不表明結果集中的實際行數。
@@FETCH_STATUS全局變量
這個變量是一個標記,用於表示當前遊標指針的狀態。這個變量主要用來判斷某行是否存在,以及在執行了FETCH NEXT語句後,是否已執行到結果集的尾部。打開遊標時,@@FETCH_STATUS變量值爲-1。一旦把第一個值放在遊標中,@@FETCH_STATUS變量值就變成0。當再也不把更多的行放在遊標中時,該變量的值將變回-1。
日期函數
這些函數能夠操做DateTime與SmallDateTime類型的值。有些函數可用於解析日期值的日期與時間部分,有些函數可用於比較、操縱日期/時間值。日期數據類型的區別以下表所示。
數據類型 |
輸出 |
time |
12:35:29. 1234567 |
date |
2007-05-08 |
smalldatetime |
2007-05-08 12:35:00 |
datetime |
2007-05-08 12:35:29.123 |
datetime2 |
2007-05-08 12:35:29. 1234567 |
datetimeoffset |
2007-05-08 12:35:29.1234567 +12:15 |
DATEADD()函數
DATEADD()函數用於在日期/時間值上加上日期單位間隔。好比,要獲得2007年4月29日起90天后的日期,可使用下列語句:
SELECT DATEADD(DAY, 90, '4-29-2007')
結果:2007-07-28 00:00:00.000
能夠把下表的值做爲時間間隔參數傳遞給DATEADD()函數。
datepart |
縮寫 |
year |
yy, yyyy |
quarter |
qq, q |
month |
mm, m |
dayofyear |
dy, y |
day |
dd, d |
week |
wk, ww |
weekday |
dw, w |
hour |
hh |
minute |
mi, n |
second |
ss, s |
millisecond |
ms |
microsecond |
mcs |
nanosecond |
ns |
在下面列出的例子中,咱們使用和上一個例子同樣的日期,而且在這些例子中還包含了時間數據。每一個操做的結果將顯示在查詢的下一行中。
18年後:
SELECT DATEADD(YEAR, 18, '4-29-1988 10:30 AM')
2006-04-29 10:30:00.000
18年前:
SELECT DATEADD(YY, -18, '4-29-1988 10:30 AM')
1970-04-29 10:30:00.000
9000秒後:
SELECT DATEADD(SECOND, 9000, '4-29-1988 10:30 AM')
1988-04-29 13:00:00.000
9000000毫秒前:
SELECT DATEADD(MS, -9000000, '4-29-1988 10:30 AM')
1988-04-29 08:00:00.000
能夠將CONVERT()函數和DATEADD()函數組合在一塊兒,來對1989年9月8日9個月前的日期值進行格式化。
SELECT CONVERT(varchar(20), DATEADD(M, -9, '9-8-1989'), 101)
12/08/1988
這將返回一個可變長度的字符值,比前面例子結果中的默認日期更易容易理解。這是一個函數嵌套調用,DATEADD()函數的返回值(一個DateTime類型的值)被做爲值參數傳遞給CONVERT()函數。
DATEDIFF()函數
DATEADD()和DATEDIFF()函數能夠看做一對錶兄弟,有點像乘法與除法。在等式的兩端有4個元素:起始日期、時間間隔(datepart)、差值和最終日期。若是已知其中的三個值,就能夠求出第4個值。若是在DATEADD()函數中使用起始日期、一個整型值和一個時間間隔,就可返回與起始日期相關的最終日期值。若是提供了起始日期、時間間隔和最終日期,DATEDIFF()函數就能夠返回差值。
爲了說明這一點,咱們選擇任意兩個日期與一個時間間隔做爲參數。這個函數將以所提供的時間間隔爲單位返回兩個日期之間的差值。要知道1989年9月8日和1991年10月17日之間差了幾個月,可編寫以下查詢代碼:
SELECT DATEDIFF(MONTH, '9-8-1989', '10-17-1991')
結果是25個月。若是以日期爲單位呢?
SELECT DATEDIFF(DAY, '9-8-1989', '10-17-1991')
結果是769天。
1996年7月2日和1997年8月4日之間差幾個星期?
SELECT DATEDIFF(WEEK, '7-2-1996', '8-4-1997')
57星期。甚至能夠算出本身的年齡是多少秒:
DECLARE @MyBirthDate datetime SET @MyBirthDate = '7-16-1962' SELECT DATEDIFF(SS, @MyBirthDate, GETDATE()) |
結果顯示有些人已經活了15億秒了!
能夠將列名做爲參數,把這個函數用在查詢中。首先創建一個簡單的表,其中包含一些人的姓名和生日:
SELECT c.FirstName ,c.LastName ,e.BirthDate ,DATEDIFF(YEAR, e.BirthDate, GETDATE()) AS ApproximateAge FROM HumanResources.Employee as e inner join Person.Contact as c on e.ContactID = c.ContactID order by c.LastName |
下圖顯示告終果:
初看起來結果是對的,但存在的問題是年齡值沒有精確到日。好比,根據表中的數據,Nancy的生日是12月21日,他今年將慶祝第32個生日(這個查詢在2010年8月運行)。若是依據上述計算結果來肯定他的年齡什麼時候變化,就應在一月份的某天給他發生日卡片,這比實際日期提早了11個月。
除非用更小的時間單位來計算這些日期的差,不然結果只在僱員實際生日的一年之內是精確的。如下例子將用差值除以一年(包括閏年)的天數,並將結果值轉換爲int類型,進行取整運算,而不是四捨五入。
SELECT c.FirstName ,c.LastName ,e.BirthDate ,DATEDIFF(YEAR, e.BirthDate, GETDATE()) AS ApproximateAge ,CONVERT(int, DATEDIFF(DAY, e.BirthDate, GETDATE())/365) AS Age FROM HumanResources.Employee as e inner join Person.Contact as c on e.ContactID = c.ContactID order by c.LastName |
比較此次的結果和上一個例子的結果,看看有什麼不一樣。
能夠看到,Nancy是31歲,其餘僱員的年齡也精確到了天。表中的BirthDate列存儲僱員的生日,並以午夜(00:00:00AM)爲界,這是一天中的第一秒。GETDATE()函數返回當前的時間與日期。當前兩個日期相差約8小時(寫這段文字時是上午8點)。若是但願這個計算更精確,就須要在當前日期的午夜把GETDATE()函數的結果轉換爲datetime類型。
DATEPART()與DATENAME()函數
這兩個函數用於返回datetime或者shortdatetime值的日期部分。DATEPART()函數返回一個整型值;DATENAME()函數返回一個包含描述性文字的字符串。好比,將日期4-29-1988傳遞給DATEPART()函數,如指定返回月份值,則返回數字4:
SELECT DATEPART(MONTH, '4-29-1988')
而使用相同的參數,DATENAME()函數返回04(這取決於你的機器的本地語言,若是是英文版,那麼將返回April):
SELECT DATENAME(MONTH, '4-29-1988')
這兩個函數都接收和DATEADD()函數同樣的時間間隔參數常量。
GETDATE()與GETUTCDATE()函數
這兩個函數都用於返回datetime類型的當前日期與時間。GETUTCDATE()函數使用服務器上的時區設置來求出UTC時間,這和格林威治標準時間或飛行員所說的"祖魯時"(Zulu Time)是同樣的。兩個函數都能精確到3.33毫秒。
SELECT GETDATE() SELECT GETUTCDATE() |
執行這兩個函數,都將返回未經格式化的結果,見下圖:
我在北京,和UTC時間相差8個小時,和標準時間相差9個小時。可使用以下DATEDIFF()函數來驗證這個時間差值:
SELECT DATEDIFF(HOUR, GETDATE(), GETUTCDATE())
SYSDATETIME()和SYSUTCDATETIME()函數
這兩個SQL Server 2008函數等價於GETDATE()和GETUTCDATE()函數,但不是返回datetime數據類型的結果,而是返回SQL Server 2008新的datetime2數據類型的結果,該數據類型能夠精確到100納秒,固然這取決於服務器安裝的硬件。
SELECT SYSDATETIME() SELECT SYSUTCDATETIME() |
DAY()、MONTH()和YEAR()函數
這三個函數分別返回以整數表示的datetime或者smalldatetime類型值的日、月、年。它們的用途很普遍,如能夠建立獨特的個性化日期格式。假設須要建立一個自定義的日期值做爲字符串,經過將這三個函數的輸出結果轉換成字符類型,而後進行鏈接操做,就能夠對輸出結果以任何形式進行組合了:
SELECT 'Year: ' + CONVERT(varchar(4), YEAR(GETDATE())) + ', Month: ' + CONVERT(varchar(2), MONTH(GETDATE())) + ', Day: ' + CONVERT(varchar(2), DAY(GETDATE())) |
這個腳本生成下列結果:
Year:2008, Month:2, Day:20
下一節將討論字符串操縱函數,並使用類似的技術來構建一個緊湊的定製時間戳。
字符串操縱函數
字符串函數能夠解析、替換、操縱字符型值。在處理原始字符數據時,最大的挑戰之一是如何可靠地提取出有意義的信息。有不少字符串解析函數可用於標識和解析子字符串(一個大字符型值的一部分)。咱們一直在作這種事,在咱們閱讀文件、發票或者書面材料時,就會本能地標識、分離出有意義的信息片斷。這個過程的自動化很是困難,即便是處理不太複雜的文本,也很困難。這些函數包含幾乎全部必需的工具,而挑戰在於如何找出最簡單、最高效的方法。
ASCII()、CHAR()、UNICODE()和NCHAR()函數
這四個函數是類似的,它們均可以在字符和字符的標準數字表示之間轉換。美國標準信息交換碼(American Standard Code for Information Interchange,ASCII)標準字符集包含128個字母、數字和標點符號。這個字符集是IBM PC體系結構的基礎,雖然有些字符如今看來已經很古老了,但仍是被保留了下來,且還是現代計算機技術的核心。若是在計算機上使用英語,則鍵盤上的每一個字符都是用ASCII碼錶示的。這對說英語(至少以英語打字)的計算機用戶來講是有利的,可是其餘人又該怎麼辦呢?
在計算機的發展過程當中, ASCII字符集發佈沒多長時間便過期了。人們很快將它擴展成爲256個字符的ANSI字符集,一個字符用一個字節來保存。這個擴展的字符列表知足了許多其餘用戶的需求,能夠支持主要的歐洲語言字符,不過還是美國標準(由美國國家標準學會持有),仍創建在最初的英語字符集的基礎上。爲了支持全部可印刷的語言,人們制訂了Unicode標準,它支持多種語言特定的字符集。每一個Unicode字符須要2個字節的存儲空間,是ASCII與ANSI字符的兩倍。可是使用2個字就能夠表示超過65 000個不一樣的字符,徹底可以支持東歐和亞洲字符。SQL Server同時支持ASCII與Unicode兩種標準。
ASCII()和CHAR()是兩個基於ASCII的函數,這兩個函數可將計算機上應用的每一個字符表示爲數字。要肯定表明一個字符的數字是什麼,就應給ASCII()函數傳送只包含一個字符的字符串,以下:
SELECT ASCII('A')
結果是65。
如要將一個已知數字轉換爲字符,又該怎麼辦?使用CHAR()函數便可:
SELECT CHAR(65)
結果是字母A。
要獲得完整的ASCII字符值列表,能夠對一個臨時表填充從0到127的數字,而後調用CHAR()函數返回相應的字符。爲了節省空間,咱們對如下這個腳本進行了刪節,但包含整個結果集,並以多欄格式給出。
-- 建立一個臨時表來保存ASCII碼: Create Table #ASCIIVals (ASCIIValue smallint) -- 插入數字0 - 127 到臨時表中: declare @Number int set @Number = 0 while(@Number < 128) begin Insert Into #ASCIIVals (ASCIIValue) Select @Number set @Number = @Number + 1 end -- 查詢全部的整型數字與其對應的ASCII碼: SELECT ASCIIValue, CHAR(ASCIIValue) AS Character FROM #ASCIIVals drop table #ASCIIVals |
表6-12是以多欄網格從新格式化的結果集。須要注意的是這裏將不可印刷的控制字符以方括號表示。因爲許多因素限制,如所安裝的字體或語言不一樣,下表的顯示可能會有稍許差別。
UNICODE()函數是ASCII()的Unicode等價函數,NCHAR()函數和CHAR()函數的功能相同,只不過NCHAR()是用於Unicode字符的。SQL Server的nchar與nvarchar類型能存儲任何Unicode字符,能夠和這兩個函數一塊兒使用。對於特別大的值,ntext類型和nvarchar(max)類型也支持Unicode字符。
要返回擴展字符編碼集中的字符,能夠將字符編碼傳遞給NCHAR()函數:
SELECT NCHAR(220)
返回字母ü。
SELECT NCHAR(233)
返回帶重音符號的小寫e:é。
SELECT NCHAR(241)
固然,ASCII標準也支持全部的歐洲字符,因此使用CHAR()函數也能夠返回這些擴展字符。若是對256~65536之間的值使用CHAR()函數,返回值就頗有趣了。例如,下面的查詢返回希臘字符Ω:
SELECT NCHAR(433)
下面的查詢返回西裏爾字母Ya(Я)。
SELECT NCHAR(1071)
CHARINDEX()和PATINDEX()函數
CHARINDEX()是原始的SQL函數,用於尋找在一個字符串中某子字符串第一次出現的位置。如函數名所示,這個函數返回一個整型值,表示某子字符串的第一個字符在整個字符串中的位置索引。如下腳本用於在字符串Washington中尋找子字符串sh的出現位置:
SELECT CHARINDEX('sh', 'Washington')
返回的結果是3,代表s是字符串Washington中的第3個字符。這說明CHARINDEX函數匹配字符的索引是從1開始的。若是沒有匹配到任何結果,函數將返回0。在這個例子中使用兩個字符做爲子字符串並無特別意義,可是若是字符串包含多個s字符,就有意義了。
PATINDEX()函數和CHARINDEXO函數相似,它執行相同的操做,但方法稍許不一樣,該函數增長了對通配符(即Like運算符中使用的字符)的支持。顧名思義,它將返回一個字符模式的索引。這個函數也能夠和ntext、nchar(max)和nvarchar(max)等大字符類型一塊兒使用。注意,若是和這些大字符類型一塊兒使用,PATINDEX()函數將返回bigint類型的值,而不是int類型的值。如下是一個例子:
SELECT PATINDEX('%M_rs%', 'The stars near Mars are far from ours')
注意,若是想找到一個字符串,在所比較的字符串的先後各有0個或者多個字符,則兩個百分符都是必須的。下劃線代表這個位置上的字符沒必要匹配,它能夠是任意字符。
和使用相同字符串的CHARINDEX()函數做一下比較:
SELECT CHARINDEX('Mars', 'The stars near Mars are far from ours')
這兩個函數都返回索引值16。請注意這些函數的執行過程。下一節將把這兩個函數和SUBSTRING()函數組合在一塊兒,演示如何使用界定符解析字符串。
LEN()函數
LEN()函數用於返回一個表明字符串長度的整型值。這是一個簡單、有用的函數,常常與其餘函數一塊兒使用,來應用業務規則。如下例子將月份和日期轉換爲字符類型,而後測試它們的長度。若是月份日期只有一個字符,就填充字符0,而後組合成一個8字符的美國格式的日期字符串(MMDDYYYY)。
DECLARE @MonthChar varchar(2), @DayChar varchar(2), @DateOut char(8) SET @MonthChar = CAST(MONTH(GETDATE()) AS varchar(2)) SET @DayChar = CAST(DAY(GETDATE()) AS varchar(2)) -- Make sure month and day are two char long: IF LEN(@MonthChar) = 1 SET @MonthChar = '0' + @MonthChar IF LEN(@DayChar) = 1 SET @DayChar = '0' + @DayChar -- Build date string: SET @DateOut = @MonthChar + @DayChar + CAST(YEAR(GETDATE()) ASchar(4)) SELECT @DateOut AS OutputDate |
這個腳本將返回表明日期的8個字符:
08152010
LEFT()和RIGHT()函數
LEFT()與RIGHT()函數是類似的,它們都返回必定長度的子字符串。這兩個函數的區別是,它們返回的分別是字符串的不一樣部分。LEFT()函數返回字符串最左邊的字符,順序從左數到右。RIGHT()函數正好相反,它從最右邊的字符開始,以從右到左的順序返回特定數量的字符。看一看使用這兩個函數返回"GeorgeWashington"這個字符串的子字符串的例子。
若是使用LEFT()函數返回一個5字符的子字符串,則函數先定位最左邊的字符,向右數5個字符,而後返回這個子字符串,以下所示。
DECLARE @FullName varchar(25) SET @FullName = 'George Washington' SELECT LEFT(@FullName, 5) |
結果爲:Georg
若是使用RIGHT()函數返回一個5字符的子字符串,則函數先定位最右邊的字符,向左數5個字符,而後返回這個子字符串,以下所示。
DECLARE @FullName varchar(25) SET @FullName = 'George Washington' SELECT RIGHT (@FullName, 5) |
結果爲:ngton
要想返回字符串中有意義的部分,這兩個函數都不是特別有用。若是想返回全名中的姓氏或者名字,該怎麼辦?這須要多作一點工做。若是能肯定每一個姓名中空格的位置,就可使用LEFT()函數在全名中讀取名字。在這種狀況下,可使用CHARINDEX()或者PATINDEX()函數來定位空格,而後使用LEFT()函數返回空格前的字符。下面是第一個用過程方法編寫的例子,它將處理過程分解成如下步驟:
DECLARE @FullName varchar(25), @SpaceIndex tinyint SET @FullName = 'George Washington' -- Get index of the delimiting space: SET @SpaceIndex = CHARINDEX(' ' , @FullName) -- Return all characters to the left of the space: SELECT LEFT(@FullName, @SpaceIndex - 1) |
結果爲:George
若是不想在結果中包含空格,就須要從@SpaceIndex值中減去1,這樣結果中就只有名字了。
SUBSTRING()函數
SUBSTRING()函數可以從字符串的一個位置開始,往右數若干字符,返回一個特定長度的子字符串。和LEFT()函數不一樣之處是,該函數能夠指定從哪一個位置開始計數,這樣就能夠在字符串的任何位置摘取子字符串了。這個函數須要三個參數:要解析的字符串、起始位置索引、要返回的子字符串長度。若是要返回到所輸入字符串尾部的全部字符,可使用比所需長度更大的長度值。SUBSTRING()函數將返回最大可能長度的字符數,而不會將多出的長度以空格填充。
只要指定字符串最左邊的字符(1)爲起始索引,就能夠用SUBSTRING()函數替代LEFT()函數。
繼續上一節的例子。能夠設置起始位置與長度,返回姓名字符串中間的值。在這個例子中,從位置4開始,返回一個6字符的子字符串"rge Wa"。
DECLARE @FullName varchar(25) SET @FullName = 'George Washington' SELECT SUBSTRING(@FullName, 4, 6) |
如今將上述各函數組合在一塊兒,便可從名字+空格+姓氏格式的全名字符串中解析出名字和姓氏。使用先前的邏輯,經過函數嵌套來減小腳本的行數,並去掉@SpaceIndex變量。下面用SUBSTRING()函數替代LEFT()函數:
DECLARE @FullName varchar(25) SET @FullName = 'George Washington' -- Return first name: SELECT SUBSTRING(@FullName, 1, CHARINDEX(' ', @FullName) - 1) |
相似的邏輯能夠用於解析姓氏,可是必須將起始位置更改成空格後的那個字符。若是空格在第7個位置上,那麼姓氏將從第8個位置開始。這就意味着起始位置是CHARINDEX()的返回結果加上1。
DECLARE @FullName varchar(25) SET @FullName = 'George Washington' --Return last name: SELECT SUBSTRING(@FullName, CHARINDEX(' ', @FullName) + 1, LEN(@FullName)) |
把上述步驟組合在一塊兒,就能夠運行下面的查詢,從全名變量中提取出名字和姓氏:
DECLARE @FullName varchar(25) SET @FullName = 'George Washington' -- Return first name: SELECT SUBSTRING(@FullName, 1, CHARINDEX(' ',@FullName) - 1) ASFirstName, SUBSTRING(@FullName, CHARINDEX(' ',@FullName) + 1,LEN(@FullName)) AS LastName |
傳遞給SUBSTRING()函數的值是空格所在位置加上1,並將該值做爲起始位置,這將是姓氏的第1個字母。因爲不可能老是知道名字的長度,因此將LEN()函數的結果做爲子字符串長度參數傳遞進來,當SUBSTRING()函數到達這個位置時,就到達了字符串的末尾,這樣就能夠將字符串中從空格後面開始的全部字符都包含進來了。
爲了舉例方便,先建立並填充一個臨時表:
CREATE TABLE #MyNames (FullName varchar(50)) GO INSERT INTO #MyNames (FullName) SELECT 'Fred Flintstone' INSERT INTO #MyNames (FullName) SELECT 'Wilma Flintstone' INSERT INTO #MyNames (FullName) SELECT 'Barney Rubble' INSERT INTO #MyNames (FullName) SELECT 'Betty Rubble' INSERT INTO #MyNames (FullName) SELECT 'George Jetson' INSERT INTO #MyNames (FullName) SELECT 'Jane Jetson' go --drop table #MyNames |
下面執行一個使用函數調用來解析名字和姓氏值的單行查詢表達式。這裏對@FullName變量的引用被表中的FullName列所替代:
SELECT SUBSTRING(FullName, 1, CHARINDEX(' ', FullName) - 1) AS FirstName ,SUBSTRING(FullName, CHARINDEX(' ', FullName) + 1, LEN(FullName))AS LastName FROM #MyNames |
在下圖所示的結果中,顯示了兩個不一樣的列,分別是名字和姓氏。
LOWER()和UPPER()函數
這兩個函數很容易理解,它們用於將字符串中全部字符分別都轉換爲小寫和大寫,這在比較用戶輸入或者存儲用於比較的字符串時是很是有用的。字符串比較一般是區分大小寫的,這取決於SQL Server安裝時的設置。若是和其餘的字符串操縱函數一塊兒使用,就能夠將字符串轉換爲合適的大小寫,以便存儲或顯示。如下例子說明混合大小寫的名字,假設名字中的第2個大寫子字符串前只包含一個空格,但在特殊狀況下也有一些名字是沒有空格的。這個例子很容易經過擴展來處理包含其餘類型的混合大小寫名字(如以MC開頭的名字,帶鏈接號的名字等)。
DECLARE @LastName varchar(25), @SpaceIndex tinyint SET @LastName = 'mc donald' -- Test value -- Find space in name: SET @SpaceIndex = CHARINDEX(' ' , @LastName) IF @SpaceIndex > 0 -- Space: Capitalize first & substring SELECT UPPER(LEFT(@LastName, 1)) + LOWER(SUBSTRING(@LastName, 2, @SpaceIndex - 1)) + UPPER(SUBSTRING(@LastName, @SpaceIndex + 1, 1)) + LOWER(SUBSTRING(@LastName, @SpaceIndex + 2, LEN(@LastName))) ELSE -- No space: Cap only first char. SELECT UPPER(LEFT(@LastName, 1)) + LOWER(SUBSTRING(@LastName, 2, LEN(@LastName))) |
這個腳本將返回MC Donald。還能夠對這個例子進行擴展,以處理姓氏包含撇號的狀況。在這個例子的業務規則中,空格是不考慮的。若是找到了撇號,就將後面的字符所有轉爲大寫。請注意若是要在腳本中測試撇號,就必須輸入兩次撇號(' '),以代表這是一個文字,而不是一對單引號。姓氏中只存儲一個撇號。
DECLARE @LastName varchar(25), @SpaceIndex tinyint, @AposIndex tinyint SET @LastName = 'o''malley' -- Test value -- Find space in name: SET @SpaceIndex = CHARINDEX(' ', @LastName) -- Find literal ' in name: SET @AposIndex = CHARINDEX('''', @LastName) IF @SpaceIndex > 0 -- Space: Capitalize first & substring SELECT UPPER(LEFT(@LastName, 1)) + LOWER(SUBSTRING(@LastName, 2, @SpaceIndex - 1)) + UPPER(SUBSTRING(@LastName, @SpaceIndex + 1, 1)) + LOWER(SUBSTRING(@LastName, @SpaceIndex + 2, LEN(@LastName))) ELSE IF @AposIndex > 0 -- Apostrophe: Cap first & substring SELECT UPPER(LEFT(@LastName, 1)) + LOWER(SUBSTRING(@LastName, 2, @AposIndex - 1)) + UPPER(SUBSTRING(@LastName, @AposIndex + 1, 1)) + LOWER(SUBSTRING(@LastName, @AposIndex + 2, LEN(@LastName))) ELSE -- Nospace: Cap only first char. SELECT UPPER(LEFT(@LastName, 1)) + LOWER(SUBSTRING(@LastName, 2, LEN(@LastName))) |
這個腳本返回O'Malley。
LTRIM()和RTRIM()函數
這兩個函數分別返回將字符串的左邊和右邊的空白修剪掉以後的字符串:
DECLARE @Value1 char(10), @Value2 char(10) SET @Value1 = 'One' SET @Value2 = 'Two' SELECT @Value1 + @Value2 SELECT CONVERT(varchar(5), LEN(@Value1 + @Value2)) + ' characters long. ' SELECT RTRIM(@Value1) + RTRIM(@Value2) SELECT CONVERT(varchar(5), LEN(RTRIM(@Value1) + RTRIM(@Value2))) + ' characters long trimmed. ' |
結果以下:
REPLACE()函數
REPLACE()函數能夠把字符串中的某個字符或某個子字符串替換爲另外一個字符或者子字符串,該函數能夠用於全局查找和替換工具中。
DECLARE @Phrase varchar(1000) SET @Phrase = 'I aint gunna use poorgrammar when commenting script and I aint gunna complain about it. ' SELECT REPLACE(@Phrase, 'aint', 'am not') |
REPLICATE()和SPACE()函數
在須要將一些字符重複填充進一個字符串時,這兩個函數是很是有用的。這裏也使用SUBSTRING()例子中的臨時表爲每一個名字填滿20個字符,而後將20減去各個字符串的長度,以便將正確的值傳遞給REPLICATE()函數:
SELECT FullName + REPLICATE('*', 20 - LEN(FullName)) FROM #MyNames |
結果是每一個名字後面都填滿了星號,各個名字的總長度都是20個字符:
Fred Flintstorle*****
Wilrna Flintstone****
Barney Rubble*******
Betty Rubble********
George Jetson********
Jane Jetson**********
SPACE()函數與上述函數相似,區別在於該函數使用空格進行填充。它返回一個由空格組成的字符串,空格的個數由參數定義。
SELECT FullName + SPACE(20 - LEN(FullName)) FROM #MyNames |
若是返回"#MyNames表不存在"的錯誤,只需再次運行本文前面"SUBSTRING()函數"一節的CREATE TABLE腳本便可。
REVERSE()函數
顧名思義,這個函數用於將字符串中的字符顛倒過來。這在處理鏈接列表中的單個字符值時將會被用到。
SELECT REVERSE('The stars near Mars are far from ours. ')
結果爲:.sruo morf raf era sraM raen srats ehT
STUFF()函數
這個函數可將字符串中的一部分替換爲另外一個字符串。它本質上是將一個字符串以特定的長度插入另外一個字符串中的特定位置上。這對於源值與目的值的長度不同的字符串替換是頗有用的。下列代碼用於將字符串中的價格替換爲109.95:
Please submit your payment for 99.95 immediately.
價格值是從第32個字符開始的,有5個字符長。在這個位置上插入的子字符串有多長並不重要,只須要知道須要刪除多少個字符就能夠了。
SELECT STUFF('Please submit your payment for 99.95 immediately. ', 32, 5, '109.95')
結果爲:Please submit your payment for 109.95 immediately.
QUOTENAME()函數
這個函數和SQL Server對象名組合使用,以將結果傳遞給表達式。它只用於給輸入的字符串加一對方括號,並返回新造成的字符串。若是參數包含保留的分隔符或者封裝字符(好比引號或括號),這個函數將修改字符串,以便SQL Server能將結果字符串中的這類字符當成文本字符。以下面的例子所示,查詢的結果如圖6-10所示。
SELECT QUOTENAME(COLUMN_NAME) AS ColumnName FROM INFORMATION_SCHEMA. COLUMNS |
數學函數
下表中列出的函數用於執行多種普通與特殊的數學運算,能夠執行代數、三角、統計、估算與財政運算等運算。
函 數 |
說 明 |
ABS() |
返回一個數的絕對值 |
ACOS() |
計算一個角的反餘弦值,以弧度表示 |
ASIN() |
計算一個角的反正弦值,以弧度表示 |
ATAN() |
計算一個角的反正切值,以弧度表示 |
ATN2() |
計算兩個值的反正切,以弧度表示 |
CEILING() |
返回大於或等於一個數的最小整數 |
COS() |
計算一個角的正弦值,以弧度表示 |
COT() |
計算一個角的餘切值,以弧度表示 |
DEGREES() |
將一個角從弧度轉換爲角度 |
EXP() |
指數運算 |
FLOOR() |
返回小於或等於一個數的最大整數 |
LOG() |
計算以2爲底的天然對數 |
LOG10() |
計算以10爲底的天然對數 |
PI() |
返回以浮點數表示的圓周率 |
POWER() |
冪運算 |
RADIANS() |
將一個角從角度轉換爲弧度 |
RAND() |
返回以隨機數算法算出的一個小數, 能夠接收一個可選的種子值 |
ROUND() |
對一個小數進行四捨五入運算, 使其具有特定的精度 |
SIGN() |
根據參數是正仍是負,返回–1或者1 |
SIN() |
計算一個角的正弦值,以弧度表示 |
SQRT() |
返回一個數的平方根 |
SQUARE() |
返回一個數的平方 |
TAN() |
計算一個角正切的值,以弧度表示 |
元數據函數
這是一些工具函數,它們返回SQL Server配置細節、服務器與數據庫設置細節的信息,包括一組用於返回不一樣對象的屬性狀態的通用以及專用函數,這些函數把對Master數據庫中系統表以及用戶數據庫的查詢封裝在函數中。建議讀者使用這些函數以及其餘的系統函數,而不是本身建立對系統表的查詢,以防從此SQL Server版本對模式進行更改。
排列函數
這些函數被用於以與結果集順序無關的特定順序,枚舉已排序的或排在前面的結果集。
ROW_NUMBER()函數
ROW_NUMBER()函數根據做爲參數傳遞給這個函數的ORDER BY子句的值,返回一個不斷遞增的整數值。若是ROW_NUMBER的ORDER BY的值和結果集中的順序相匹配,返回值將是遞增的,以升序排列。若是ROW_NUMBER的ORDER BY子句的值和結果集中的順序不一樣,這些值將不會按順序列出,但它們表示ROW_NUMBER函數的ORDER BY子句的順序。以下面的例子和結果所示:
SELECT ProductCategoryID ,Name ,ROW_NUMBER() OVER (ORDER BY Name) AS RowNum FROM Production.ProductCategory ORDER BY Name |
因爲ROW_NUMBER()調用中的ORDERBY子句和查詢結果的順序匹配,因此對這些結果按順序列出,以下圖所示:
不過,在函數調用中使用另外一個ORDER BY子句時,這些值就是無序的了。
SELECT ProductCategoryID ,Name ,ROW_NUMBER() OVER (ORDER BY Name) AS RowNum FROM Production.ProductCategory ORDER BY ProductCategoryID |
這是瞭解如何使用ORDER BY子句對結果進行排序的有效方法。以下圖所示:
RANK()與DENSE_RANK()函數
這兩個函數與ROW_NUMBER()函數相似,由於它們都返回一個基於ORDER BY子句的值。不過這些值不必定永遠是惟一的。排列值對於所提供的ORDER BY子句中的重複結果而言也是重複的,並且惟一性是僅僅基於ORDER BY列表中的惟一值的。這些函數用不一樣的方法來處理重複的值。RANK()函數保留列表中行的位置序號,對於每一個重複的值,該函數會跳過下面與其相鄰的值,因而就能夠將下一個不重複的值保留在正確的位置上。
其行爲相似於短跑比賽中的並列成績。例如劉翔與Dayron Robles(古巴)在110欄的比賽中都跑出了12’92的成績,那他們就是並列第一,而其後的一名選手將會得到第三名的成績。
SELECT ProductID ,Name ,ListPrice ,RANK() OVER (ORDER BY ListPrice DESC) AS [Rank] FROM Production.Product ORDER BY [Rank] |
注意在下圖的結果列表中,重複的價格值所對應的結果是相同的,而每一個鏈接以後的值都被跳過了。好比,產品"Road-150 Red, 52"和"Road-150 Red, 56"都排在第1,而接下來的行"Mountain-100 Silver,38"就排在第6了。
DENSE_RANK()函數的工做方式與RANK()函數相同,不過它不會跳過每一個鏈接後的值,這樣就不會有值被跳過了,可是在鏈接處排列序號位置將會丟失。
SELECT ProductID ,Name ,ListPrice ,DENSE_RANK() OVER (ORDER BY ListPrice DESC) AS [Rank] FROM Production.Product ORDER BY [Rank] |
下圖的結果重複了排列值,可是不會跳過列中的任何數字。
NTILE(n)函數
這個函數也用於對結果進行排列,並返回一個整型的排列值,可是它不會對結果以惟一的排列順序進行枚舉,而是將結果切分爲有限數量的排列組。好比,一個表有10 000行,使用1000爲參數值調用NTILE()函數,即NTILE(1000),並將結果分紅以10爲單位的1000個組,每一個組賦予相同的排列值。和本節討論的其餘排列函數同樣,NTILE()函數也支持OVER(ORDER BY…)語法。下面的例子根據產品價格,按照從高到低的順序把Product表分爲50組產品:
SELECT ProductID ,Name ,ListPrice ,NTILE(50) OVER (ORDER BY ListPrice DESC) AS GroupedProducts FROM Production.Product ORDER BY GroupedProducts |
結果爲:
安全函數
與安全相關的函數返回SQL Server用戶的角色成員和權限信息。這類函數也包括一組管理事件與跟蹤的函數。下表顯示了這些函數:
函 數 |
說 明 |
fn_trace_geteventinfo() |
爲指定的跟蹤ID返回一個填充事件信息的表類型值 |
fn_trace_getfilterinfo() |
爲指定的跟蹤ID返回一個填充與過濾器有關的信息的表類型值 |
fn_trace_getinfo() |
爲指定的跟蹤ID返回一個填充跟蹤信息的表類型值 |
fn_trace_getable() |
爲指定的跟蹤ID返回一個填充文件信息的表類型值 |
HAS_DBACCESS() |
返回一個代表當前用戶是否有訪問指定數據庫權限的標誌 |
IS_MEMBER() |
返回一個代表當前用戶是Windows組用戶仍是SQL Server用戶的標誌 |
IS_SRVROLEMEMBER() |
返回一個代表當前用戶是不是數據庫服務器角色成員的標誌 |
SUSER_SID() |
返回指定用戶的登陸名的安全ID,或者(若是參數被忽略)返回當前用戶的安全ID。返回指定用戶的用戶ID,或者(若是參數被忽略的話)返回當前用戶的用戶ID |
SUSER_SNAME() |
返回指定安全ID的登陸名。若是不提供任何安全ID,則返回當前用戶的登陸名 |
USER_ID() |
返回指定用戶名的用戶ID,或者(若是參數被忽略的話)返回當前用戶的用戶ID |
USER_NAME() |
返回指定用戶ID的用戶名 |
系統函數與系統變量
本節討論具備多種用途的工具函數,包括值比較、值類型測試等功能。這個類別的函數也包羅了其餘函數:
函 數 |
說 明 |
APP_NAME() |
返回與當前鏈接相關聯的應用程序的名字 |
COALESCE() |
從以逗號分隔的表達式列表中返回第一個非空值 |
COLLATIONPROPERTY() |
返回一個特定字符集排序規則的特定屬性的值。這些屬性包括CodePage、LCID、ComparisonStyle |
CURRENT_TIMESTAMP() |
返回當前日期與時間。和GETDATE()函數是同義的。這個函數的存在只是爲了與ANSI-SQL兼容 |
C1UJRRENT_USER() |
返回當前用戶的名字。與USER_NAME()函數相同 |
DATALENGTH() |
返回存儲或處理一個值所需的字節數。對於ANSI字符串類型,這個函數返回的值與LEN()函數相同,但對於其餘數據類型而言就可能不必定相同了 |
fn_helpcollations() |
返回一個填充有由當前SQLSewer版本支持的字符集排序規則的表類型值 |
fn_servershareddrives() |
返回一個填充有服務器共享的驅動列表的表類型值 |
fn_virtualfilestats() |
返回一個填充有包括日誌文件在內數據庫文件的I/O狀態的表類型值 |
FORMATMESSAGE() |
從sysmessages表中爲指定的信息代碼和以逗號分隔的參數列表返回錯誤信息 |
GETANSINULL() |
根據ANSLNULL_DFLT_ON與ANSLNULL_DFLT_OFF數據庫設置返回數據庫的可空性設置 |
HOST_ID() |
返回當前會話的工做站ID |
HOST_NAME() |
返回當前會話的工做站名 |
IDENT_CURRENT() |
返回最後一個爲指定的表生成的標識(ID)值。與會話、範圍無關 |
IDENT_INCR() |
返回最後一次建立的標識(ID)列中定義的增量值 |
IDENT_SEED() |
返回最後一次建立的標識(ID)列中定義的種子值 |
IDENTITY() |
用在SELECT…INTO語句中,在一個列中插入自動生成的標識值 |
ISDATE() |
返回一個代表指定的值是否可被轉換爲日期值的標誌 |
ISNULL() |
判斷指定的值是不是空值,而後返回一個事先提供的替代值 |
ISNUMERIC() |
返回一個代表指定的值是否可被轉換爲數字值的標誌 |
NEWID() |
返回一個新生成的UniqueIdentifier類型的值。這是一個128位的整型、全球惟一的值,一般以字母或數字十六進制來表示(如89DE6247·C2E242DB-8CE8·A787E505D7EA)。這個類型常常被用做複製的和半鏈接系統中的主鍵. |
NULLIF() |
兩個特定的參數的值若是是相同的,則返回NULL |
PARSENAME() |
返回一個具備4部分對象名的特定部分 |
PERMISSIONS() |
返回一個整型值,該值是一個表示當前用戶在指定的數據庫對象上權限或者權限組合的位映像 |
ROWCOUNT_BIG() |
與@@RowCount變量同樣,這個函數返回被最後一條語句修改或返回的行數量。返回值類型是bigint |
SCOPE_IDENTITY() |
與@@IDENTIY變量同樣,這個函數返回限制在當前會話與範圍內的最後一次生成的標識值 |
SERVERPROPERTY() |
返回一個表示服務器屬性狀態的標記。屬性包括Collation、Edition、EngineEdition、InstanceName、IsClustered、IsFullTextInstalled、IsIntegrated- SecurityOnly、IsSingleUser、IsSyncWithBackup、LicenseTYpe、MachineName、NumLicenses、ProcessID、ProductLevel、ProductVersion、ServerName |
SESSION_USER |
返回當前用戶名。調用本函數不須要括號 |
SESSIONPROPERTY() |
返回表示一個會話屬性狀態的標記。屬性包括:ANSL_NULLS,ANSI_PADDING,ANSL_WARNINGS,ARITHABORT,CONCAT_NULL_ YIELDS_NULL,NUMERIC_ROUNDABORT,QUOTED_IDENTIFIER |
STATS_DATE() |
返回指定的索引統計信息最後一次被更新的時間 |
SYSTEM_USER |
返回當前用戶名。調用本函數不須要括號 |
USER_NAME() |
爲一個指定的用戶ID返回用戶名。若是沒有提供ID號則返回當前的數據庫用戶 |
COALESCE()函數
COALESCE()函數是很是有用的,它返回其參數中第一個非空表達式。它可以節省頗多IF或者CASE分支邏輯。如下例子用產品數據填充一個表,每一個產品最多有3種價格:
CREATE TABLE #ProductPrices (ProductName varchar(25), SuperSalePriceMoney NULL, SalePrice Money NULL, ListPrice Money NULL) GO INSERT INTO #ProductPrices VALUES('Standard Widget', NULL, NULL,15.95) INSERT INTO #ProductPrices VALUES('Economy Widget', NULL, 9.95, 12.95) INSERT INTO #ProductPrices VALUES('Deluxe Widget', 19.95, 20.95,22.95) INSERT INTO #ProductPrices VALUES('Super Deluxe Widget', 29.45, 32.45,38.95) INSERT INTO #ProductPrices VALUES('Executive Widget', NULL, 45.95,54.95) GO |
全部的產品都有訂價,有些有銷售價,有些還有促銷價。一項產品的當前價格是全部己有價格的最低價,或者在讀取每一個價格列時以列出順序讀到的第一個非空值:
SELECT ProductName, COALESCE(SuperSalePrice, SalePrice, ListPrice) ASCurrentPrice FROM #ProductPrices |
這個方法比使用多行分支與判斷邏輯要簡潔得多,而結果也是一樣簡單,以下圖所示:
DATALENGTH()函數
DATALENGTH()函數返回一個用於對值進行管理的字節數,這有助於揭示不一樣數據類型間的一些有趣差異。當把varchar類型傳遞給DATALENGTH()和LEN()函數時,它們將返回相同的值:
DECLARE @Value varchar(20) SET @Value = 'abc' SELECT DATALENGTH(@Value) SELECT LEN(@Value) |
這些語句的返回值都爲3。由於varchar類型使用了3個單字節字符來存儲三個字符的值。然而,若是使用nVarchar類型來管理相同長度的值,就要佔用多一倍的字節:
DECLARE @Value nvarchar(20) SET @Value = 'abc' SELECT DATALENGTH(@Value) SELECT LEN(@Value) |
DATALENGTH()函數返回值爲6,由於每一個使用Unicode字符集的字符都要佔用2個字節。LEN()函數返回值爲3,由於這個函數返回字符數,不是字節數。如下是一個有趣的測試:要存儲一個值爲2的整型變量,要佔用多少個字節?而若是要存儲一個值爲20億的整型變量,又將佔用多少個字節呢?試一下:
DECLARE @Value1 int, @Value2 int SET @Value1 = 2 SET @Value2 = 2000000000 SELECT DATALENGTH(@Value1) SELECT LEN(@Value1) SELECT DATALENGTH(@Value2) SELECT LEN(@Value2) |
在這兩種狀況下,DATALENGTH()函數都返回4。由於int類型不論值是多少,老是使用4個字節。LEN()函數本質上將整型值當成已轉換成字符型的數據來處理,因此,在這個例子中,它分別返回1和10,即值的位數。
在下表中的全局系統變量都將返回int類型的值。這些變量可用於存儲過程和其餘實現定製業務邏輯的編程對象。
變 量 |
說 明 |
@@ERROR |
當前會話最後一次發生的錯誤代碼 |
@@IDENTITY |
當前會話最後一次生成的標識值 |
@@ROWCOUNT |
當前會話中最後一次返回結果集的執行操做所返回的行數 |
@@TRANCOUNT |
當前會話中活動的事務數。這是在執行相關的COMMIT TRANSACTION或者ABORT TRANSACTION語句以前嵌套的多個BEGIN TRANSACTION語句的結果 |
系通通計變量
下表描述了用於肯定數據庫系統使用信息與環境信息的管理工具:
變 量 |
說 明 |
@@CONNECTIONS |
返回打開鏈接的次數 |
@@CPU_BUSY |
從上次啓動服務器開始,SQL Server一共工做的毫秒數 |
@@IDLE |
從上次啓動服務器開始,SQL Server一共空閒的毫秒數 |
@@IO_BUSY |
從上次啓動服務器開始,SQL Server一共處理I/0的毫秒數 |
@@PACK_RECEIVED |
從上次啓動服務器開始,SQL Server一共收到的網絡數據包數 |
@@PACK_SENT |
從上次啓動服務器開始,SQL Server一共發送的網絡數據包數 |
@@PACKET_ERRORS |
從上次啓動服務器開始,SQL Server一共收到的網絡數據包錯誤數 |
@@TIMETICKS |
每一個時鐘滴答有多少毫秒 |
@@TOTAL_ERRORS |
從上次啓動服務器開始,SQL Server一共收到的磁盤I/O錯誤數 |
@@TOTAL_READ |
從上次啓動服務器開始,SQL Server一共進行的物理磁盤讀取次數 |
@@TOTAL_WRITE |
從上次啓動服務器開始,SQL Server一共進行的物理磁盤寫入次數 |