Reputation: 153
I'm trying to map from a string value to a list of ids to use in an IN clause and haven't found a way to make it work yet. Hopefully the query I've tried so far will make this clear:
SELECT COUNT(*) FROM events
WHERE level_id IN (
SELECT
CASE
WHEN skill_level='Beginner' THEN (SELECT id from levels WHERE code IN ('L1', 'L2'))
WHEN skill_level='Intermediate' THEN (SELECT id from levels WHERE code IN ('L3', 'L4'))
WHEN skill_level='Advanced' THEN (SELECT id from levels WHERE code IN ('L5', 'L6'))
END ids
FROM users WHERE id=2
)
Which could simplify for a 'Beginner' skill level user to this:
SELECT COUNT(*) FROM events WHERE level_id IN (1, 2)
I created a fiddle to test this here.
I get the error "more than one row returned by a subquery used as an expression" when I try this. I've tried a number of permutations including the array() operator but I always get one error or another.
Maybe I'm fundamentally going about this the wrong way. If anyone can suggest a fix for my query, or another way to accomplish the same thing it would be greatly appreciated.
Upvotes: 1
Views: 148
Reputation: 3547
Error "more than one row returned by a subquery used as an expression" arises because of:
WHEN skill_level=... THEN (SELECT id from levels WHERE code IN (...))
inside CASE
statement.
UPDATE
Maybe the best would be to use suggestion of @gordon-linoff
SELECT COUNT(*)
FROM
events E
JOIN levels L ON E.level_id = L.id
JOIN users U ON
( U.skill_level='Beginner' AND L.code IN ('L1', 'L2'))
OR (U.skill_level='Intermediate' AND L.code IN ('L3', 'L4'))
OR (U.skill_level='Advanced' AND L.code IN ('L5', 'L6'))
WHERE
U.id = 2
https://www.db-fiddle.com/f/tP8MPuy2ks3Zfuo9jSAmJA/4
Previous answer
Fix would be to use CASE
-statement as predicate for JOIN
:
SELECT COUNT(*) FROM events
WHERE
level_id IN (
SELECT
L.id
FROM
users U
JOIN levels L ON
CASE
WHEN skill_level='Beginner' THEN code IN ('L1', 'L2')
WHEN skill_level='Intermediate' THEN code IN ('L3', 'L4')
WHEN skill_level='Advanced' THEN code IN ('L5', 'L6')
END
WHERE
U.id = 2
)
Look on: https://www.db-fiddle.com/f/tP8MPuy2ks3Zfuo9jSAmJA/1
Upvotes: 1
Reputation: 153
I found a way that works in Postgresql 11. Didn't work in 9.4, but as I am working in 11, that's fine. I don't know if it's a good way, but it does work. Alex Yu's answer might be better but I haven't done any performance testing. Here it is for what it's worth:
SELECT COUNT(*) FROM events
WHERE level_id = ANY(ARRAY(
SELECT
CASE
WHEN skill_level='Beginner' THEN ARRAY(SELECT id from levels WHERE code IN ('L1', 'L2'))
WHEN skill_level='Intermediate' THEN ARRAY(SELECT id from levels WHERE code IN ('L3', 'L4'))
WHEN skill_level='Advanced' THEN ARRAY(SELECT id from levels WHERE code IN ('L5', 'L6'))
END ids
FROM users WHERE id=2
));
count |
---|
3 |
Upvotes: 1
Reputation: 1269773
Don't use a case
like that. Use simpler boolean logic:
SELECT COUNT(*)
FROM events
WHERE (skill_level = 'Beginner' AND level_id IN ('L1', 'L2')) OR
(skill_level = 'Intermediate' AND level_id IN ('L3', 'L4')) OR
(skill_level = 'Advanced' AND level_id IN ('L5', 'L6'));
I'm not sure what this logic is for:
FROM users WHERE id = 2
Your question does not explain that.
Upvotes: 2
Reputation: 1827
It misses a key in levels
table. You should add a skill_level column to link skill_level users. Then it becomes easier to make your query.
See the example here
Upvotes: 0