一旦成功地從表中檢索出數據,就需要進一步操縱這些數據,以獲得有用或有意義的結果。這些要求包括:執行計算與數學運算、轉換數據、解析數值、組合值和聚合一個範圍內的值等。
下表給出了T-SQL函數的類別和描述。
函數類別 | 作用 |
聚合函數 | 執行的操作是將多個值合併爲一個值。例如 COUNT、SUM、MIN 和 MAX。 |
配置函數 | 是一種標量函數,可返回有關配置設置的信息。 |
轉換函數 | 將值從一種數據類型轉換爲另一種。 |
加密函數 | 支持加密、解密、數字簽名和數字簽名驗證。 |
遊標函數 | 返回有關遊標狀態的信息。 |
日期和時間函數 | 可以更改日期和時間的值。 |
數學函數 | 執行三角、幾何和其他數字運算。 |
元數據函數 | 返回數據庫和數據庫對象的屬性信息。 |
排名函數 | 是一種非確定性函數,可以返回分區中每一行的排名值。 |
行集函數 | 返回可在 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),Score decimal(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
0 收藏 |
Ctrl+Enter 發佈
發佈
取消