2009-01-29 8 views
28

J'ai une jointure externe gauche (ci-dessous) renvoyant les résultats comme prévu. Je dois limiter les résultats de la "bonne" table au "premier" hit. Est-ce que je peux faire ça? Actuellement, je reçois un résultat pour chaque enregistrement dans les deux tableaux, je veux seulement voir un résultat de la table sur la gauche (items), peu importe le nombre de résultats que j'ai dans la bonne table (photos).Comment limiter une jointure externe gauche LINQ à une ligne

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

Répondre

51

Ceci fera le travail pour vous.

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

J'ai eu ce sql quand je l'ai généré contre mon propre modèle (et sans le nom et la deuxième colonne id dans la projection).

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 

Quand j'ai demandé le plan, il a montré que la sous-requête est mis en œuvre par cette jointure:

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

J'aime l'élégance de cette solution, mais je pense que cela peut créer une requête qui est plus difficile pour SQL à optimiser en raison du sous-select –

+0

I vérifié et était heureux à la fois avec le SQL généré et le plan d'exécution estimé. La sous-sélection devait être une jointure externe gauche. –

+1

cool toute chance que vous pouvez poster le SQL, je suis curieux de le voir. –

3

Qu'est-ce que vous voulez faire est un groupe de la table. La meilleure façon de le faire est:

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

Edit: Ceci est David B parlant. Je ne fais que ça parce que Nick me l'a demandé. Nick, s'il vous plaît modifier ou supprimer cette section que vous jugez approprié.

Le code SQL généré est assez volumineux. L'int 0 (à comparer au nombre) est passé via le paramètre.

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 

Le plan d'exécution révèle trois jointures à gauche. Au moins un est trivial et ne devrait pas être compté (il apporte le zéro). Il y a assez de complexité ici que je ne peux pas clairement indiquer n'importe quel problème pour l'efficacité. Cela pourrait bien fonctionner.

+0

Ceci peut pas de la solution pour Linq to SQL, mais cela a résolu le problème pour Entity Framework, avec sql résultante similaire. Vous cherchez une meilleure solution maintenant. –

2

Vous pouvez faire quelque chose comme:

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

Autour de la dernière partie de la requête. Je ne sais pas si ça va fonctionner ou quel type de SQL wack il va produire :)

Questions connexes