using …

Pure C#

‘Case When’ Kategorisi için Arşiv

Dynamic/Conditional Order By Clause in SQL Server/T-SQL

Yazan: esersahin 11/11/2009

http://www.dominicpettifer.co.uk/Blog/21/dynamic-conditional-order-by-clause-in-sql-server-t-sql

Technical Article – This article shows you how to achieve a conditional Order By clause in your SQL Server Stored Procedures, without having to resort to inline SQL in your applications, or Dynamic SQL (yuck!) in your Stored Procedures.

Inline SQL or Stored Procedures

You’ve probably come across times when you needed to sort a recordset dynamically in a Stored Procedure based on an input parameter. For instance, if you’re displaying records on a webpage with sortable columns, like on most ecommerce sites. You could use an inline SQL statement dynamically built in your ASP/PHP server-side code with string concatenation eg:

string sql = "SELECT * FROM Products ORDER BY ";

if(Request.QueryString["orderBy"] == "NameAsc")
{
sql += "Name ASC";
}
else if(Request.QueryString["orderBy"] == "NameDesc")
{
sql += "Name DESC";
}
else if(......etc etc

However, you’re probably using a Stored Procedure for reasons such as performance (execution plan is cached in a Sproc), maintainability (string concatenation can get messy with complex queries), prevent security vulnerabilities (such as SQL Injection), and maybe you want to achieve efficient database tier paging using SQL Server 2005’s ROW_NUMBER() function.

Introducing the CASE Function

The trick is to use the CASE function, but there are quirks with this that can trip you up. First of all you declare a Stored Procedure with an Order By input parameter and apply the CASE to the Order By clause like this:

CREATE PROCEDURE GetProducts
(
@OrderBy      VARCHAR(50),
@Input2       VARCHAR(30)
)
AS
BEGIN
SET NOCOUNT ON

SELECT Id, ProductName, Description, Price, Quantity
FROM Products
WHERE ProductName LIKE @Input2
ORDER BY
CASE
WHEN @OrderBy = 'ProductNameAsc' THEN ProductName
END ASC,
CASE
WHEN @OrderBy = 'ProductNameDesc' THEN ProductName
END DESC

END

Ascending and Descending actions need to be grouped into separate CASE statements, separated with a comma. In your server-side code/script make sure to append ‘Asc’ or ‘Desc’ onto the order by string, or you could have two Stored procedure input parameters for column name and order by direction if you want.

Multiple Columns with Different DataTypes

You’ll hit problems when you try to include multiple columns with different data types (VARCHAR, INT etc.). Eg:

ORDER BY
CASE
WHEN @OrderBy = 'ProductNameAsc' THEN ProductName
WHEN @OrderBy = 'QuantityAsc' THEN Quantity
END ASC

…will throw an error because ProductName is a VARCHAR and Quantity is an INT…

Conversion failed when converting the nvarchar value ‘Value’ to data type int.
Warning: Null value is eliminated by an aggregate or other SET operation.

I’ve seen people wrap the column around a convert function eg.

WHEN @OrderBy = 'QuantityAsc' THEN CONVERT(VARCHAR(32), Quantity)

DON’T do this as the order by will be different as a string versus it’s original data type (eg. 30 will come before 4 as the first character 3 is earlier in the alphebet then 4). This is especially true with DATETIME types as the CONVERT function could convert to into any number of date time formats (2009/01/15, 01/15/2009 etc.)

Instead you have to separate each datatype into separate groups of case statements like this:

SELECT Id, ProductName, Description, Price, Quantity
FROM Products
WHERE ProductName LIKE @Input2
ORDER BY
CASE –- VARCHAR types ascending
WHEN @OrderBy = 'ProductNameAsc' THEN ProductName
END ASC,
CASE –- VARCHAR types descending
WHEN @OrderBy = 'ProductNameDesc' THEN ProductName
END DESC,
CASE -- INT types ascending
WHEN @OrderBy = 'QuantityAsc' THEN Quantity
END ASC,
CASE -- INT types descending
WHEN @OrderBy = 'QuantityDesc' THEN Quantity
END DESC,
CASE -- MONEY types ascending
WHEN @OrderBy = 'PriceAsc' THEN Price
END ASC,
CASE -- MONEY types descending
WHEN @OrderBy = 'PriceDesc' THEN Price
END DESC,
CASE -- Default order by
WHEN 1 = 1 THEN ProductName
END ASC

Remember to provide a default Order By for when none of the CASE statements match.

Paging With ROW_NUMBER()

You can also use this technique with database tier paging using the ROW_NUMBER() function in SQL Server 2005/2008:

SELECT
Id,
ProductName,
Deacription,
Quantity
FROM
(
SELECT ROW_NUMBER() OVER(
ORDER BY
CASE
WHEN @OrderBy = 'ProductNameAsc' THEN ProductName
END ASC,
CASE
WHEN @OrderBy = 'ProductNameDesc' THEN ProductName
END DESC,
CASE
WHEN 1 = 1 THEN ProductName
END ASC,
//-- Snip --//
) AS RowNumber,
Id,
ProductName,
Description,
Price,
Quantity
FROM Products
WHERE ProductName LIKE @Input2
) i
WHERE RowNmber BETWEEN @FirstRecord AND @LastRecord
ORDER BY RowNumber ASC

Yazı kategorisi: Case When, ORDER BY | » yorum bırak;

Case in Where Clause

Yazan: esersahin 11/11/2009

http://p2p.wrox.com/sql-server-2000/832-case-where-clause.html

Hi everybody
Can I use case statement in WHERE clause.
For example

DECLARE @VAR1 nvarchar(50)
DECLARE @VAR2 nvarchar(50)
SELECT * FROM ORDERS
WHERE CASE @VAR1
          WHEN ‘Customers’ THEN CustomerID = @VAR2
          WHEN ‘Employee’ THEN EmployeeID = @VAR2
          END

This is not working OK.
Can someone explain me the right way?
Thanks.
Best regards
Alex

 

In your situation, where you wish to select on two entirely different columns depending on a parameter, it is probably better to use an IF/ELSE construction, as:

Code:
IF @Var1='Customers' THEN
   SELECT * FROM Orders WHERE CustomerID=@Var2;
ELSE
   SELECT * FROM Orders WHERE EmployeeID=@Var2;

It’s important to realize that CASE is an expression and not an executable statement.

The general form of the predicate of a WHERE clause is:

Code:
 expression operator expression

where operator is = or <> or > or < etc. The WHERE clause in your query does not follow this form, which is why you were having problems.

If you insist on using the CASE expression, then you can try a couple of ugly alternatives, like:

Code:
   SELECT * FROM Orders
    WHERE CustomerID=CASE @Var1 WHEN 'Customers' THEN @Var2 ELSE 0 END
       OR EmployeeID=CASE @Var1 WHEN 'Employees' THEN @Var2 ELSE 0 END

assuming that 0 is not a legal value for either ‘CustomerID’ or ‘EmployeeID’. You could also try a nested CASE like:

Code:
   SELECT * FROM Orders
    WHERE 1 =
      CASE @Var1
         WHEN 'Customers' THEN CASE WHEN CustomerID=@Var2 THEN 1 ELSE 0 END
         WHEN 'Employees' THEN CASE WHEN EmployeeID=@Var2 THEN 1 ELSE 0 END
      END

I think that the CASE expression doesn’t work well in this situation and that the IF/ELSE is simpler and easier to understand.

Jeff Mason
Custom Apps, Inc.
www.custom-apps.com

I think this should work for you…

SELECT * FROM ORDERS
WHERE
(
  CASE
    WHEN @VAR1 = ‘Customers’ AND CustomerID = @VAR2 THEN 1
    WHEN @VAR1 = ‘Employee’ AND EmployeeID = @VAR2 THEN 1
    ELSE 0
  END
) = 1

or you could do this …

SELECT *
FROM ORDERS
WHERE (@VAR1 = ‘Customers’ and CustomerID = @VAR2) OR
      (@VAR1 = ‘Employee’ and EmployeeID = @VAR2)

Jeff’s first answer is best (though it doesn’t handle an illegal value for @VAR1) and SBacon’s answer is a close second, but Jeff’s other answers and Craig’s answer are all overkill.

Why not just:

Code:
DECLARE @VAR1 nvarchar(50)
DECLARE @VAR2 nvarchar(50)
SELECT * FROM ORDERS
WHERE @VAR2 = ( CASE @VAR1
                WHEN 'Customers' THEN CustomerID
                WHEN 'Employee'  THEN EmployeeID
                ELSE -1
                END )

The ELSE is in there to protect against an illegal value for @VAR1 of course. Jeff’s first answer is best (though it doesn’t handle an illegal value for @VAR1) and SBacon’s answer is a close second, but Jeff’s other answers and Craig’s answer are all overkill.

Why not just:

Code:
DECLARE @VAR1 nvarchar(50)
DECLARE @VAR2 nvarchar(50)
SELECT * FROM ORDERS
WHERE @VAR2 = ( CASE @VAR1
                WHEN 'Customers' THEN CustomerID
                WHEN 'Employee'  THEN EmployeeID
                ELSE -1
                END )

The ELSE is in there to protect against an illegal value for @VAR1 of course.

 

Quote:
quote:Originally posted by Old Pedant
 …Jeff’s other answers … are all overkill.

Well… I did say they were “…a couple of ugly alternatives…” ;)

(Any idea why we’re responding to a 5 year old original post? :D )

Jeff Mason
je.mason@comcast.net

> (Any idea why we’re responding to a 5 year old original post?

Ummm…because I inanely didn’t look at the original message date and because SBacon is a complete newbie and how the heck did he *find* this thread in the first place and… Well, maybe Google sent him this way??? But then why is he responding instead of asking? And… Ehhh, leave it at “because I inanely…”.

Yazı kategorisi: Case When, Where Clause | » yorum bırak;

Updating Data Using the CASE Expression

Yazan: esersahin 20/03/2009

http://www.java2s.com/Tutorial/SQLServer/0040__Insert-Delete-Update/UpdatingDataUsingtheCASEExpression.htm

6>
7> CREATE TABLE sales(
8>    stor_id char(4) NOT NULL,
9>    ord_num        varchar(20) NOT NULL,
10>    ord_date       datetime          NOT NULL,
11>    qty            smallint          NOT NULL,
12>    payterms       varchar(12) NOT NULL,
13>    title_id       varchar(80)
14> )
15> GO
1> insert sales values('1', 'QA7442.3', '09/13/94', 75, 'ON Billing','1')
2> insert sales values('2', 'D4482',    '09/14/94', 10, 'Net 60', '1')
3> insert sales values('3', 'N914008',  '09/14/94', 20, 'Net 30', '2')
4> insert sales values('4', 'N914014',  '09/14/94', 25, 'Net 30', '3')
5> insert sales values('5', '423LL922', '09/14/94', 15, 'ON Billing','3')
6> insert sales values('6', '423LL930', '09/14/94', 10, 'ON Billing','2')
7> GO

(1 rows affected)

(1 rows affected)

(1 rows affected)

(1 rows affected)

(1 rows affected)

(1 rows affected)
1>
2>
3> UPDATE sales
4>     SET payterms =
5>         CASE
6>             WHEN (SELECT SUM(qty) FROM sales s1
7> WHERE sales.stor_id = s1.stor_id) < 10
8>                 THEN 'On Billing'
9>             WHEN (SELECT SUM(qty) FROM sales s1
10> WHERE sales.stor_id = s1.stor_id) < 100
11>                 THEN 'Net 30'
12>             ELSE 'Net 60'
13> END
14>     GO

(6 rows affected)
1>
2> SELECT stor_id, SUBSTRING(ord_num,1,5) ord_num,
3>             ord_date, qty, payterms, title_id FROM sales
4>     GO
stor_id ord_num ord_date                qty    payterms     title_id
------- ------- ----------------------- ------ ------------ --------------------------------------------------------------------------------
1 QA744 1994-09-13 00:00:00.000     75 Net 30       1
2 D4482 1994-09-14 00:00:00.000     10 Net 30       1
3 N9140 1994-09-14 00:00:00.000     20 Net 30       2
4 N9140 1994-09-14 00:00:00.000     25 Net 30       3
5       423LL 1994-09-14 00:00:00.000     15 Net 30       3
6       423LL 1994-09-14 00:00:00.000     10 Net 30       2

(6 rows affected)
1>
2> drop table sales;
3> GO

Yazı kategorisi: Case When, Transact-SQL | » yorum bırak;