LINQ à SQL-jointure externe gauche avec plusieurs conditions de jointure

j'ai le SQL suivant, que j'essaie de traduire en LINQ:

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 (c.-à-d. into x from y in x.DefaultIfEmpty() etc.) mais je ne sais pas comment introduire l'autre condition de jointure ( AND f.otherid = 17 )

MODIFIER

pourquoi la condition AND f.otherid = 17 fait-elle partie de la jointure au lieu de la clause où? Parce que f peut ne pas exister pour certains les lignes et je veux encore ces lignes pour être inclus. Si la condition est appliquée dans la clause où, après la jointure - alors je ne comprends 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

ce qui n'est pas tout à fait ce que je cherche.

133
demandé sur RustyTheBoyRobot 2009-07-14 04:53:09

5 réponses

vous devez introduire votre condition d'adhésion avant d'appeler DefaultIfEmpty() . J'utiliserais simplement la syntaxe de la 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-commande:

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
219
répondu dahlbyk 2013-05-08 17:03:18

cela fonctionne aussi ...si vous avez plusieurs colonnes jointures

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
24
répondu ZenXavier 2012-03-16 20:59:24

je sais que c'est" un peu tard " mais juste au cas où quelqu'un doit faire cela dans syntaxe de la méthode LINQ ( c'est pourquoi j'ai trouvé ce post initialement ), ce serait la façon de faire cela:

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();
9
répondu Prokurors 2015-10-18 17:36:16

une autre option valable consiste à étaler les jointures sur multiple LINQ clauses , 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;
    }
}
5
répondu MAbraham1 2010-06-14 19:58:33

il me semble qu'il est utile de considérer certaines réécritures de votre code SQL avant de tenter de le traduire.

personnellement, j'écrirais une telle question comme un syndicat (bien que j'éviterais complètement nulls!):

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

donc 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çu pour produire une seule colonne résultat comprenant des lignes en double-double zéros! Il est difficile de ne pas en arriver à la conclusion que cette approche est imparfaite.

-1
répondu onedaywhen 2013-05-08 13:57:13