web-dev-qa-db-fra.com

Frapper la limite de paramètre 2100 (SQL Server) lors de l'utilisation de Contains ()

from f in CUSTOMERS
where depts.Contains(f.DEPT_ID)
select f.NAME

depts est une liste (IEnumerable<int>) des identifiants de département

Cette requête fonctionne correctement jusqu'à ce que vous passiez une longue liste (disons environ 3000 ID de service) ..

Le flux de protocole d'appel de procédure distante TDS (Tabular Data Stream) entrant est incorrect. Trop de paramètres ont été fournis dans cette demande RPC. Le maximum est 2100.

J'ai changé ma requête en: 

var dept_ids = string.Join(" ", depts.ToStringArray());
from f in CUSTOMERS
where dept_ids.IndexOf(Convert.ToString(f.DEPT_id)) != -1
select f.NAME

utiliser IndexOf() corrige l'erreur mais ralentit la requête. Y a-t-il un autre moyen de résoudre ce problème? Merci beaucoup.

54
ban-G

Ma solution (Guides -> Liste de Guid):

List<tstTest> tsts = new List<tstTest>();
for(int i = 0; i < Math.Ceiling((double)Guides.Count / 2000); i++)
{
    tsts.AddRange(dc.tstTests.Where(x => Guides.Skip(i * 2000).Take(2000).Contains(x.tstGuid)));
}
this.DataContext = tsts;
11
ADM-IT

Pourquoi ne pas écrire la requête en SQL et attacher votre entité?

Cela fait longtemps que je n'ai pas travaillé à Linq, mais voici:

IQuery q = Session.CreateQuery(@"
         select * 
         from customerTable f
         where f.DEPT_id in (" + string.Join(",", depts.ToStringArray()) + ")");
q.AttachEntity(CUSTOMER);

Bien sûr, vous devrez vous protéger contre les injections, mais cela ne devrait pas être trop difficile.

6
Joel

Vous voudrez peut-être consulter le projet LINQKit , car il existe quelque part une technique permettant de regrouper de telles instructions pour résoudre ce problème. Je pense que l'idée est d'utiliser PredicateBuilder pour diviser la collection locale en fragments plus petits, mais je n'ai pas examiné la solution en détail, car je recherchais plutôt un moyen plus naturel de gérer cela.

Malheureusement, il ressort de la réponse de Microsoft à ma suggestion de corriger ce problème qu’aucun plan n’a été prévu pour que cela soit adressé à .NET Framework 4.0 ou même aux service packs ultérieurs.

https://connect.Microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=475984

METTRE À JOUR:

J'ai ouvert des discussions pour savoir si cela allait être résolu pour LINQ to SQL ou pour le ADO.NET Entity Framework sur les forums MSDN. Veuillez consulter ces messages pour plus d'informations sur ces sujets et pour voir la solution de contournement temporaire que j'ai proposée à l'aide de XML et d'un fichier UDF SQL.

2
jpierson

J'ai eu un problème similaire, et j'ai deux façons de le résoudre.

  1. Intersect méthode
  2. rejoindre sur les identifiants

Pour obtenir des valeurs qui ne sont PAS dans la liste, j'ai utilisé Sauf méthode OR left join.

1
Roman O

Vous pouvez toujours partitionner votre liste de dépôts en ensembles plus petits avant de les transmettre comme paramètres à l'instruction IN générée par Linq. Vois ici:

Diviser un grand IEnumerable en un plus petit IEnumerable d'une quantité fixe d'élément

0
Stephen Burns