Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
Home
Discussion Groups
DB Engine
SQL ServerMSDESQL Server CE
Services
Analysis (Data Mining)Analysis (OLAP)DTSIntegration ServicesNotification ServicesReporting Services
Programming
CLRConnectivitySQLXML
Other Technologies
ClusteringEnglish QueryFull-Text SearchReplicationService Broker
General
Data WarehousingPerformanceSecuritySetupSQL Server ToolsOther SQL Server Topics
DirectoryUser Groups
Related Topics
MS AccessOther DB ProductsMS Server Products.NET DevelopmentVB DevelopmentJava DevelopmentMore Topics ...

SQL Server Forum / Programming / SQL / July 2008

Tip: Looking for answers? Try searching our database.

CASE Statement when ranges involved

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
Terry - 18 Jul 2008 13:15 GMT
I would like to convert the following VBA to T-SQL, the problem I have is
finding the correct syntax for specifying a range to be tested:

Regards

VBA------
   Select Case Val(ProductGroupNumber)
       Case 1000 To 1110
           Calc_MTA_Product_Groups = "n"
       Case 1150 To 1160, 1165 To 1201
           Calc_MTA_Product_Groups = "k"
       Case 1161 To 1162, 1350 To 1380, 1700 To 1800, 1915, 1990
           Calc_MTA_Product_Groups = "q"
       Case 1205 To 1320, 1500 To 1530, 1928
           Calc_MTA_Product_Groups = "e"
       Case 1450 To 1470
           Calc_MTA_Product_Groups = "h"
       Case 1475 To 1480
           Calc_MTA_Product_Groups = "f"
       Case 1550 To 1555
           Calc_MTA_Product_Groups = "g"
       Case 1560
           Calc_MTA_Product_Groups = "i"
       Case 1600
           Calc_MTA_Product_Groups = "l"
       Case Else
           Calc_MTA_Product_Groups = "x"
   End Select

I have tested out the following OK:

SELECT     Floyd.dbo.StockItem.ItemID AS mtapg_ItemID,
Floyd.dbo.StockItem.Code AS mtapg_ItemCode, CONVERT(int,
Floyd.dbo.ProductGroup.Code)
                     AS mtapg_ProductGroup, CASE CONVERT(int,
Floyd.dbo.ProductGroup.Code) WHEN 1000 THEN 'n' ELSE 'x' END AS
mtapg_EquipmentType
FROM         Floyd.dbo.StockItem INNER JOIN
                     Floyd.dbo.ProductGroup ON
Floyd.dbo.StockItem.ProductGroupID = Floyd.dbo.ProductGroup.ProductGroupID
Stuart Ainsworth - 18 Jul 2008 13:40 GMT
On Jul 18, 8:15 am, "Terry" <i...@REMOVETHISfloydautomatic.co.uk>
wrote:
> I would like to convert the following VBA to T-SQL, the problem I have is
> finding the correct syntax for specifying a range to be tested:
[quoted text clipped - 36 lines]
>                       Floyd.dbo.ProductGroup ON
> Floyd.dbo.StockItem.ProductGroupID = Floyd.dbo.ProductGroup.ProductGroupID

Have you tried looking at Books Online?  Not trying to be snarky, but
it's probably the most valuable (and least used) tool for SQL Server
professionals.

Anyway, here's the sample straight from there:

USE AdventureWorks;
GO
SELECT   ProductNumber, Name, 'Price Range' =
     CASE
        WHEN ListPrice =  0 THEN 'Mfg item - not for resale'
        WHEN ListPrice < 50 THEN 'Under $50'
        WHEN ListPrice >= 50 and ListPrice < 250 THEN 'Under $250'
        WHEN ListPrice >= 250 and ListPrice < 1000 THEN 'Under $1000'
        ELSE 'Over $1000'
     END
FROM Production.Product
ORDER BY ProductNumber ;
GO

You can also use OR's and AND's to create very complex WHEN clauses:

>         Case 1161 To 1162, 1350 To 1380, 1700 To 1800, 1915, 1990
>             Calc_MTA_Product_Groups = "q"

Calc_MTA_Product_Groups  =
WHEN ProductGroupNumber IN (1161,1162, 1915, 1990) OR
ProductGroupNumber BETWEEN 1350 AND 1380
   OR ProductGroupNumber BETWEEN 1700 AND 1800
THEN q END

HTH,
Stu
Terry - 18 Jul 2008 14:43 GMT
Hi Stuart,

Thanks for the examples, just what I needed.

regards

On Jul 18, 8:15 am, "Terry" <i...@REMOVETHISfloydautomatic.co.uk>
wrote:
> I would like to convert the following VBA to T-SQL, the problem I have is
> finding the correct syntax for specifying a range to be tested:
[quoted text clipped - 36 lines]
> Floyd.dbo.ProductGroup ON
> Floyd.dbo.StockItem.ProductGroupID = Floyd.dbo.ProductGroup.ProductGroupID

Have you tried looking at Books Online?  Not trying to be snarky, but
it's probably the most valuable (and least used) tool for SQL Server
professionals.

Anyway, here's the sample straight from there:

USE AdventureWorks;
GO
SELECT   ProductNumber, Name, 'Price Range' =
     CASE
        WHEN ListPrice =  0 THEN 'Mfg item - not for resale'
        WHEN ListPrice < 50 THEN 'Under $50'
        WHEN ListPrice >= 50 and ListPrice < 250 THEN 'Under $250'
        WHEN ListPrice >= 250 and ListPrice < 1000 THEN 'Under $1000'
        ELSE 'Over $1000'
     END
FROM Production.Product
ORDER BY ProductNumber ;
GO

You can also use OR's and AND's to create very complex WHEN clauses:

>         Case 1161 To 1162, 1350 To 1380, 1700 To 1800, 1915, 1990
>             Calc_MTA_Product_Groups = "q"

Calc_MTA_Product_Groups  =
WHEN ProductGroupNumber IN (1161,1162, 1915, 1990) OR
ProductGroupNumber BETWEEN 1350 AND 1380
   OR ProductGroupNumber BETWEEN 1700 AND 1800
THEN q END

HTH,
Stu
Plamen Ratchev - 18 Jul 2008 13:40 GMT
You can use CASE with BETWEEN and IN:

CASE WHEN CONVERT(int, Floyd.dbo.ProductGroup.Code)
                BETWEEN 1000 AND 1110
      THEN ...
      WHEN CONVERT(int, Floyd.dbo.ProductGroup.Code)
               BETWEEN 1150 AND 1160
        AND CONVERT(int, Floyd.dbo.ProductGroup.Code)
               BETWEEN 1165 AND 1201
      THEN ...
      WHEN CONVERT(int, Floyd.dbo.ProductGroup.Code)
               BETWEEN 1350 AND 1380
        AND CONVERT(int, Floyd.dbo.ProductGroup.Code)
               BETWEEN 1700 AND 1800
        AND  CONVERT(int, Floyd.dbo.ProductGroup.Code) IN (1161, 1162,
1915, 1990)
      THEN ...

HTH,

Plamen Ratchev
http://www.SQLStudio.com
Plamen Ratchev - 18 Jul 2008 13:53 GMT
The conditions in the different CASE WHEN clauses should have been with OR,
not AND of course...

CASE WHEN CONVERT(int, Floyd.dbo.ProductGroup.Code)
                BETWEEN 1000 AND 1110
      THEN ...
      WHEN CONVERT(int, Floyd.dbo.ProductGroup.Code)
               BETWEEN 1150 AND 1160
         OR CONVERT(int, Floyd.dbo.ProductGroup.Code)
               BETWEEN 1165 AND 1201
      THEN ...
      WHEN CONVERT(int, Floyd.dbo.ProductGroup.Code)
               BETWEEN 1350 AND 1380
         OR CONVERT(int, Floyd.dbo.ProductGroup.Code)
               BETWEEN 1700 AND 1800
         OR  CONVERT(int, Floyd.dbo.ProductGroup.Code) IN (1161, 1162,
1915, 1990)
      THEN ...

HTH,

Plamen Ratchev
http://www.SQLStudio.com
Terry - 18 Jul 2008 14:40 GMT
Hi Plamen,

I guessed it would be something like your example, but it didn't look right
with all those CONVERT's. I figured there must have been another way with
perhaps just one convertion and then testing against the result of that. The
problem I have is that the ProductGroup is a string, luckily for me it's a
string representation of a number which makes it easier to handle after
conversion.

Regards

> The conditions in the different CASE WHEN clauses should have been with
> OR, not AND of course...
[quoted text clipped - 19 lines]
> Plamen Ratchev
> http://www.SQLStudio.com
Plamen@sqlstudio.com - 18 Jul 2008 14:58 GMT
You can use a view and do the CONVERT/CAST in the view. Alternatively
you can use derived table or CTE on SQL Server 2005:

SELECT CASE WHEN code
                 BETWEEN 1000 AND 1110
                 THEN ...
FROM (SELECT CAST(Floyd.dbo.ProductGroup.Code AS INT) AS code
                    ...
         FROM Floyd.dbo.ProductGroup) AS T;

HTH,

Plamen Ratchev
http://www.SQLStudio.com
Tom Cooper - 18 Jul 2008 14:59 GMT
If you don't want to type all of those CONVERT's, you can have a shorter
query by using a derived table, e.g., something like

SELECT x.mtapg_ItemID,
  x.mtapg_ItemCode,
  x.mtapg_ProductGroup,
 CASE WHEN x.mtapg_ProductGroup                 BETWEEN 1000 AND 1110
      THEN ...
      WHEN x.mtapg_ProductGroup                BETWEEN 1150 AND 1160
         OR x.mtapg_ProductGroup                BETWEEN 1165 AND 1201
      THEN ...
      WHEN x.mtapg_ProductGroup                BETWEEN 1350 AND 1380
         OR x.mtapg_ProductGroup                BETWEEN 1700 AND 1800
         OR  x.mtapg_ProductGroup IN (1161, 1162, 1915, 1990)
      THEN ...
      END AS   mtapg_EquipmentType

FROM (SELECT     Floyd.dbo.StockItem.ItemID AS mtapg_ItemID,
 Floyd.dbo.StockItem.Code AS mtapg_ItemCode,
 CONVERT(int, Floyd.dbo.ProductGroup.Code) AS mtapg_ProductGroup
FROM         Floyd.dbo.StockItem INNER JOIN
                     Floyd.dbo.ProductGroup ON
Floyd.dbo.StockItem.ProductGroupID = Floyd.dbo.ProductGroup.ProductGroupID )
As x

Note that which form you use is mostly a matter of programming style,
readiability, etc.  There is probably no significant difference in
performance between the two methods.  But derived tables (and CTE's if you
are using SQL 2005) can save you typing (and maintaining!) the same piece of
code multiple times in the same query.  Another choice would be to have a
view that includes the converted integer.  Of course, the best choice would
have been to choose the correct datatype when the database was designed.  If
ProductGroup.Code is always an integer, it makes life much, much simpler if
the datatype in the table is int.

Tom

> Hi Plamen,
>
[quoted text clipped - 30 lines]
>> Plamen Ratchev
>> http://www.SQLStudio.com
Terry - 19 Jul 2008 22:32 GMT
Thanks Tom,

I'll have another look. No choice about the table design though.

Regards

> If you don't want to type all of those CONVERT's, you can have a shorter
> query by using a derived table, e.g., something like
[quoted text clipped - 67 lines]
>>> Plamen Ratchev
>>> http://www.SQLStudio.com
Steve Kass - 20 Jul 2008 00:33 GMT
Terry,

Just for the record, but not as a recommendation, you can
do this without the CONVERT expressions:

CASE
 WHEN Floyd.dbo.ProductGroup.Code BETWEEN 1000 AND 1110 THEN ...
 WHEN Floyd.dbo.ProductGroup.Code BETWEEN 1111 AND 1240 THEN ...
END

When a string and a number are compared, the string is implicitly
converted to a number, and the comparison is between numbers.

That said, I don't think relying on implicit type conversions
is a very good idea. But I'm not sure it makes things much
worse, given the faulty design of the table to begin with.

From what you've said, there may not be any safeguards in
place to guarantee that [Code] does in fact represent an [int],
so I would recommend that you trap any bad strings, giving a
result of NULL for them, or giving a non-NULL value that
makes sense in the context:

CASE
 WHEN Floyd.dbo.ProductGroup.Code LIKE '%[^0-9]%' THEN NULL
 WHEN ...

Checking for characters outside the range 0-9 isn't perfect, but it's close.
A few edge cases: the empty string '' would still pass the first
CASE predicate and be treated as 0; strings like '200.00' and '+20'
would be rejected (but it would be very hard to accept only such
strings that represent integer values); and numbers too large to be
represented as whatever data type SQL Server chooses for the
numbers in the CASE predicates would cause overflow.

My recommendation is to follow the suggests of Plamen and
others, and use a derived table (or a WITH expression
if you are using SQL Server 2005 or later), or
put all the CONVERTs in (though I'd use the ANSI standard
CAST instead of CONVERT, personally).

You could also vote on this Microsoft Connect item, which
suggests a new function that would be useful in some situations
like yours:

http://connect.microsoft.com/SQLServer/feedback/ViewFeedback.aspx?FeedbackID=354766

Steve Kass
Drew University
http://www.stevekass.com

>Hi Plamen,
>
[quoted text clipped - 35 lines]
>
>  
Terry - 21 Jul 2008 10:10 GMT
Thanks Steve,

Very interesting in as much as the particular values i'm testing, for other
companies, may contain characters, hence the table design. However your
point is taken re CAST rather than CONVERT. Being new to this I had a 50/50
choice. I will have a look at derived tables and CTE's to simplify things
further, and to learn more.
Regards
Terry

> Terry,
>
[quoted text clipped - 82 lines]
>>>Plamen Ratchev
>>>http://www.SQLStudio.com
Terry - 21 Jul 2008 10:22 GMT
Hi Steve,

Checked out the link you gave regarding new convert/cast functions and based
on the fact that the particular tables I will have to work with, littered
with numbers as text, a new function would be of some interest.
Regards

Terry

> Terry,
>
[quoted text clipped - 82 lines]
>>>Plamen Ratchev
>>>http://www.SQLStudio.com
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2009 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.