服务器上的 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”子句的部分记录