[英]T-SQL Conditional Order By
我正在嘗試編寫一個存儲過程,該存儲過程返回一個對象列表,其中包含用戶選擇的排序順序和排序方向,並作為sql參數傳入。
假設我有一個包含以下列的產品表:product_id(int),name(varchar),value(int),created_date(datetime)和參數@sortDir和@sortOrder
我想做點什么
select *
from Product
if (@sortOrder = 'name' and @sortDir = 'asc')
then order by name asc
if (@sortOrder = 'created_date' and @sortDir = 'asc')
then order by created_date asc
if (@sortOrder = 'name' and @sortDir = 'desc')
then order by name desc
if (@sortOrder = 'created_date' and @sortDir = 'desc')
then order by created_date desc
我嘗試用case語句做,但由於數據類型不同而遇到了問題。 有人有什么建議嗎?
CASE
是一個返回值的表達式。 它不像IF
那樣用於流量控制。 並且您不能在查詢中使用IF
。
不幸的是, CASE
表達式存在一些限制,使得執行您想要的操作變得很麻煩。 例如, CASE
表達式中的所有分支必須返回相同的類型,或者可以隱式轉換為相同的類型。 我不會嘗試使用字符串和日期。 您也無法使用CASE
指定排序方向。
SELECT column_list_please
FROM dbo.Product -- dbo prefix please
ORDER BY
CASE WHEN @sortDir = 'asc' AND @sortOrder = 'name' THEN name END,
CASE WHEN @sortDir = 'asc' AND @sortOrder = 'created_date' THEN created_date END,
CASE WHEN @sortDir = 'desc' AND @sortOrder = 'name' THEN name END DESC,
CASE WHEN @sortDir = 'desc' AND @sortOrder = 'created_date' THEN created_date END DESC;
一個可以說更簡單的解決方案(特別是如果這變得更復雜)是使用動態SQL。 要阻止SQL注入,您可以測試值:
IF @sortDir NOT IN ('asc', 'desc')
OR @sortOrder NOT IN ('name', 'created_date')
BEGIN
RAISERROR('Invalid params', 11, 1);
RETURN;
END
DECLARE @sql NVARCHAR(MAX) = N'SELECT column_list_please
FROM dbo.Product ORDER BY ' + @sortOrder + ' ' + @sortDir;
EXEC sp_executesql @sql;
動態SQL的另一個好處,盡管所有恐慌都在其中傳播:您可以為每種排序變化獲得最佳計划,而不是一個單一的計划,它將優化您最初使用的任何類型變化。 在我最近的一次性能比較中,它也表現得最好:
你需要一個case語句,雖然我會使用多個case語句:
order by (case when @sortOrder = 'name' and @sortDir = 'asc' then name end) asc,
(case when @sortOrder = 'name' and @sortDir = 'desc' then name end) desc,
(case when @sortOrder = 'created_date' and @sortDir = 'asc' then created_date end) asc,
(case when @sortOrder = 'created_date' and @sortDir = 'desc' then created_date end) desc
有四個不同的子句消除了類型之間轉換的問題。
有多種方法可以做到這一點。 一種方法是:
SELECT *
FROM
(
SELECT
ROW_NUMBER() OVER ( ORDER BY
CASE WHEN @sortOrder = 'name' and @sortDir = 'asc' then name
END ASC,
CASE WHEN @sortOrder = 'name' and @sortDir = 'desc' THEN name
END DESC,
CASE WHEN i(@sortOrder = 'created_date' and @sortDir = 'asc' THEN created_date
END ASC,
CASE WHEN i(@sortOrder = 'created_date' and @sortDir = 'desc' THEN created_date
END ASC) RowNum
*
)
order by
RowNum
您也可以使用動態sql來完成它。
declare @str varchar(max)
set @str = 'select * from Product order by ' + @sortOrder + ' ' + @sortDir
exec(@str)
當我嘗試做類似的事情時,我偶然發現了這一點,但是我創建的select語句包含了一個相當大且難看的字符串連接,嚴重限制了我使用動態SQL方法的能力。 如果您有簡單的搜索案例,這是一個不錯的解決方案。
話雖這么說,另一種在SQL服務器中執行此操作的方法是使用表變量。 這需要更多的開銷和復雜性,但它也為您提供了很大的靈活性。
DECLARE @RawProducts TABLE (
product_id int,
name varchar(50),
value int,
created_date datetime
)
INSERT INTO @RawProducts
SELECT * FROM [Product]
IF @sortOrder = 'name' AND @sortDir = 'asc' BEGIN
SELECT * FROM @RawProducts
ORDER BY [name] ASC
END
IF @sortOrder = 'name' AND @sortDir = 'desc' BEGIN
SELECT * FROM @RawProducts
ORDER BY [name] desc
END
IF @sortOrder = 'created_date' AND @sortDir = 'asc' BEGIN
SELECT * FROM @RawProducts
ORDER BY [created_date] ASC
END
IF @sortOrder = 'created_date' AND @sortDir = 'desc' BEGIN
SELECT * FROM @RawProducts
ORDER BY [created_date] desc
END
這種方法的一個缺點是,您需要為每個要訂購的案例添加一個案例塊,但是您必須使用任何不涉及動態SQL的解決方案。
我唯一一次推薦這種方法的方法是,如果你有一個相對較小的數據集,並且你很容易辨別搜索案例。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.