2009-03-17 37 views
51
from f in CUSTOMERS 
where depts.Contains(f.DEPT_ID) 
select f.NAME 

depts es una lista (IEnumerable<int>) de identificadores de departamentoGolpear el 2100 límite de parámetros (SQL Server) cuando se utiliza Contiene()

Esta consulta funciona bien hasta que pase una lista grande (es decir, aproximadamente 3000 identificadores de dept) .. entonces me sale este error:

The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. Too many parameters were provided in this RPC request. The maximum is 2100.

he cambiado de consulta a:

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

usando IndexOf() corrigió el error pero ralentizó la consulta. ¿Hay alguna otra forma de resolver esto? muchas gracias.

+1

¿Qué tal [como tal] (http://stackoverflow.com/questions/567963/linq-expression-to-return-property-value/568771#568771) (que se lotes en piezas manejables).Las otras opciones (que no son LINQ) implican CSV y una UDF "dividida" y parámetros de tabla-valor (en SQL2008). –

+0

Marca, ¿puede explicar cuál es la mejor alternativa para 'contener' si tengo varios parámetros que cuentan desde 1 hasta 2000? Sé que esto crea un montón de planes en db, pero parece que el uso de 'like '% %'' tomará aún más tiempo de recursos db. ¿Qué debería usar? –

+0

El problema del límite de 2100 parámetros no existe en Entity Framework: http://stackoverflow.com/questions/8898564/entity-framework-hitting-2100-parameter-limit – nmit026

Respuesta

6

¿Por qué no escribir la consulta en sql y adjuntar su entidad?

Ha sido un tiempo desde que trabajé en LINQ, pero aquí va:

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

Por supuesto, necesitará para proteger contra la inyección, pero eso no debería ser demasiado difícil.

+0

gracias joel. déjame probarlo y te dejaré saber cómo va. –

+10

Advertencia: eso está bien con enteros, pero con cadenas: cuidado con la inyección de SQL. –

+1

¿Presumiblemente quieres una coma en algún lado, Joel? –

2

Querrá consultar LINQKit project, ya que allí, en alguna parte, hay una técnica para preparar estas declaraciones para resolver este problema. Creo que la idea es usar PredicateBuilder para dividir la colección local en fragmentos más pequeños, pero no he revisado la solución en detalle porque, en cambio, he estado buscando una forma más natural de manejar esto.

Desafortunadamente, parece que desde Microsoft's response to my suggestion se soluciona este comportamiento, ya que no hay planes establecidos para que esto se aplique a .NET Framework 4.0 o incluso a los siguientes service packs.

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

ACTUALIZACIÓN:

He abierto una cierta discusión acerca de si esto iba a ser fijada para LINQ to SQL o la ADO.NET Entity Framework en los foros de MSDN. Consulte estas publicaciones para obtener más información sobre estos temas y para ver la solución temporal que he creado utilizando XML y una UDF de SQL.

1

Tuve un problema similar y obtuve dos formas de solucionarlo.

  1. Intersect método
  2. se unen en las ID

Para obtener valores que no están en la lista, he utilizado Except método o combinación izquierda.

+2

¿Podría dar un ejemplo de cómo hacerlo? –

10

Mi solución (Guías -> Lista 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; 
+1

En lugar de usar Techo (y multiplicar en Omitir) e incrementar en 1, simplemente use Recuento para la condición e incremente en 2000. También haga que sea una constante para que sea configurable. – webXL

Cuestiones relacionadas