2009-01-29 15 views
28

Tengo una combinación externa izquierda (a continuación) que devuelve los resultados como se esperaba. Necesito limitar los resultados de la tabla "derecha" al "primer" golpe. ¿Puedo hacer eso de alguna manera? Actualmente, obtengo un resultado para cada registro en ambas tablas, solo quiero ver un resultado de la tabla de la izquierda (ítems) sin importar cuántos resultados tenga en la tabla correcta (fotos).Cómo limitar una combinación externa izquierda LINQ a una fila

 var query = from i in db.items 
       join p in db.photos 
       on i.id equals p.item_id into tempPhoto 
       from tp in tempPhoto.DefaultIfEmpty() 
       orderby i.date descending 
       select new 
       { 
        itemName = i.name, 
        itemID = i.id, 
        id = i.id, 
        photoID = tp.PhotoID.ToString() 
       }; 


    GridView1.DataSource = query; 
    GridView1.DataBind(); 

Respuesta

51

Esto hará el trabajo por usted.

from i in db.items 
let p = db.photos.Where(p2 => i.id == p2.item_id).FirstOrDefault() 
orderby i.date descending 
select new 
{ 
    itemName = i.name, 
    itemID = i.id, 
    id = i.id, 
    photoID = p == null ? null : p.PhotoID.ToString(); 
} 

Tengo este sql cuando me genera contra mi propio modelo (y sin el nombre y columnas segunda id en la proyección).

SELECT [t0].[Id] AS [Id], CONVERT(NVarChar,(
    SELECT [t2].[PhotoId] 
    FROM (
     SELECT TOP (1) [t1].[PhotoId] 
     FROM [dbo].[Photos] AS [t1] 
     WHERE [t1].[Item_Id] = ([t0].[Id]) 
     ) AS [t2] 
    )) AS [PhotoId] 
FROM [dbo].[Items] AS [t0] 
ORDER BY [t0].[Id] DESC 

Cuando pregunté por el plan, se demostró que la sub consulta se lleva a cabo por esta combinación:

<RelOp LogicalOp="Left Outer Join" PhysicalOp="Nested Loops"> 
+0

Me gusta la elegancia de esta solución, sin embargo, creo que esto puede crear una consulta que es más difícil de optimizar para SQL debido a la sub selección –

+0

I comprobado y contento tanto con el SQL generado como con el plan de ejecución estimado. La subselección se planificó como una combinación externa izquierda. –

+1

refresque cualquier posibilidad de que pueda publicar el SQL, tengo curiosidad por verlo. –

3

Lo que se quiere hacer es grupo de la tabla. La mejor manera de hacer esto es:

var query = from i in db.items 
       join p in (from p in db.photos 
          group p by p.item_id into gp 
          where gp.Count() > 0 
          select new { item_id = g.Key, Photo = g.First() }) 
      on i.id equals p.item_id into tempPhoto 
      from tp in tempPhoto.DefaultIfEmpty() 
      orderby i.date descending 
      select new 
      { 
       itemName = i.name, 
       itemID = i.id, 
       id = i.id, 
       photoID = tp.Photo.PhotoID.ToString() 
      }; 

Editar: Se trata de David B hablando. Solo estoy haciendo esto porque Nick me lo pidió. Nick, modifique o elimine esta sección como lo considere apropiado.

El SQL generado es bastante grande. El int 0 (para ser comparado con el recuento) se pasa a través del parámetro.

SELECT [t0].X AS [id], CONVERT(NVarChar(MAX),(
    SELECT [t6].Y 
    FROM (
     SELECT TOP (1) [t5].Y 
     FROM [dbo].[Photos] AS [t5] 
     WHERE (([t4].Y IS NULL) AND ([t5].Y IS NULL)) OR (([t4].Y IS NOT NULL) AND ([t5].Y IS NOT NULL) AND ([t4].Y = [t5].Y)) 
     ) AS [t6] 
    )) AS [PhotoId] 
FROM [dbo].[Items] AS [t0] 
CROSS APPLY ((
     SELECT NULL AS [EMPTY] 
     ) AS [t1] 
    OUTER APPLY (
     SELECT [t3].Y 
     FROM (
      SELECT COUNT(*) AS [value], [t2].Y 
      FROM [dbo].[Photos] AS [t2] 
      GROUP BY [t2].Y 
      ) AS [t3] 
     WHERE (([t0].X) = [t3].Y) AND ([t3].[value] > @p0) 
     ) AS [t4]) 
ORDER BY [t0].Z DESC 

El plan de ejecución muestra tres combinaciones a la izquierda. Al menos uno es trivial y no debe contarse (trae el cero). Hay suficiente complejidad aquí que no puedo señalar claramente ningún problema de eficiencia. Podría funcionar genial.

+0

Esto puede no ha sido la respuesta para Linq a SQL, pero resolvió el problema para Entity Framework, con sql resultante similar. Buscando una mejor solución ahora. –

2

Se podría hacer algo como:

var q = from c in 
      (from s in args 
      select s).First() 
     select c; 

Alrededor de la última parte de la consulta. No estoy seguro si funcionará o qué tipo de SQL wack producirá :)

Cuestiones relacionadas