简体   繁体   English

根据参数更改 SQL where 子句?

[英]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, and ProductTag contains the joins between them and Product .我假设Tags实际上包含所有可能标签的列表,并且ProductTag包含它们与Product之间的连接。 If there is no such table, and Tags is just a list of tags per Product , then you can replace Tags with (SELECT DISTINCT TagId FROM Tags) and ProductTag with Tags .如果没有这样的表,并且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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM