C# 使用.NET中的类型化数据集将SQL参数传递给IN()子句

C# 使用.NET中的类型化数据集将SQL参数传递给IN()子句,c#,sql,parameter-passing,strongly-typed-dataset,C#,Sql,Parameter Passing,Strongly Typed Dataset,首先道歉,因为在这个网站上有类似的问题,但没有人直接回答这个问题 我在VS2010中使用类型化数据集。我使用如下查询在数据集中创建TableAdapter: SELECT * from Table WHERE ID IN(@IDs) 现在,如果我调用:TableAdapter.Fill(MyDataTable,“1,2,3”)会出现一个错误,说明VS无法将1,2,3转换为int.Fair类型 因此,我决定在参数集合中将参数(即@IDs)类型更改为string。重试-仍然显示相同的错误消息 那么

首先道歉,因为在这个网站上有类似的问题,但没有人直接回答这个问题

我在VS2010中使用类型化数据集。我使用如下查询在数据集中创建TableAdapter:

SELECT * from Table WHERE ID IN(@IDs)
现在,如果我调用:
TableAdapter.Fill(MyDataTable,“1,2,3”)
会出现一个错误,说明VS无法将1,2,3转换为int.Fair类型

因此,我决定在参数集合中将参数(即@IDs)类型更改为string。重试-仍然显示相同的错误消息

那么,这个类型化数据集有没有办法接受我的“1,2,3”参数?目前我只有几个参数要传递,所以我可以轻松地创建5个左右的参数并分别传递它们,但是如果有数百个呢?有没有办法用逗号分隔的参数调用
Fill()
方法


(我知道我可以使用动态SQL创建并执行该语句,但如果有其他方法允许我保留类型化数据集以供在ReportViewer/bindingsources中使用,我会更喜欢)

您不能以这种方式对值列表使用单个参数。但可能有特定于数据库的方法来实现您想要的。例如,使用SQL Server 2005或更高版本,您可以创建一个表值函数来拆分字符串参数,例如:

CREATE FUNCTION dbo.F_Split
(
@InputString VARCHAR(MAX)
,@Separator VARCHAR(MAX)
)
RETURNS @ValueTable TABLE (Value VARCHAR(MAX))
AS
BEGIN

    DECLARE @SeparatorIndex INT, @TotalLength INT, @StartIndex INT, @Value VARCHAR(MAX)
    SET @TotalLength=LEN(@InputString)
    SET @StartIndex = 1

    IF @Separator IS NULL RETURN

    WHILE @StartIndex <= @TotalLength
    BEGIN
        SET @SeparatorIndex = CHARINDEX(@Separator, @InputString, @StartIndex)
        IF @SeparatorIndex > 0
        BEGIN
            SET @Value = SUBSTRING(@InputString, @StartIndex, @SeparatorIndex-@StartIndex)
            SET @StartIndex = @SeparatorIndex + 1
        END
        ELSE
        BEGIN
            Set @Value = SUBSTRING(@InputString, @StartIndex, @TotalLength-@StartIndex+1)
            SET @StartIndex = @TotalLength+1
        END
        INSERT INTO @ValueTable
        (Value)
        VALUES
        (@Value)
    END

    RETURN
END
   int[] arr = new int[3]; 
    arr[0] = "1";        
    arr[1] = "2";             
    arr[2] = "3";             

foreach(vat data in arr)
{

//Do your Code here

// 
var MyDatatable = obj.GetDatabyID(data);

TableAdapter.Fill(MyDataTable);

}
@乔是对的

或者可以使用foreach循环来实现这一点

比如:

CREATE FUNCTION dbo.F_Split
(
@InputString VARCHAR(MAX)
,@Separator VARCHAR(MAX)
)
RETURNS @ValueTable TABLE (Value VARCHAR(MAX))
AS
BEGIN

    DECLARE @SeparatorIndex INT, @TotalLength INT, @StartIndex INT, @Value VARCHAR(MAX)
    SET @TotalLength=LEN(@InputString)
    SET @StartIndex = 1

    IF @Separator IS NULL RETURN

    WHILE @StartIndex <= @TotalLength
    BEGIN
        SET @SeparatorIndex = CHARINDEX(@Separator, @InputString, @StartIndex)
        IF @SeparatorIndex > 0
        BEGIN
            SET @Value = SUBSTRING(@InputString, @StartIndex, @SeparatorIndex-@StartIndex)
            SET @StartIndex = @SeparatorIndex + 1
        END
        ELSE
        BEGIN
            Set @Value = SUBSTRING(@InputString, @StartIndex, @TotalLength-@StartIndex+1)
            SET @StartIndex = @TotalLength+1
        END
        INSERT INTO @ValueTable
        (Value)
        VALUES
        (@Value)
    END

    RETURN
END
   int[] arr = new int[3]; 
    arr[0] = "1";        
    arr[1] = "2";             
    arr[2] = "3";             

foreach(vat data in arr)
{

//Do your Code here

// 
var MyDatatable = obj.GetDatabyID(data);

TableAdapter.Fill(MyDataTable);

}

关于

我所知道的唯一一个可以在子句中使用.NET参数的数据库是PostgreSQL,因为PostgreSQL的数组概念可以与中的
一起使用,Npgsql允许使用数组(或
IEnumerable
)参数


对于其他数据库,您必须构造SQL,或者将字符串传递给数据库过程,该过程将其转换为0个或多个参数,然后对这些参数执行操作。

SQL Server 2008具有一个名为

所以你需要

  • 将查询定义为
    SELECT*from表,其中ID位于(SELECT*from(@IDs))
  • 返回visual Studio中的TableAdapter visual designer,并更新@IDS参数以将@IDS参数修改为DbType=Object和ProviderType=Structured
  • 在您使用的数据库中运行此SQL批处理:
    CREATE TYPE MyIntArray AS TABLE(Value INT);转到
    。 这将创建一个MyIntArray“表类型”,其中只有一列为INT类型
  • 现在棘手的事情是将“MyIntArray”类型传递给ADO.NET端的TableAdapter 不幸的是,表适配器设计器不支持
    SqlParameter.TypeName
    参数,因此我们需要自己修复它。目标是修改生成的TableAdapter类的
    CommandCollection
    属性。不幸的是,此属性受保护,因此您必须派生TableAdapter,或者例如使用反射来调整它。下面是一个派生类的示例:

        public class MyTableAdapter2 : MyTableAdapter
        {
            public MyTableAdapter2()
            {
                SqlCommand[] cmds = base.CommandCollection;
                // here, the IDS parameter is index 0 of command 1
                // you'll have to be more clever, but you get the idea
                cmds[1].Parameters[0].TypeName = "MyIntArray";
            }
        }
    
    下面是调用此方法的方法:

            MyTableAdapter t = new MyTableAdapter2();
    
            // create the TVP parameter, with one column. the name is irrelevant.
            DataTable tvp = new DataTable();
            tvp.Columns.Add();
    
            // add one row for each value
            DataRow row = tvp.NewRow();
            row[0] = 1;
            tvp.Rows.Add(row);
    
            row = tvp.NewRow();
            row[0] = 2;
            tvp.Rows.Add(row);
    
            row = tvp.NewRow();
            row[0] = 3;
            tvp.Rows.Add(row);
    
            t.Fill(new MyDataTable(), tvp);
    

    您还可以创建IDs参数列表 因此,您将使用@ID1、@ID2、@ID3等,而不是使用@IDs

    var sql = "SELECT * from Table WHERE ID IN (" + getKeys(values.Count) + ")";
    
    和getKeys(count)执行如下操作:

    var result = string.Empty;
                for (int i = 0; i < count; i++)
                {
                    result += ", @ID" + i;
                }
                return string.IsNullOrEmpty(result) ? string.Empty : result.Substring(1);
    
    var result=string.Empty;
    for(int i=0;i
    最后,添加以下参数:

    foreach (int i = 0; i < values.Count; i++)
                {
                    cmd.Parameters.Add(new SqlParameter("@ID" + i, SqlDbType.VarChar) { Value = values[i]});
                }
    
    foreach(inti=0;i
    我尝试了一种在SQL方式中使用字符串“contains”概念的变通方法:

    在您的情况下,更改SQL-

    原件:

    从ID所在的表中选择*(@IDs)

    成为:

    从表中选择*,其中CharIndex(','+Cast(ID为Varchar(10))+',',@IDs)>0

    使用.net代码-

    原件:

    TableAdapter.Fill(MyDataTable,“1,2,3”)

    成为:

    TableAdapter.Fill(MyDataTable,“,1,2,3,”)


    您还可以使用XML将参数列表传入存储过程:

    1) 在Visual Studio中:

    创建新的Tableadapter并创建类型化数据集以获取单个记录:

    SELECT * FROM myTable WHERE (ID = @ID)
    
    2) 在SQL Server管理器中:

    使用与类型化数据集相同的选择字段创建存储过程:

    CREATE PROCEDURE [dbo].[usrsp_GetIds]
        @paramList xml = NULL
    AS
        SET NOCOUNT ON;
    
    /*
    Create a temp table to hold paramaters list.
    Parse XML string and insert each value into table.
    Param list contains: List of ID's
    */
    DECLARE @tblParams AS TABLE (ID INT)
    INSERT INTO @tblParams(ID) 
        SELECT 
            XmlValues.ID.value('.', 'INT')
        FROM 
            @paramList.nodes('/params/value') AS XmlValues(ID)
    
    /* 
    Select records that match ID's in param list:
    */
    SELECT * FROM myTable 
    WHERE 
        ID IN (
            SELECT ID FROM @tblParams
        )
    
    3) 在Visual Studio中:

    将新查询添加到Tableadapter,选择上面创建的存储过程usrsp_GetIds,并将其命名为FillBy_Ids。这将创建以下命令:

    TableAdapter.FillBy_Ids(@paramList)
    
    4) 在Visual Studio中:

    在.net代码中,创建一个实用程序函数,将字符串数组转换为XML:

        ''' <summary>
        ''' Converts an array of strings to XML values.
        ''' </summary>
        ''' <remarks>Used to pass parameter values to the data store.</remarks>
        Public Shared Function ConvertToXML(xmlRootName As String, values() As String) As String
            'Note: XML values must be HTML encoded.
            Dim sb As New StringBuilder
            sb.AppendFormat("<{0}>", HttpUtility.HtmlEncode(xmlRootName))
            For Each value As String In values
                sb.AppendLine()
                sb.Append(vbTab)
                sb.AppendFormat("<value>{0}</value>", HttpUtility.HtmlEncode(value))
            Next
            sb.AppendLine()
            sb.AppendFormat("</{0}>", xmlRootName)
            Return sb.ToString
        End Function
    

    我可以通过将
    ClearBeforeFill
    属性设置为
    false
    并在
    foreach
    循环中填充
    TableAdapter
    来解决这个问题

    List<string> aList = new List<string>();
    aList.Add("1");
    aList.Add("2");
    aList.Add("3");
    
    yourTableAdapter.ClearBeforeFill = true;
    yourTableAdapter.Fill(yourDataSet.yourTableName, ""); //clears table
    
    foreach (string a in aList)
    {
        yourTableAdapter.ClearBeforeFill = false;
        yourTableAdapter.Fill(yourDataSet.yourTableName, a);
    }
    yourTableAdapter.Dispose();
    
    List aList=newlist();
    添加(“1”);
    (a)添加(“2”);
    (a)添加(“3”);
    yourTableAdapter.ClearBeforeFill=true;
    yourTableAdapter.Fill(yourDataSet.yourTableName,“”)//清除表格
    foreach(列表中的字符串a)
    {
    yourTableAdapter.ClearBeforeFill=false;
    yourTableAdapter.Fill(yourDataSet.yourTableName,a);
    }
    yourTableAdapter.Dispose();
    
    是SQL Server吗?如果是,哪个版本?SQL server 2008(及更高版本)允许表值参数。但这需要SQL server上的类型定义。如果允许您在服务器上使用“创建类型”,我可以回答这个问题。是的,我有完全访问权限,请详细说明。对于阅读本文的任何人,请查看Joe和Simon M的答案。这两个答案都有助于解决我的问题。这可能会有帮助:效果很好。我只想补充一件事来澄清@在查询中,ID必须类似于“1,2,3”。当我尝试它时,我感到困惑。但是如何在类型化数据集中声明函数呢?这是唯一一个简单的1TVP行示例