2016-12-07 75 views
0

我目前正試圖嘗試從我的簡單的C#應用​​程序捕獲插入SQL查詢,但是當我試圖插入數據到數據庫與用戶ID將其添加到userIDList參數中,則出現錯誤提示哪裏在條款錯誤服務器上最多隻支持2100參數

傳入請求的參數太多。服務器最多支持210035個參數。

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(); 
         } 
        } 

任何其他解決辦法可以解決這個? 謝謝

+0

可能的複製。該服務器最多支持2100個參數](http://stackoverflow.com/questions/23045912/the-incoming-request-has-too-many-parameters-the-server-supports-a-maximum-of-2) –

+0

這有點奇怪,因爲你*沒有使用參數*(你可能應該是,btw);你也沒有使用LINQ,所以你可能不需要這個標籤。但是,問題:你使用的是什麼版本的sql-server?最簡單的「修復」可能是'string_split',但那只是2016年;否則,自定義拆分UDF或表值參數可能是很好的替代品。但是:你確定示例代碼是錯誤的代碼嗎?因爲*不會創建大量參數*(只有2) –

+0

@MarcGravell我正在使用SQL Server 2014,您是什麼意思自定義拆分UDF? –

回答

1

有很多方法可以解決這個問題。

除了將ID列表作爲單獨參數發送外,您還可以將單個@IDList參數作爲單個逗號分隔的字符串發送,並在服務器端將其解析爲ID。下面是我使用的這個(貸款以及從傑夫MODEN的代碼修改)功能:

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

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#中很清晰,每種​​方法都是分開的。

編輯:使用羅斯文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 
} 
+0

這是一個存儲過程嗎? –

+0

不,這是一個TVF(表值函數 - 類似於SP但不是SP)。 –

+0

但我使用C#,在哪裏可以找到TVF? –

1

您可以創建一個Table-Valued-Parameter它作爲參數傳遞。它要求您在數據庫中創建一個新類型,並使您能夠將數組傳遞給查詢,並讓數據庫將其視爲一個表。如果這是你做了很多事情,它可以派上用場。

我不再能夠訪問項目,我已經實施了這個項目,但blog post中的所有內容都可用。下面的代碼沒有經過測試,但我希望它能讓你朝正確的方向發展。

1。在你的數據庫創建一個新的類型:

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

2.把它作爲參數:

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; 
} 

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) 

創建參數方法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