服务器上的 Where In 子句错误仅支持最多 2100 个参数

Posted

技术标签:

【中文标题】服务器上的 Where In 子句错误仅支持最多 2100 个参数【英文标题】:Where In Clause error on The server support a maximum of 2100 parameters only 【发布时间】:2016-12-07 08:29:11 【问题描述】:

我目前正在尝试从我的简单 C# 应用程序中捕获插入 SQL 查询,但是当我尝试使用添加到 userIDList 参数中的用户 ID 将数据插入数据库时​​,出现错误说

传入的请求参数过多。服务器支持一个 最多 2100 个参数。

userIDList 有时会包含 60 个以上的数组,然后会弹出错误。

我的 SQL CommandText 将包含

"SELECT * FROM TIME_ATTENDANCE_REPORT WHERE TRXDATETIME = @Date AND USERID IN (001,002,003,004,....)

所以我认为如果超过某个数字,就会弹出错误

这是我的示例代码:

List<string> userIDList = new List<string>();
using (SqlCommand sqlDBComm = new SqlCommand())
                    
                        openConnection();
                        SqlDataReader sqlDBReader;
                        sqlDBReader = null;
                        sqlDBComm.CommandText = "SELECT * FROM TIME_ATTENDANCE_REPORT WHERE TRXDATETIME = @Date AND USERID IN (" + string.Join(",", userIDList) + ") ORDER BY USERID ASC ";
                        sqlDBComm.Parameters.Add("@Date", SqlDbType.DateTime);
                        sqlDBComm.Parameters["@Date"].Value = GetDateFrom;
                        sqlDBComm.Connection = sqlDB;
                        sqlDBComm.CommandType = CommandType.Text;
                        try
                        
                            sqlDBReader = sqlDBComm.ExecuteReader();
                            t.Load(sqlDBReader);
                            sqlDBReader.Close();

                            if (t.Rows.Count > 0)
                            
                                status = "Update";
                            
                            else
                            
                                status = "Insert";
                            
                        
                        catch (Exception errMsg)
                        
                            MessageBox.Show("Error Code: " + errMsg.ToString());
                        
                        finally
                        
                            sqlDBReader.Close();
                            closeConnection();
                        
                    

还有其他解决方案可以解决这个问题吗? 谢谢

【问题讨论】:

The incoming request has too many parameters. The server supports a maximum of 2100 parameters的可能重复 这有点奇怪,因为你没有使用参数(你可能应该是,顺便说一句);你也没有使用 LINQ,所以你可能不需要那个标签。但是,问题:您使用的是什么版本的 sql-server?最简单的“修复”可能是string_split,但这只是 2016 年;否则,自定义拆分 UDF 或表值参数可能是很好的替代品。但是:您确定示例代码是出错的代码吗?因为不会创建很多参数(只有 2 个) @MarcGravell 我使用的是 SQL Server 2014,自定义拆分 UDF 是什么意思? @MarcusZac 编写与string_split 执行相同操作的UDF(通过CREATE FUNCTION)只需少量工作(但速度较慢); @Cetin 提供了一个例子 【参考方案1】:

有很多方法可以解决这个问题。

您可以将单个 @IDList 参数作为单个逗号分隔的字符串发送,而不是将 ID 列表作为单独的参数发送,并让它在服务器端解析为 ID。这是我为此使用的一个函数(从 Jeff Moden 的代码中借用和修改):

CREATE FUNCTION [dbo].[iSplitter] (@Parameter VARCHAR(MAX))
RETURNS @splitResult TABLE (number INT, [value] INT)
AS
BEGIN
SET @Parameter = ','+@Parameter +',';

WITH cteTally AS
    (
        SELECT TOP (LEN(@Parameter))
            ROW_NUMBER() OVER (ORDER BY t1.Object_ID) AS N
            FROM Master.sys.All_Columns t1
            CROSS JOIN Master.sys.All_Columns t2
    )
INSERT @splitResult
    SELECT ROW_NUMBER() OVER (ORDER BY N) AS Number,
    SUBSTRING(@Parameter,N+1,CHARINDEX(',',@Parameter,N+1)-N-1) AS [Value]
    FROM cteTally
        WHERE N < LEN(@Parameter) AND SUBSTRING(@Parameter,N,1) = ','
RETURN
END

这个函数创建一次后,我会这样做:

sqlDBComm.CommandText = @"SELECT * FROM TIME_ATTENDANCE_REPORT tar
    inner Join dbo.iSplitter(@UserIdList) ul on tar.USERID = ul.[value]
    WHERE TRXDATETIME = @Date
    ORDER BY USERID ASC ";
sqlDBComm.Parameters.AddWithValue("@UserIdList",string.Join(",", userIDList));

这对于 5-6K 整数 ID 非常有效,但如果用于 20-30K 或更多 ID,则会超时。然后,我创建了另一个替代方案作为 CLR 过程,它可以在不到一秒的时间内解析列表服务器端。但我认为这个足以满足您的需求。

另一种方法是将 ID 作为 XML 参数发送并再次解析服务器端。

还有一种方法是发送一个表参数。

PS:这是link that shows sample code for other ways。该网站使用土耳其语,但 C# 中的代码非常清晰,每种​​方法都分开。

编辑:使用 Northwind Orders 表的 XML 示例:

void Main()

    int[] IDList =  10265,10266,10267,10268,10269,10270,10271,10272,10273,10274,10275, 10320, 10400 ;
    var idsAsXML = new XElement("IDS",
        from i in IDList
        select new XElement("Row", new XAttribute("Id", i)));

    string sql = @"
  DECLARE @hDoc int;
  DECLARE @tbl TABLE (Id int);
  exec sp_xml_preparedocument @hDoc OUTPUT, @XML;
  INSERT @tbl 
    SELECT * 
    FROM OPENXML(@hDoc, @Nodename, 1) WITH (Id int);
  EXEC sp_xml_removedocument @hDoc;

  select * from Orders o
  where exists (select * from @tbl t where t.Id = o.OrderId) ";

    DataTable tbl = new DataTable();
    using (SqlConnection con = new SqlConnection(@"server=.\SQLExpress;Trusted_Connection=yes;Database=Northwind"))
    
        SqlCommand cmd = new SqlCommand(sql, con);
        cmd.Parameters.AddWithValue("@XML", idsAsXML.ToString());
        cmd.Parameters.AddWithValue("@NodeName", "/IDS/Row");
        con.Open();
        tbl.Load(cmd.ExecuteReader());
        con.Close();
    

    //tbl.Dump(); // linqPad luxury

【讨论】:

这是一个存储过程吗? 不,它是 TVF(表值函数 - 类似于 SP,但不是 SP)。 但是我用的是C#,在哪里可以找到TVF? 我的意思是,您在数据库上创建一次(使用 SSMS 或通过 C# 代码,只要您有权限)。无论如何,我看到它困扰您,然后我添加一个不需要您对数据库执行任何操作的 XML 示例。 执行代码时会不会一直创建一个xml文件?【参考方案2】:

您可以创建一个Table-Valued-Parameter 并将其作为参数传递。它要求您在数据库中创建一个新类型,并使您能够将数组传递给查询并让数据库将其视为表。如果这是你经常做的事情,它可能会派上用场。

我不再有权访问我实施此操作的项目,但所有内容都可以在 blog post 中找到。下面的代码未经测试,但我希望它可以让你朝着正确的方向前进。

1. 在您的数据库中创建一个新类型:

CREATE TYPE integer_list_tbltype AS TABLE (n int NOT NULL PRIMARY KEY)

2.作为参数传递:

sqlDBComm.Parameters.Add("@userIds", SqlDbType.Structured)
sqlDBComm.Parameters["@userIds"].Direction = ParameterDirection.Input
sqlDBComm.Parameters["@userIds"].TypeName = "integer_list_tbltype"
sqlDBComm.Parameters["@userIds"].Value = CreateDataTable(userIDList)

3.创建参数的方法:

private static DataTable CreateDataTable(IEnumerable<int> ids) 
    DataTable table = new DataTable();
    table.Columns.Add("n", typeof(int));
    foreach (int id in ids) 
        table.Rows.Add(id);
    
    return table;

4. 在您的 SQL 中使用它:

... AND USERID IN (SELECT n FROM @userIds)

CreateDataTable 从这里:

How to pass table value parameters to stored procedure from .net code

从这里休息:

http://www.sommarskog.se/arrays-in-sql-2008.html#introduction

【讨论】:

以上是关于服务器上的 Where In 子句错误仅支持最多 2100 个参数的主要内容,如果未能解决你的问题,请参考以下文章

使用 Spark 执行“WHERE IN”子句,如何仅重新训练第一个数据集的列?

Node.JS 仅返回 MySQL“WHERE IN”子句的部分记录

IN vs = in Where 子句

python bigquery 库 DB-API 接口如何支持 WHERE IN 或 WHERE ANY 子句

Cassandra Where子句中的IN查询

错误:当前子查询表达式仅允许作为配置单元中的 where 子句谓词