2009-07-14 9 views
126

je l'instruction SQL suivante, que je suis en train de se traduire par LINQ:LINQ to SQL - Gauche jointure externe avec plusieurs conditions de jointure

SELECT f.value 
FROM period as p 
LEFT OUTER JOIN facts AS f ON p.id = f.periodid AND f.otherid = 17 
WHERE p.companyid = 100 

J'ai vu la mise en œuvre typique de la jointure externe gauche (ie. into x from y in x.DefaultIfEmpty() etc.), mais je suis pas sûr de savoir comment introduire l'autre condition de jointure (AND f.otherid = 17)

EDIT

Pourquoi la partie AND f.otherid = 17 condition de le JOIN au lieu de dans la clause WHERE? Parce que f peut ne pas exister pour certaines lignes et je veux toujours ces lignes à inclure. Si la condition est appliquée dans la clause WHERE, après le JOIN - alors je n'obtiens pas le comportement que je veux.

Malheureusement ceci:

from p in context.Periods 
join f in context.Facts on p.id equals f.periodid into fg 
from fgi in fg.DefaultIfEmpty() 
where p.companyid == 100 && fgi.otherid == 17 
select f.value 

semble être équivalent à ceci:

SELECT f.value 
FROM period as p 
LEFT OUTER JOIN facts AS f ON p.id = f.periodid 
WHERE p.companyid = 100 AND f.otherid = 17 

qui est pas tout à fait ce que je suis après.

+0

Sweet!Je l'ai cherché pendant un certain temps, mais je ne savais pas comment chercher cela. Vous ne savez pas comment ajouter des tags à cette réponse. Voici les critères de recherche que j'ai utilisés: linq to sql filter dans join ou à partir de linq to sql où clause dans join ou – Solburn

Répondre

205

Vous devez introduire votre condition de jointure avant d'appeler le DefaultIfEmpty(). Je voudrais simplement utiliser la syntaxe de méthode d'extension:

from p in context.Periods 
join f in context.Facts on p.id equals f.periodid into fg 
from fgi in fg.Where(f => f.otherid == 17).DefaultIfEmpty() 
where p.companyid == 100 
select f.value 

Ou vous pouvez utiliser une sous-requête:

from p in context.Periods 
join f in context.Facts on p.id equals f.periodid into fg 
from fgi in (from f in fg 
      where f.otherid == 17 
      select f).DefaultIfEmpty() 
where p.companyid == 100 
select f.value 
5

Une autre option valable est de diffuser les jointures dans plusieurs clauses de LINQ, comme suit:

public static IEnumerable<Announcementboard> GetSiteContent(string pageName, DateTime date) 
{ 
    IEnumerable<Announcementboard> content = null; 
    IEnumerable<Announcementboard> addMoreContent = null; 
     try 
     { 
      content = from c in DB.Announcementboards 
       //Can be displayed beginning on this date 
       where c.Displayondate > date.AddDays(-1) 
       //Doesn't Expire or Expires at future date 
       && (c.Displaythrudate == null || c.Displaythrudate > date) 
       //Content is NOT draft, and IS published 
       && c.Isdraft == "N" && c.Publishedon != null 
       orderby c.Sortorder ascending, c.Heading ascending 
       select c; 

      //Get the content specific to page names 
      if (!string.IsNullOrEmpty(pageName)) 
      { 
       addMoreContent = from c in content 
        join p in DB.Announceonpages on c.Announcementid equals p.Announcementid 
        join s in DB.Apppagenames on p.Apppagenameid equals s.Apppagenameid 
        where s.Apppageref.ToLower() == pageName.ToLower() 
        select c; 
      } 

      //CROSS-JOIN this content 
      content = content.Union(addMoreContent); 

      //Exclude dupes - effectively OUTER JOIN 
      content = content.Distinct(); 

      return content; 
     } 
    catch (MyLovelyException ex) 
    { 
     throw ex; 
    } 
} 
+0

ne serait-il pas plus lent que de faire toute l'opération dans une seule requête linq? –

+0

@ umar-t, Oui très probablement, considérant que c'était il y a plus de huit ans quand je l'ai écrit. Personnellement, j'aime la sous-requête corrélée postulée par Dahlbyk ici https://stackoverflow.com/a/1123051/212950 – MAbraham1

23

cela fonctionne également, ... si vous avez plusieurs jointures de colonnes

from p in context.Periods 
join f in context.Facts 
on new { 
    id = p.periodid, 
    p.otherid 
} equals new { 
    f.id, 
    f.otherid 
} into fg 
from fgi in fg.DefaultIfEmpty() 
where p.companyid == 100 
select f.value 
-1

Il me semble qu'il est utile d'envisager certaines réécritures dans votre code SQL avant de tenter de le traduire.

Personnellement, j'écrirait une telle requête en tant que syndicat (même si il faut éviter nulls tout!):

SELECT f.value 
    FROM period as p JOIN facts AS f ON p.id = f.periodid 
WHERE p.companyid = 100 
     AND f.otherid = 17 
UNION 
SELECT NULL AS value 
    FROM period as p 
WHERE p.companyid = 100 
     AND NOT EXISTS ( 
         SELECT * 
         FROM facts AS f 
         WHERE p.id = f.periodid 
          AND f.otherid = 17 
        ); 

Je suppose que je suis d'accord avec l'esprit de la réponse de @ MAbraham1 (bien que leur code semble être sans rapport avec la question).

Cependant, il semble que la requête est expressément conçue pour produire un résultat de colonne unique comprenant des lignes en double - en effet des doublons de null! Il est difficile de ne pas conclure que cette approche est erronée.

7

Je sais qu'il est « un peu en retard », mais juste au cas où si quelqu'un a besoin de le faire dans LINQ Méthode syntaxe (qui est la raison pour laquelle j'ai trouvé ce poste initialement), ce serait de savoir comment faire :

var results = context.Periods 
    .GroupJoin(
     context.Facts, 
     period => period.id, 
     fk => fk.periodid, 
     (period, fact) => fact.Where(f => f.otherid == 17) 
           .Select(fact.Value) 
           .DefaultIfEmpty() 
    ) 
    .Where(period.companyid==100) 
    .SelectMany(fact=>fact).ToList(); 
+0

Très utile pour voir la version lambda! – Learner

Questions connexes