簡體   English   中英

T-SQL條件順序依據

[英]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的另一個好處,盡管所有恐慌都在其中傳播:您可以為每種排序變化獲得最佳計划,而不是一個單一的計划,它將優化您最初使用的任何類型變化。 在我最近的一次性能比較中,它也表現得最好:

http://sqlperformance.com/conditional-order-by

你需要一個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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM