[英]Change SQL where clause based on a parameter?
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.这将发生在具有大型 WHERE 子句的大型查询中,因此在 IF ELSE 语句中放置两个略有不同的查询并不理想。
What should I do to get this working?我应该怎么做才能让它工作?
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我刚刚测试过 - 有效
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.接下来,最简单的方法可能是首先找出所有标签是否匹配,并将其存储在变量中。
I'm assuming that
Tags
actually contains a list of all possible tags, andProductTag
contains the joins between them andProduct
.我假设
Tags
实际上包含所有可能标签的列表,并且ProductTag
包含它们与Product
之间的连接。 If there is no such table, andTags
is just a list of tags perProduct
, then you can replaceTags
with(SELECT DISTINCT TagId FROM Tags)
andProductTag
withTags
.如果没有这样的表,并且
Tags
只是每个Product
的标签列表,那么您可以将Tags
替换为(SELECT DISTINCT TagId FROM Tags)
并将ProductTag
替换为Tags
。
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 ProductTag pt
JOIN @tags tList ON tList.TagId = pt.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
LEFT JOIN ProductTag pt ON pt.TagId = tList.TagId
HAVING COUNT(t.Id) = COUNT(tList.TagId) -- all exist
OR COUNT(pt.TagId ) > 0 -- at least one match
);
Here's what I came up with, don't know why I didn't come up with it sooner.这是我想出的,不知道为什么我没有早点想出。 Thanks to everyone who answered.
感谢所有回答的人。
DECLARE @getNulls int = (select top (1) item from dbo.SplitString(@tagIds,','))
--set the value to null when the list contains all values in the database.
If NOT EXISTS(select Id from Tags EXCEPT select item from dbo.SplitString(@tagIds,','))
begin
set @getNulls = null
end
set ansi_nulls off --must do this for it to work
Select p.Id
FROM Products p
left join Tags t on t.Id = p.TagId
WHERE p.TagId in (select item from dbo.SplitString(@tagIds,',')) or p.TagId = @getNulls
set ansi_nulls on
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.