Reputation: 47
I need to alter a query to do something like this (following is generic pseudo-code):
if (tag list contains all tags in the database) {
select every product regardless of tag, even products with null tag
}
else { //tag list is only a few tags long
select only the products that have a tag in the tag list
}
I have tried doing stuff like this, but it doesn't work:
SELECT p.Id
FROM Tags t
JOIN Products p ON p.TagId = t.Id
WHERE ((EXISTS(select Id from Tags EXCEPT select item from dbo.SplitString(@tagList,',')) AND p.TagId in (select item from dbo.SplitString(@tagList,',')))
OR (p.TagId in (select item from dbo.SplitString(@tagList,',')) or p.TagId is null))
This will take place inside of a large query with a large WHERE clause, so putting two slightly different queries in an IF ELSE statement is not ideal.
What should I do to get this working?
Upvotes: 0
Views: 484
Reputation: 71579
First things first: you should use properly normalized input parameters. Ideally this would be a Table-Valued parameter, however if you cannot do that then you could insert the split values into a table variable
DECLARE @tags TABLE (TagId int PRIMARY KEY);
INSERT @tags (TagId)
SELECT item
FROM dbo.SplitString(@tagList, ',');
Next, the easiest way is probably to just find out first whether all tags match, and store that in a variable.
DECLARE @isAllTags bit = CASE WHEN EXISTS(
SELECT t.Id
FROM Tags t
EXCEPT
SELECT tList.Id
FROM @tags tList
) THEN 0 ELSE 1 END;
SELECT p.Id
FROM Products p
WHERE @isAllTags = 1
OR EXISTS (SELECT 1
FROM @tags tList
WHERE tList.TagId = p.TagId);
You could merge these queries, but it's unlikely to be more performant.
You could even do it in a very set-based fashion, but it's probably going to be really slow
SELECT p.Id
FROM Products p
WHERE EXISTS (SELECT 1
FROM Tags t
LEFT JOIN @tags tList ON tList.TagId = t.Id
CROSS APPLY (VALUES (CASE WHEN p.TagId = tList.TagId THEN 1 END )) v(ProductMatch)
HAVING COUNT(t.Id) = COUNT(tList.TagId) -- all exist
OR COUNT(v.ProductMatch) > 0 -- at least one match
);
Upvotes: 1
Reputation: 19340
Try this, this might work.
SELECT p.Id
FROM
Products p LEFT JOIN
Tags t ON p.TagId = t.Id
WHERE
t.Id is null
OR
(t.id is not null and
t.Id in (SELECT value FROM STRING_SPLIT(@tagList, ',')))
I just tested - works
Upvotes: 0