【问题标题】:Query a SQL field for whether it contains any one of multiple values查询一个 SQL 字段是否包含多个值中的任何一个
【发布时间】:2012-08-16 23:40:17
【问题描述】:

我想查询 SQL 列的行是否包含多个值中的任何一个。

例如,返回表的行,其中 A 列包含以下任何单词: ('cow','farmer','milk').

当您知道单词是什么时就很容易了,但是我想编写一个存储过程,我可以在其中输入任何字符串数组,并且如果某一行的列 A 包含其中任何一个,它将返回该行。

我希望能够喂食:

('cow','farmer','milk')

('cow','farmer','steak','yikes')

('cow','farmer','three', 'pigs', 'wolf')

它必须相对简单,但我终其一生都无法弄清楚。 我在 SQL Server 2008 上

【问题讨论】:

  • 你能显示示例数据吗?例如。该表是否在每一行中包含单个值,或者该表是否还可以包含类似cow,milk 的字符串?
  • 抱歉,数据是完整的句子,我想看看这些词是否存在。
  • 您可以尝试我的解决方案,只需将 JOIN 更改为 LIKE 而不是 =。目前无法通过手机正确编辑。

标签: sql sql-server sql-server-2008 stored-procedures


【解决方案1】:

一种简单的方法:

declare @candidates varchar = '|cow|farmer|three|pigs|wolf|'
select * from TableName where @candidates like '%|' + FieldName + '|%'

在 SQL 2008 中执行此操作的最佳方法是使用 table-valued parameter

【讨论】:

  • +1 - 对此问题的下意识反应是使用拆分功能,但在这种情况下,它只是没有必要。我想说LIKE 仍然比 TVP 更有效,但我不确定,因为我没有在这个用例中直接测试过这些,我还是要发布一个 TVP 答案,以便 OP 可以测试.
  • 我认为 OP 想要其中 FieldName 包含其中一个单词的行 - 这会检查 FieldName equals 其中一个单词。
  • @Blorgbeard 我也将“包含多个值中的任何一个”解释为“其值对应于任何值”,但您可能是对的。
  • 我一直在努力让它工作,发现这是因为 FieldName 对我来说是一个 nvarchar(64),而我只是将 @candidates 声明为 nvarchar。如果我明确声明 (at)candidates 是一个 nvarchar(64),然后将其设置为一个它可以工作的列表,无论列表的大小如何。
  • 对于FieldName 的形式为potato|monkey|pigs|carrot|sandwich 并且您想要匹配的情况是否有类似的解决方案,因为它包含来自@candidates 的值?
【解决方案2】:

SQL Server 不知道什么是“数组”。您可以编写一个拆分表值函数,将每个值转换为一行,然后与基表连接,但与 Av 的答案相比,这绝对是次优的。或者,您可以针对表值参数进行测试。 TVP 是definitely the best performer compared to all of the splitting techniques, even CLR

CREATE TYPE dbo.FarmItems AS TABLE(Item VARCHAR(32));
GO

CREATE PROCEDURE dbo.FindFarmItems
  @List dbo.FarmItems READONLY
AS
BEGIN
  SET NOCOUNT ON;

  SELECT t.col1, t.col2, ...
    FROM dbo.table AS t
    INNER JOIN @List AS L
    ON t.columnA = L.Item;
END
GO

然后在 C# 中,您只需使用“数组”构造一个 DataTable 并将其传入:

DataTable dt = new DataTable();
dt.Columns.Add("Item", typeof(string));
dt.Rows.Add("cow");
dt.Rows.Add("farmer");
...

using (SqlConnection conn = new ...)
{
    SqlCommand c = new SqlCommand("dbo.FindFarmItems", conn);
    2.CommandType = CommandType.StoredProcedure;
    SqlParameter tvp = c.Parameters.AddWithValue("@List", dt);
    tvp.SqlDbType = SqlDbType.Structured;
    // execute, get a reader, etc...
}

【讨论】:

    【解决方案3】:

    您可以使用 XML 数据类型将列表数据传递给您的存储过程:

    create procedure dbo.LookItUp
    
      @idList xml
    
    as
    
      declare @lookup table
      (
        id int not null
      )
    
      insert @lookup (id)
      select distinct t.id.value('.','int')
      from @idList.nodes('/ids/id') as t( id )
      where t.id is not null
    
      select *
      from dbo.my_data_table t
      join @lookup           lu on lu.id = t.object_id
    
      return 0
    go
    

    简单!

    【讨论】:

      【解决方案4】:

      我遇到了同样的困境,并想出了以下问题。例如,如果 table_a 包含 ('My Cow', 'My Goat', 'My Dog') 并且 table_b 包含 ('Dog', 'Cow'),那么...

      create table #table_a (field1 varchar(128))
      insert into #table_a values('My Cow')
      insert into #table_a values('My Goat')
      insert into #table_a values('My Dog')
      
      create table #table_b (field1 varchar(128))
      insert into #table_b values('Dog')
      insert into #table_b values('Cow')
      
      select * from #table_a table_a where (select count(*) from #table_b table_b where charindex(table_b.field1, table_a.field1) > 0) > 0
      

      返回('我的牛','我的狗')

      希望这会有所帮助。

      【讨论】:

        【解决方案5】:

        您不能将数组传递给存储过程,因为 TSQL 中不存在数组。你有几个选择,我给你两个。

        选项 1 - 又快又脏(我不推荐)
        将值作为字符串传递并将其添加到动态 SQL 语句中。

        CREATE PROCEDURE MyProc
          @Values varchar(1000)
        AS
        
        DECLARE @SQL VARCHAR(2000)
        
        SET @SQL = 'SELECT blahblah WHERE SomeField IN (' + @Values + ')'
        
        -- Execute the statement and return the result
        
        END
        

        除了明显的 SQL 注入漏洞外,这种方法不适用于大型集合,而且性能也不会太好。此外,如果任何值包含逗号,它将不起作用。我真的不推荐它,尽管它可能对快速测试有用。

        选项 2 - 更灵活的解决方案
        将所有值存储在您将在存储过程中引用的临时表中。

        CREATE TABLE #MyTempTable
        -- Fields...
        
        INSERT INTO #MyTempTable
        -- Insert the values
        
        CREATE PROCEDURE MyProc
          @Values varchar(1000)
        AS
        
        SELECT 
          SomeFields
        FROM
          MyTable
          JOIN
          #MyTempTable ON
            -- Add join clause
        
        END
        

        此解决方案可能会更好地扩展。

        正如其他人建议的那样,您还可以使用内存表(我个人避免使用,因为我从来没有使用过它们,它们的性能总是比临时表差)或表参数,但您必须声明预先确定它的类型。

        【讨论】:

        • 使用新信息,第一个解决方案不起作用,因为您无法组合 LIKE an IN。对于第二个,代码的关键部分完全缺失 - 如何将值数组(可能来自应用程序)放入#temp 表中。
        • OP 在原帖中没有写“LIKE”,因此我没有使用。显然,既然他明确了自己的需求,我的解决方案就不再适用了。
        【解决方案6】:

        您可以简单地使用 WHERE 列 IN(逗号分隔的字符串)

        这是我的完整示例:

        create table #bar (foo varchar(20))
        
        insert into #bar (foo) values('cow')
        insert into #bar (foo) values('cat')
        
        select foo from #bar
        where foo in ('cow','farmer','milk')
        
        drop table #bar
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-04-27
          • 2015-12-21
          • 1970-01-01
          • 1970-01-01
          • 2021-02-16
          • 2021-12-11
          • 2022-07-27
          相关资源
          最近更新 更多