【问题标题】:Execute stored procedure with array-like string separated by comma [duplicate]使用逗号分隔的类似数组的字符串执行存储过程[重复]
【发布时间】:2012-12-24 19:21:58
【问题描述】:

可能重复:
Help with a sql search query using a comma delimitted parameter

我想编写一个对表执行选择的存储过程,并且需要一个varchar(max) 类型的输入变量。

我想发送一堆由,分隔的值作为输入参数,例如

'Jack','Jane','Joe'

然后获取包含这些名称之一的行。

在 SQL 中代码是

Select * from Personnel where Name in ('Jack','Joe','Jane');  

现在我想在我的 C# 应用程序中有一个变量,比如 strNames 并像这样填充它

string strNames = "'Jack','Joe','Jane'";

并将这个变量发送到 SP 并执行它。像

Select * from Personnel where Name in (''Jack','Joe','Jane'') -- this is wrong

但是我怎样才能告诉 SQL Server 运行这样的命令呢?

我需要做到这一点,我知道这是可能的,请给我线索。

【问题讨论】:

  • 是否可以将此字符串 Select * from Table1 发送到存储过程,然后 sp 将其作为命令执行?我以某种方式希望 sql 执行一个字符串。
  • 是的,有可能,但我永远不会那样做。
  • 糟糕,因为它很容易受到攻击?还是其他原因?我很好奇
  • 漏洞是一回事。您还会失去 .NET 框架提供的所有便利,例如参数总是以正确的格式传递给查询的事实等(想想日期或浮点值的 i18 问题)。另外,如果您想将语句发送到存储过程,您也可以立即在您的应用程序中执行它 - 有什么区别?
  • 马赫迪。您可以使用 sp_executesql 命令执行此操作:msdn.microsoft.com/en-us/library/ms188001.aspx 它确实有它的位置。但是你需要非常小心地使用它。我主要关心的是确保你没有使用用户输入“构建”你的 sql 命令,因此是 sql-injection。

标签: c# .net sql tsql sql-server-2008-r2


【解决方案1】:

首先,将单个名称传递给存储过程时不需要引用它们。

using (SqlCommand cmd = new SqlCommand("MyStoredProc", conn))
{
    cmd.CommandType = CommandType.StoredProcedure;
    cmd.Parameters.AddWithValue("@longFilter", "Jack,Jill,Joe");

    using (SqlDataReader reader = cmd.ExecuteReader())
    {
        ...
    }
}

然后,在存储过程中,您可以使用简单的文本函数和临时表,如下所示,以逗号分隔字符串,并为字符串的每个部分在临时表中添加一个条目:

DECLARE @temp AS TABLE (Name NVARCHAR(255))

IF ISNULL(@longFilter, '') <> ''
BEGIN
    DECLARE @s NVARCHAR(max)
    WHILE LEN(@longFilter) > 0
    BEGIN
        IF CHARINDEX(',', @longFilter) > 0
        BEGIN
            SET @s = LTRIM(RTRIM(SUBSTRING(@longFilter, 1, CHARINDEX(',', @longFilter) - 1)))
            SET @longFilter = SUBSTRING(@longFilter, CHARINDEX(',', @longFilter) + 1, LEN(@longFilter))
        END ELSE
        BEGIN
            SET @s = LTRIM(RTRIM(@longFilter))
            SET @longFilter= ''
        END

        -- This was missing until 20140522
        INSERT INTO @temp (Name) VALUES (@s)
    END
END

稍后使用以下SELECT 获取姓名在@temp 中的所有人的列表,或者如果@temp 不包含任何行(未过滤的结果),则为所有人:

SELECT * FROM Personnel WHERE Name IN (SELECT Name FROM @temp) OR (SELECT COUNT(*) FROM @temp) = 0

【讨论】:

  • 这是如何工作的?看起来 longFilter 值从未存储在临时表中。是否缺少某些 SQL 代码?我错过了什么明显的东西吗?
  • 实际上,您是对的(有趣的是,以前没有人注意到它)- 缺少实际 INSERT 到临时表中。我在评论中添加了它。感谢您指出这一点!
【解决方案2】:

你可以使用Table Valued Parameters

基本上,您可以在过程中插入一个值列表作为参数,并将它们用作表格,类似于

Select * from Personnel 
  where Name in (select name from @NamesTable).

现在,细节

  • 要使用表值参数,参数的类型必须在sql server中预定义,使用

    create type NamesTable as table (Name varchar(50))
    
  • 然后您可以将定义的类型用作过程中的参数

    create procedure getPersonnelList
      @NamesTable NamesTable readonly
    as
    begin
      select * from personnel
        where Name in (select Name from @NamesTable)
    end
    

    您可以在this SQL Fiddle 中看到这一点

  • 在 C# 方面,您需要创建参数。如果您在集合中有名称并构建字符串,则可以使用它来生成参数,如果它们是逗号分隔的字符串,则快速string.Split 可以解决这个问题。由于我不知道您的具体情况,我假设您有一个名为namesList&lt;string&gt;。您需要将其转换为要发送到过程的表值参数,使用类似:

    DataTable tvparameter = new DataTable();
    tvparameter.Columns.Add("Name", typeof(string));
    foreach (string name in names)
    {
      tvparameter.Rows.Add(name);
    }
    

    您可以在SO Question. 中找到有关如何在 C# 代码中生成 TVP 的更多信息。

  • 现在您只需将该参数发送到过程,就是这样。这是一个完整的控制台程序,它执行过程并输出结果。

    List<string> names = new List<string> { "Joe", "Jane", "Jack" };
    
    using (SqlConnection cnn = new SqlConnection("..."))
    {
      cnn.Open();
      using (SqlCommand cmd = new SqlCommand("getPersonnelList", cnn))
      {
        cmd.CommandType = CommandType.StoredProcedure;
    
        DataTable tvparameter = new DataTable();
        tvparameter.Columns.Add("Name", typeof(string));
        foreach (string name in names)
        {
          tvparameter.Rows.Add(name);
        }
    
        cmd.Parameters.AddWithValue("@NamesTable", tvparameter);
    
        using (SqlDataReader dr = cmd.ExecuteReader())
        {
          while (dr.Read())
          {
            Console.WriteLine("{0} - {1}", dr["ID"], dr["Name"]);
          }
        }
      }
    }
    

【讨论】:

    【解决方案3】:

    我猜你需要 Sql Server 中的拆分函数来将逗号分隔的字符串分解为表。请参考这些链接。

    Split Function in Sql Server to break Comma-Separated Strings into Table

    SQL User Defined Function to Parse a Delimited String

    您可以使用从表中选择数据

    Select * from 
    Personnel where 
      Name in (select items from dbo.Split ('Jack,Joe,Jane',','))
    

    【讨论】:

    【解决方案4】:

    您可以简单地检查名称是否包含在字符串中。请注意末尾开头的逗号,以确保您匹配全名

    string strNames = ",Jack,Joe,Jane,";
    

    SQL变成

    select * from Personnel where PATINDEX('%,' + Name + ',%', @strNames) > 0
    

    http://www.sqlfiddle.com/#!3/8ee5a/1

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-01-16
      • 2018-12-08
      • 2011-04-29
      • 2011-12-24
      • 1970-01-01
      • 2016-12-24
      • 2023-04-09
      相关资源
      最近更新 更多