亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 開發 > 綜合 > 正文

數據庫查詢結果的動態排序

2024-07-21 02:39:42
字體:
來源:轉載
供稿:網友
在公共新聞組中,一個經常出現的問題是“怎樣才能根據傳遞給存儲過程的參數返回一個排序的輸出?”。在一些高水平專家的幫助之下,我整理出了這個問題的幾種解決方案。

一、用IF...ELSE執行預先編寫好的查詢


  對于大多數人來說,首先想到的做法也許是:通過IF...ELSE語句,執行幾個預先編寫好的查詢中的一個。例如,假設要從Northwind數據庫查詢得到一個貨主(Shipper)的排序列表,發出調用的代碼以存儲過程參數的形式指定一個列,存儲過程根據這個列排序輸出結果。Listing 1顯示了這種存儲過程的一個可能的實現(GetSortedShippers存儲過程)。


【Listing 1: 用IF...ELSE執行多個預先編寫好的查詢中的一個】


CREATE PROC GetSortedShippers

@OrdSeq AS int

AS


IF @OrdSeq = 1

SELECT * FROM Shippers ORDER BY ShipperID

ELSE IF @OrdSeq = 2

SELECT * FROM Shippers ORDER BY CompanyName

ELSE IF @OrdSeq = 3

SELECT * FROM Shippers ORDER BY Phone



  這種方法的優點是代碼很簡單、很輕易理解,SQL Server的查詢優化器能夠為每一個SELECT查詢創建一個查詢優化計劃,確保代碼具有最優的性能。這種方法最主要的缺點是,假如查詢的要求發生了改變,你必須修改多個獨立的SELECT查詢——在這里是三個。


二、用列名字作為參數

  另外一個選擇是讓查詢以參數的形式接收一個列名字。Listing 2顯示了修改后的GetSortedShippers存儲過程。CASE表達式根據接收到的參數,確定SQL Server在ORDER BY子句中使用哪一個列值。注重,ORDER BY子句中的表達式并未在SELECT清單中出現。在ANSI SQL-92標準中,ORDER BY子句中不答應出現沒有在SELECT清單中指定的表達式,但ANSI SQL-99標準答應。SQL Server一直答應這種用法。


【Listing 2:用列名字作為參數,第一次嘗試】


CREATE PROC GetSortedShippers

@ColName AS sysname

AS


SELECT *

FROM Shippers

ORDER BY

CASE @ColName

WHEN 'ShipperID' THEN ShipperID

WHEN 'CompanyName' THEN CompanyName

WHEN 'Phone' THEN Phone

ELSE NULL

END



  現在,我們來試一下新的存儲過程,以參數的形式指定ShipperID列:


EXEC GetSortedShippers 'ShipperID'



  此時一切正常。但是,當我們視圖把CompanyName列作為參數調用存儲過程時,它不再有效:


EXEC GetSortedShippers 'CompanyName'



  仔細看一下錯誤信息:


Server: Msg 245, Level 16, State 1, Procedure GetSortedShippers, Line 5

Syntax error converting the nvarchar value 'Speedy

EXPress' to a column of data type int.



  它顯示出,SQL Server試圖把“Speedy Express”(nvarchar數據類型)轉換成一個整數值——當然,這個操作是不可能成功的。出現錯誤的原因在于,按照“數據類型優先級”規則,CASE表示式中最高優先級的數據類型決定了表達式返回值的數據類型。“數據類型優先級”規則可以在SQL Server Books Online(BOL)找到,它規定了int數據類型的優先級要比nvarchar數據類型高。前面的代碼要求SQL Server按照CompanyName排序輸出,CompanyName是nvarchar數據類型。這個CASE表達式的返回值可能是ShipperID(int類型),可能是CompanyName(nvarchar類型),或Phone(nvarchar類型)。由于int類型具有較高的優先級,因此CASE表達式返回值的數據類型應該是int。


為了避免出現這種轉換錯誤,我們可以嘗試把ShipperID轉換成varchar數據類型。
采用這種方法之后,nvarchar將作為最高優先級的數據類型被返回。Listing 3顯示了修改后的GetSortedShippers存儲過程。

【Listing 3:用列名字作為參數,第二次嘗試】


ALTER PROC GetSortedShippers

@ColName AS sysname

AS


SELECT *

FROM Shippers

ORDER BY

CASE @ColName

WHEN 'ShipperID'

THEN CAST(ShipperID AS varchar(11))

WHEN 'CompanyName'

THEN CompanyName

WHEN 'Phone'

THEN Phone

ELSE NULL

END



  現在,假設我們再把三個列名字中的任意一個作為參數調用存儲過程,輸出結果看起來正確??雌饋砭拖笾付ǖ牧姓_地為查詢輸出提供了排序標準。但這個表只有三個貨主,它們的ID分別是1、2、3。假設我們把更多的貨主加入到表,如Listing 4所示(ShipperID列有IDENTITY屬性,SQL Server自動為該列生成值)。


【Listing 4:向Shippers表插入一些記錄】


INSERT INTO Shippers VALUES('Shipper4', '(111) 222-9999')

INSERT INTO Shippers VALUES('Shipper5', '(111) 222-8888')

INSERT INTO Shippers VALUES('Shipper6', '(111) 222-7777')

INSERT INTO Shippers VALUES('Shipper7', '(111) 222-6666')

INSERT INTO Shippers VALUES('Shipper8', '(111) 222-5555')

INSERT INTO Shippers VALUES('Shipper9', '(111) 222-4444')

INSERT INTO Shippers VALUES('Shipper10', '(111) 222-3333')



  現在調用存儲過程,指定ShipperID作為排序列:


EXEC GetSortedShippers 'ShipperID'



  表一顯示了存儲過程的輸出。ShipperID等于10的記錄位置錯誤,因為這個存儲過程的排序輸出是字符排序,而不是整數排序。按照字符排序時,10排列在2的前面,因為10的開始字符是1。


表一:記錄排序錯誤的查詢結果


ShipperID CompanyName Phone

1 Speedy Express (503) 555-9831

10 Shipper10 (111) 222-3333

2 United Package (503) 555-3199

3 Federal Shipping (503) 555-9931

4 Shipper4 (111) 222-9999

5 Shipper5 (111) 222-8888

6 Shipper6 (111) 222-7777

7 Shipper7 (111) 222-6666

8 Shipper8 (111) 222-5555

9 Shipper9 (111) 222-4444


為了解決這個問題,我們可以用前置的0補足ShipperID值,使得ShipperID值都有同樣的長度。按照這種方法,基于字符的排序具有和整數排序同樣的輸出結果。修改后的存儲過程如Listing 5所示。十個0被置于ShipperID的絕對值之前,而在結果中,代碼只是使用最右邊的10個字符。SIGN函數確定在正數的前面加上加號(+)前綴,還是在負數的前面加上負號(-)前綴。按照這種方法,輸出結果總是有11個字符,包含一個“+”或“-”字符、前導的字符0以及ShipperID的絕對值。

【Listing 5:用列名字作為參數,第三次嘗試】


ALTER PROC GetSortedShippers

@ColName AS sysname

AS


SELECT *

FROM Shippers

ORDER BY

CASE @ColName

WHEN 'ShipperID' THEN CASE SIGN(ShipperID)

WHEN -1 THEN '-'

WHEN 0 THEN '+'

WHEN 1 THEN '+'

ELSE NULL

END +

RIGHT(REPLICATE('0', 10) +

CAST(ABS(ShipperID) AS varchar(10)), 10)

WHEN 'CompanyName' THEN CompanyName

WHEN 'Phone' THEN Phone

ELSE NULL

END



  假如ShipperID的值都是正數,加上符號前綴就沒有必要,但為了讓方案適用于盡可能多的范圍,本例加上了符號前綴。排序時“-”在“+”的前面,所以它可以用于正、負數混雜排序的情況。



  現在,假如我們用任意三個列名字之一作為參數調用存儲過程,存儲過程都能夠正確地返回結果。Richard Romley提出了一種巧妙的處理方法,如Listing 6所示。它不再要求我們搞清楚可能涉及的列數據類型。這種方法把ORDER BY子句分成三個獨立的CASE表達式,每一個表達式處理一個不同的列,避免了由于CASE只返回一種特定數據類型的能力而導致的問題。


【Listing 6:用列名字作為參數,Romley提出的方法】


ALTER PROC GetSortedShippers

@ColName AS sysname

AS


SELECT *

FROM Shippers

ORDER BY

CASE @ColName WHEN 'ShipperID'

THEN ShipperID ELSE NULL END,

CASE @ColName WHEN 'CompanyName'

THEN CompanyName ELSE NULL END,

CASE @ColName WHEN 'Phone'

THEN Phone ELSE NULL END



  按照這種方法編寫代碼,SQL Server能夠為每一個CASE表達式返回恰當的數據類型,而且無需進行數據類型轉換。但應該注重的是,只有當指定的列不需要進行計算時,索引才能夠優化排序操作。


三、用列號作為參數

  就象第一個方案所顯示地那樣,你也許更喜歡用列的編號作為參數,而不是使用列的名字(列的編號即一個代表你想要作為排序依據的列的數字)。這種方法的基本思想與使用列名字作為參數的思想一樣:CASE表達式根據指定的列號確定使用哪一個列進行排序。Listing 7顯示了修改后的GetSortedShippers存儲過程。


【Listing 7:用列號作為參數】


ALTER PROC GetSortedShippers

@ColNumber AS int

AS


SELECT *

FROM Shippers

ORDER BY

CASE @ColNumber

WHEN 1 THEN CASE SIGN(ShipperID)

WHEN -1 THEN '-'

WHEN 0 THEN '+'

WHEN 1 THEN '+'

ELSE NULL

END +

RIGHT(REPLICATE('0', 10) +

CAST(ABS(ShipperID) AS varchar(10)), 10)

WHEN 2 THEN CompanyName

WHEN 3 THEN Phone

ELSE NULL

END



  當然,在這里你也可以使用Richard的方法,避免ORDER BY子句中列數據類型帶來的問題。假如要根據ShipperID排序輸出,你可以按照下面的方式調用修改后的GetSortedShippers存儲過程:


EXEC GetSortedShippers 1


四、動態執行

  使用動態執行技術,我們能夠更輕松地編寫出GetSortedShippers存儲過程。使用這種方法時,我們只需動態地構造出SELECT語句,然后用EXEC()命令執行這個SELECT語句。假設傳遞給存儲過程的參數是列的名字,存儲過程可以大大縮短:


ALTER PROC GetSortedShippers

@ColName AS sysname

AS

EXEC('SELECT * FROM Shippers ORDER BY ' +

@ColName)



  在SQL Server 2000和7.0中,你可以用系統存儲過程sp_ExecuteSQL替代Exec()命令。BOL說明了使用sp_ExecuteSQL比使用Exec()命令更有利的地方。一般地,假如滿足以下三個條件,你能夠在不授予存儲過程所涉及對象權限的情況下,授予執行存儲過程的權限:首先,只使用Data Manipulation Language(DML)語言(即SELECT,INSERT,UPDATE,DELETE);其次,所有被引用的對象都有與存儲過程同樣的所有者;第三,沒有使用動態命令。


  上面的存儲過程不能滿足第三個條件。在這種情況下,你必須為所有需要使用存儲過程的用戶和組顯式地授予Shippers表的SELECT權限。假如這一點可以接受的話,一切不存在問題。類似地,你可以修改存儲過程,使它接受一個列號參數,如Listing 8所示。


【Listing 8:用列號作為參數,動態執行(代碼較長的方法)】


ALTER PROC GetSortedShippers

@ColNumber AS int

AS


DECLARE @cmd AS varchar(8000)


SET @cmd = 'SELECT * FROM Shippers ORDER BY ' +

CASE @ColNumber

WHEN 1 THEN 'ShipperID'

WHEN 2 THEN 'CompanyName'

WHEN 3 THEN 'Phone'

ELSE 'NULL'

END


EXEC(@cmd)



  注重,當你使用了函數時,你應該在一個變量而不是EXEC()命令內構造SELECT語句。
此時,CASE表達式動態地確定使用哪一個列。還有一種更簡短的格式,T-SQL答應在ORDER BY子句中指定SELECT清單中列的位置,如Listing 9所示。這種格式遵從了SQL-92標準,但ANSI SQL-99標準不支持這種格式,所以最好不要使用這種格式。


【Listing 9:列號作為參數,動態執行(代碼較短的方法)】


ALTER PROC GetSortedShippers

@ColNumber AS int

AS

DECLARE @cmd AS varchar(8000)

SET @cmd = 'SELECT * FROM Shippers ORDER BY ' + CAST(@ColNumber AS varchar(4))


EXEC(@cmd)


五、用戶定義函數

  假如你使用的是SQL Server 2000,想要編寫一個用戶定義的函數(UDF),這個用戶定義函數接受列的名字或編號為參數、返回排序的結果集,Listing 10顯示了大多數程序員當成第一選擇的方法。


【Listing 10:列名字作為參數,使用UDF】


CREATE FUNCTION ufn_GetSortedShippers

(

@ColName AS sysname

)

RETURNS TABLE

AS


RETURN

SELECT *

FROM Shippers

ORDER BY

CASE @ColName

WHEN 'ShipperID' THEN CASE SIGN(ShipperID)

WHEN -1 THEN '-'

WHEN 0 THEN '+'

WHEN 1 THEN '+'

ELSE NULL

END +

RIGHT(REPLICATE('0', 10) +

CAST(ABS(ShipperID) AS

varchar(10)), 10)

WHEN 'CompanyName' THEN CompanyName

WHEN 'Phone' THEN Phone

ELSE NULL

END



  但是,SQL Server不接受這個函數,它將返回如下錯誤信息:


Server: Msg 1033, Level 15, State 1, Procedure ufn_GetSortedShippers,

Line 24

The ORDER BY clause is invalid in views, inline functions, and

subqueries, unless TOP is also specified.



  注重錯誤信息中的“unless”。SQL Server 2000不答應在視圖、嵌入式UDF、子查詢中出現ORDER BY子句,因為它們都應該返回一個表,表不能指定行的次序。然而,假如使用了TOP要害詞,ORDER BY子句將幫助確定查詢所返回的行。因此,假如指定了TOP,你還可以同時指定ORDER BY。由于在帶有TOP的UDF中答應使用ORDER BY子句,你可以使用一個技巧:把“SELECT *”替換成“SELECT TOP 100 PERCENT *”。這樣,你就能夠成功地構造出一個接受列名字或編號為參數、返回排序結果的函數。


  新構造的函數可以按照如下方式調用:


SELECT * FROM ufn_GetSortedShippers('ShipperID')



  現在,你已經了解了幾種用參數確定查詢輸出中記錄次序的方法。在編寫那些答應用戶指定查詢結果排序標準的列的應用程序時,你可以使用本文介紹的各種技術,用列名字或編號作為參數,構造出使用CASE表達式和動態執行能力的各種方案。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
日韩毛片中文字幕| 色偷偷偷亚洲综合网另类| 亚洲午夜小视频| 亚洲综合在线中文字幕| 成人性生交大片免费看视频直播| 日本中文字幕成人| 欧美电影免费观看| 在线性视频日韩欧美| 日韩免费在线电影| 国产一区二区欧美日韩| 欧美资源在线观看| 91福利视频网| 欧美—级高清免费播放| 欧美激情中文字幕在线| 欧美高清自拍一区| 日韩成人xxxx| 欧美中文在线免费| 国产一区二区美女视频| 欧美洲成人男女午夜视频| 亚洲电影免费观看高清完整版在线| 美女av一区二区| 最近2019中文字幕在线高清| 色婷婷成人综合| 欧美黄网免费在线观看| 成人在线视频福利| 亚洲精品免费一区二区三区| 欧美激情a在线| 日韩最新免费不卡| 亚洲一区国产精品| 欧美噜噜久久久xxx| 米奇精品一区二区三区在线观看| 日本三级韩国三级久久| 亚洲美女av黄| 欧美俄罗斯乱妇| 日韩毛片中文字幕| 91精品国产91久久久久| 久久色在线播放| 国产精品极品尤物在线观看| 最近2019年中文视频免费在线观看| 精品国模在线视频| 成人av资源在线播放| 午夜精品久久17c| 911国产网站尤物在线观看| 国产精品福利在线观看网址| 日韩av资源在线播放| 国产精品久久久久久av| 久久亚洲一区二区三区四区五区高| 国产精品久久久久久久久久久久| 久久久久久久久国产精品| 亚洲午夜av电影| 亚洲天堂免费在线| 欧美一级淫片aaaaaaa视频| 日韩欧美黄色动漫| 成人看片人aa| 亚洲香蕉伊综合在人在线视看| 亚洲福利在线看| 亚洲欧美激情另类校园| 日韩欧美在线视频免费观看| 精品视频—区二区三区免费| 日韩在线国产精品| 国产精品久久久久影院日本| 欧美与黑人午夜性猛交久久久| 欧美精品在线观看| 亚洲在线www| 精品视频9999| 性欧美亚洲xxxx乳在线观看| 欧美视频精品一区| 亚洲美女在线观看| 综合国产在线观看| 国产精品久久久久免费a∨| 欧美福利在线观看| 亚洲成人激情小说| 日韩最新中文字幕电影免费看| 国产亚洲美女精品久久久| 富二代精品短视频| 懂色aⅴ精品一区二区三区蜜月| 在线观看日韩视频| 亚洲欧美精品一区| 国产区亚洲区欧美区| 欧美性xxxx在线播放| 欧美大片免费观看在线观看网站推荐| 国外成人在线播放| 亚洲精品日韩在线| 日本精品久久久| 久久精品视频网站| 欧美裸身视频免费观看| 日韩av在线免播放器| 麻豆国产va免费精品高清在线| 久久激情五月丁香伊人| 国产精品自产拍在线观看| 精品视频在线播放色网色视频| 中文字幕亚洲欧美日韩在线不卡| 日韩电影中文字幕| 久久国产天堂福利天堂| 97免费中文视频在线观看| 亚洲欧美日韩在线高清直播| 精品夜色国产国偷在线| 91九色视频导航| 91社影院在线观看| 这里只有视频精品| 久久久久久久爱| 欧美日韩在线免费| 国语自产精品视频在免费| 久久久久久久91| 91精品久久久久久久久久入口| 欧美一区三区三区高中清蜜桃| 秋霞av国产精品一区| 日韩精品在线免费| 九九久久综合网站| 中文字幕精品影院| 亚洲电影av在线| 欧美成人精品在线| 伊人久久久久久久久久久久久| 国产精品成人观看视频国产奇米| 欧美综合激情网| 欧美激情啊啊啊| 亚洲综合成人婷婷小说| 欧美激情视频给我| 成人在线视频网| www.亚洲免费视频| 久久久久久久久亚洲| 亚洲国产小视频在线观看| 国产精品www| 国产精品久久久久91| 成人免费高清完整版在线观看| 色噜噜国产精品视频一区二区| 精品伊人久久97| 亚洲视频在线播放| 久久视频在线观看免费| 欧美乱大交做爰xxxⅹ性3| 亚洲乱码av中文一区二区| 亚洲女人被黑人巨大进入| 这里只有精品久久| 亚洲人成77777在线观看网| 国产精品老牛影院在线观看| 久久久久这里只有精品| 久久99久久亚洲国产| 亚洲精品国产精品国自产在线| 91久久久国产精品| 97涩涩爰在线观看亚洲| 久99九色视频在线观看| 91中文字幕在线观看| 北条麻妃一区二区三区中文字幕| 国产一区二区三区丝袜| 亚洲最大成人免费视频| 国产精品爽爽ⅴa在线观看| 色综合视频一区中文字幕| 久久久久久高潮国产精品视| 日韩在线播放视频| 亚洲精品久久久久久久久久久久| 欧美一级黄色网| 亚洲欧美精品中文字幕在线| 亚洲成人免费网站| 成人免费在线视频网址| 亚洲欧洲中文天堂| 精品国产鲁一鲁一区二区张丽| 欧美精品一本久久男人的天堂| 国产一区二区在线免费视频| 日韩国产一区三区| 久久精品电影网站| 成人日韩在线电影| 国产亚洲激情视频在线| 亚洲变态欧美另类捆绑| 亚洲国产精品电影在线观看|