【问题标题】:How to perform a WHERE similar to "NOT IN" with a list of value coming from a HTTP FORM?如何使用来自 HTTP FORM 的值列表执行类似于“NOT IN”的 WHERE?
【发布时间】:2012-04-27 18:08:38
【问题描述】:

我有一个用 C# 开发的 Web 应用程序,其中一个页面显示 SQL 查询的输出;该查询是对存储过程的 linq 调用,我使用的是 SQL Server 2008。

其中一列显示与结果行关联的标签;在同一页面上显示复选框列表,每个复选框对应一个标签,如果用户打开或关闭一个或多个复选框,我想过滤查询。

我最简单的 SQL 解决方案对我们来说是“NOT IN”,如下所示:

select * from [mytable] where [tags] not in ('tag1','tag2, etc...)

鉴于我将 FORM POST 转换为带有逗号分隔值的字符串。 示例:

string tags = ParseFormAndConvertCheckBoxToCSV(Page.Request.Form);
string sqlcmd = "select * from [mytable] where [tags] not in (" + tags + ")";

但我不想动态构建 SQL,因为据我所知 that would be bad

我可以想象splitting the string with comma separated list of values in SQL nd store the values into a in-memory table的几种方式:

declare @tagscsv nvarchar(MAX)
declare @notags TABLE(Value nvarchar(50))

set @tagscsv = 'taxi,ivf'

Declare @x XML 
select @x = cast('<A>'+ replace(@tagscsv,',','</A><A>')+ '</A>' as xml)

insert into @notags
select t.value('.', 'nvarchar(50)') as v from @x.nodes('/A') as x(t)

select * from [mytable] where [tags] not in (select * from @notags)

...但这听起来也像个肮脏的把戏。

在我看来这应该是一个很常见的情况,我发现很多人过去都遇到过这个问题,但是在谷歌上搜索我找不到一个优雅的解决方案。

有人可以帮忙吗?

【问题讨论】:

  • tags ='A' OR tags='B' 等,是你要找的吗?
  • I don't want to dynamically build the SQL because that would be bad. 是什么意思??
  • 试试我的解决方案 [在这个问题上][1]。你是对的,这是一个非常普遍的问题。 [1]:stackoverflow.com/questions/10355507/…
  • 请提供其他上下文。您是否在使用字符串 SQL 查询、linq to sql 等?网页建立在什么平台上(ASP、APSX、MVC、PHP 等)?另外,是什么让拆分逗号分隔列表成为一种肮脏的把戏?
  • 我编辑了问题以提供更多上下文,是的,我正在使用 C#、linq to entity 和 SQL Server 2008 的存储过程;但我觉得我的问题是一般性的,也适用于其他编程框架和数据库。

标签: sql sql-server forms


【解决方案1】:

编辑:遗漏了存储过程部分。 使用存储过程,您没有很多选择。我通常会做你所做的,拆分逗号分隔的字符串并将值保存到临时表(或表变量)中。

CREATE PROCEDURE SplitAndStuff
    @List nvarchar(MAX) = NULL,
    @SplitOn nvarchar(5) = ','  --This can be replaced with a literal if it is always a comma.
AS
BEGIN
    DECLARE @Pos int;
    DECLARE @SplitOnLength int;
    DECLARE @Results TABLE (value nvarchar(MAX))
    SET @SplitOnLength = DATALENGTH(@SplitOn) / 2;

    IF (RIGHT(@List, @SplitOnLength) <> @SplitOn) SET @List = @List + @SplitOn; --Add trailling split string if there is not one already.

    SET @Pos = CHARINDEX(@SplitOn, @List, 1)                                    --Initalize for loop.  (The starting position returned is 1-based, not 0-based.)

    WHILE @Pos > 0
    BEGIN
        INSERT INTO @Results (value) SELECT CAST(SUBSTRING(@List,1,@Pos-1)AS int);

        SET @List = SUBSTRING(@List, @Pos+@SplitOnLength, DATALENGTH(@List) / 2);
        SET @Pos = CHARINDEX(@SplitOn, @List, 1);
    END
END
GO

如果您的存储过程没有做任何其他事情(除了查找之外),您可以在下面使用我原来的 LINQ 答案:

由于您使用的是 LINQ,因此您需要拆分 tagscsv 并使用生成的数组来执行“在哪里”查询:

string[] tags = tagscsv.Split(',');
var output = from q in db.Table where !tags.Contains(q) select q;

另见:

【讨论】:

  • 分割字符串我认为我使用xml找到的解决方案比循环更快,就纯粹的性能而言。
  • @Max Favilli 您必须对其进行测试才能确定,但​​从概念上讲,您必须遍历字符串一次以用标签替换逗号,然后再次解析 XML,然后您正在循环XML 中的每个元素。当然,您可以增强我的循环(前几天我很快做到了)以不更改 List 并仅跟踪子字符串的起点和终点,这将导致只有 1 次通过(如果您计算下一次的搜索,则为 2整个字符串的逗号)。还要记住,XML 有额外的开销来构建和维护树。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多