Question

Using SQL Server 2012, I have a classification table that is something like this:

prodName   class1min   class1max   class2min   class2max   class3min   class3max
---------------------------------------------------------------------------------
prod A      1.5         1.8         1.8         2.1         2.1         2.5
prod B      3.6         3.9         3.9         4.5         4.5         5.6
prod C      2.7         3.2         3.2         3.6         3.6         4.2
etc...

Given a product name and a value, I need to find what classification (1, 2 or 3) my value belongs in.

ie. I have a product B item with a value of 4.1. I just need to be able to identify that it belongs in class 2.

So far I have experimented with creating unpivot tables based on what product is selected, like this:

Prod B:

class       value
------------------
class1min   3.6
class1max   3.9
class2min   3.9
class2max   4.5
class3min   4.5
class3max   5.6

Then by inserting my value and sorting, I'm at least able to visualize the classification.

class       value
------------------
class1min   3.6
class1max   3.9
class2min   3.9
myvalue     4.1
class2max   4.5
class3min   4.5
class3max   5.6

I'm still having problems isolating it by code though, and I think there might be a better way.

NOTE: in the case of a tie, I'd like the lower classification.

Was it helpful?

Solution

You can unpivot the data and then perform the comparison. Since you are using SQL Server 2012, then you can easily unpivot the min/max columns in sets using CROSS APPLY:

select prodname, class, [min], [max]
from yourtable
cross apply
(
  values
    ('class1', class1min, class1max),
    ('class2', class2min, class2max),
    ('class3', class3min, class3max)
) c(class, [min], [max])

See Demo. Once the data has been unpivoted, then you can compare your value to find the class. If you have more classifications, then you can easily add more values in the subquery:

DECLARE @Prod VARCHAR(32) = 'Prod B', 
  @val DECIMAL(10,2) = 4.1;

select prodname, class, [min], [max]
from yourtable
cross apply
(
  values
    ('class1', class1min, class1max),
    ('class2', class2min, class2max),
    ('class3', class3min, class3max)
) c(class, [min], [max])
where prodname = @Prod
  and @val > [min]
  and @val <= [max]

See SQL Fiddle with Demo

OTHER TIPS

Well, since you ask, a normalized approach might look like this [with a PK formed on (prodname,class)]...

prodName   class  min    max  
prod A      1     1.5    1.8   
prod B      1     3.6    3.9   
prod C      1     2.7    3.2   
prod A      2     1.8    2.1   
prod B      2     3.9    4.5   
prod C      2     3.2    3.6   
prod A      3     2.1    2.5
prod B      3     4.5    5.6
prod C      3     3.6    4.2

Since there are only three possible classes, you can accomplish this quite simply with a CASE expression:

DECLARE @Prod VARCHAR(32) = 'Prod B', @val DECIMAL(10,2) = 4.1;

SELECT [Class] = CASE WHEN @val <= class1max THEN 'Class1'
    WHEN @val > class3Min THEN 'Class3' ELSE 'Class2' END
FROM dbo.tableName
WHERE ProdName = @Prod;

Now that we know that the "problem" is that you actually have 9 classifications, not the 3 as stated in the question, this still expands just fine:

DECLARE @Prod VARCHAR(32) = 'Prod B', @val DECIMAL(10,2) = 4.1;

SELECT [Class] = CASE WHEN @val <= class1max THEN 'Class1'
    WHEN @val > class2Min AND @val <= class2Max THEN 'Class2'
    WHEN @val > class3Min AND @val <= class3Max THEN 'Class3'
    WHEN @val > class4Min AND @val <= class4Max THEN 'Class4'
    WHEN @val > class5Min AND @val <= class5Max THEN 'Class5'
    WHEN @val > class6Min AND @val <= class6Max THEN 'Class6'
    WHEN @val > class7Min AND @val <= class7Max THEN 'Class7'
    WHEN @val > class8Min AND @val <= class8Max THEN 'Class8'
    ELSE 'Class9' END
FROM dbo.tableName
WHERE ProdName = @Prod;

If the problem with this is that it's too much code, well, you could consider changing the design.

As commented already you can simply normalize your data into multiple tables. e.g.

Product
--------
id_Product
name
moreProductRelatedColumns

ProductClassification
--------------
id_Classification
classCode
fk_productId
min
max

Then you can select by:

Select classCode 
from Products join ProductClassification pc on id_Product = fk_productId
where value between pc.min and pc.max

In your current Table design you can use nested CASE WHEN Statements. However I would suggest the normalized Table Design as you've no problems to add or remove classes from Products without rewriting your queries.

DECLARE @target_product varchar(32) = 'prod B'
DECLARE @target_value decimal(9,2) = 4.1

SELECT LEFT([class],6)
FROM MyTable
UNPIVOT([value] FOR [class] IN (
  [class1min],[class1max],
  [class2min],[class2max],
  [class3min],[class3max]
)) p1
WHERE @target_product = [prodName]
GROUP BY prodName,LEFT([class],6)
HAVING @target_value >= MIN([value]) AND @target_value < MAX([value])
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top