Reputation: 14559
I have a table of the following form:
| PersonId | Eats |
|----------|-------------------|
| 1 | Meat |
| 1 | Animal Byproducts |
| 1 | Vegetables |
| 2 | Animal Byproducts |
| 2 | Vegetables |
| 3 | Vegetables |
And I want to turn it into a table like:
| PersonId | DietaryPreference |
|----------|--------------------|
| 1 | Omnivore |
| 2 | Vegetarian |
| 3 | Vegan |
My initial thought is do to something like
WITH cte AS (
SELECT
PersonId,
STRING_AGG (Eats,',') WITHIN GROUP (ORDER BY Eats ASC) AS EatsConcat
FROM MyTable
GROUP BY PersonId
)
SELECT
PersonId,
CASE EatsConcat
WHEN 'Animal Byproducts,Meat,Vegetables' THEN 'Omnivore'
WHEN 'Animal Byproducts,Vegetables' THEN 'Vegetarian'
WHEN 'Vegetables' THEN 'Vegan'
END AS DietaryPreference
FROM cte
but this seems like a messy way of doing it since there's no real reason to use string aggregration when what I'm really doing is set comparison. Is there a better way of doing this?
Upvotes: 4
Views: 56
Reputation: 453453
You can also use just aggregation and no CASE
.
The below prefixes each of the dietary preferences with a digit, so that the order of the selection hierarchy is also the lexicographic order and it can be used with MAX
, and then finds the MAX
and strips the digit off in the final SELECT
SELECT m.PersonId,
SUBSTRING(MAX(V.DietaryPreference), 2, 8000) AS DietaryPreference
FROM MyTable m
JOIN (VALUES ('Meat','3Omnivore'),
('AnimalByproducts','2Vegetarian'),
('Vegetables', '1Vegan')) V(Eats, DietaryPreference)
ON V.Eats = M.Eats
GROUP BY m.PersonId
Upvotes: 1
Reputation: 1270181
You can use conditional aggregation to process the hierarchy of values:
select personid,
coalesce(max(case when eats in ('Meat') then 'Omnivore' end),
max(case when eats in ('Animal Byproducts') then 'Vegetarian' end),
max(case when etas in ('Vegetables') then 'Vegan' end)
) as dietarypreference
from mytable
group by personid
Upvotes: 1
Reputation: 95689
Assuming that simply someone that eats meat, regardless of what others they might not, is an Omnivore, and someone that eats Animal Byproducts (but not meat) is a Vegetarian, then you could do something like this:
WITH CTE AS(
SELECT V.PersonID,
COUNT(CASE V.Eats WHEN 'Meat' THEN 1 END) AS Meat,
COUNT(CASE V.Eats WHEN 'AnimalByproducts' THEN 1 END) AS AnimalByproducts,
COUNT(CASE V.Eats WHEN 'Vegetables' THEN 1 END) AS Vegetables
FROM (VALUES(1,'Meat'),
(1,'AnimalByproducts'),
(1,'Vegetables'),
(2,'AnimalByproducts'),
(2,'Vegetables'),
(3,'Vegetables'))V(PersonID,Eats)
GROUP BY V.PersonID)
SELECT C.PersonID,
CASE WHEN C.Meat > 0 THEN 'Omnivore'
WHEN AnimalByproducts > 0 THEN 'Vegetarian'
WHEN Vegetables > 0 THEN 'Vegan'
ELSE 'Very hungry'
END
FROM CTE C;
Upvotes: 1