Reputation: 112
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.
Upvotes: 0
Views: 409
Reputation: 247760
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]
Upvotes: 3
Reputation: 280413
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.
Upvotes: 1
Reputation: 33945
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
Upvotes: 1
Reputation: 10908
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])
Upvotes: 0
Reputation: 26
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.
Upvotes: 0