153 votes

LINQ to 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 l'implémentation typique de la jointure externe gauche (c'est-à-dire. into x from y in x.DefaultIfEmpty() etc.) mais je ne sais pas comment introduire l'autre condition de jointure ( AND f.otherid = 17 )

EDIT

Pourquoi le AND f.otherid = 17 dans la clause JOIN plutôt que dans la clause WHERE ? Parce que f peut ne pas exister pour certaines lignes et je veux quand même que ces lignes soient incluses. Si la condition est appliquée dans la clause WHERE, après le JOIN, je n'obtiens pas le comportement que je souhaite.

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 équivalente à celle-ci :

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 recherche.

0 votes

Super ! Je le cherchais depuis un moment mais je ne savais pas comment faire une recherche. Je ne sais pas comment ajouter des balises à cette réponse. Voici les critères de recherche que j'ai utilisé : linq to sql filter in join or from linq to sql where clause in join or from

252voto

dahlbyk Points 24897

Vous devez introduire votre condition de jointure 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-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

2 votes

Merci de nous avoir fait part du qualificateur .Where sur l'instruction from .... defaultifempty. Je ne savais pas que l'on pouvait faire cela.

29voto

ZenXavier Points 121

Cela fonctionne aussi, ...si vous avez des jointures à colonnes multiples.

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

12voto

Prokurors Points 147

Je sais que c'est " un peu en retard "mais juste au cas où si quelqu'un a besoin de faire cela en Syntaxe des méthodes LINQ ( c'est pourquoi j'ai trouvé ce post au départ ), c'est ainsi qu'il faut procéder :

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

5voto

MAbraham1 Points 513

Une autre option valable est de répartir les jonctions à travers clauses LINQ multiples 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;
            }

            // Add the specified content using UNION
            content = content.Union(addMoreContent);

            // Exclude the duplicates using DISTINCT
            content = content.Distinct();

            return content;
        }
    catch (MyLovelyException ex)
    {
        // Add your exception handling here
        throw ex;
    }
}

0 votes

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

0 votes

@umar-t, Oui très probablement, étant donné 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 proposée par Dahlbyk ici. stackoverflow.com/a/1123051/212950

1 votes

Une "union" est une opération différente d'une "jonction croisée". C'est comme l'addition par rapport à la multiplication.

0voto

Petr Felzmann Points 505

Peut être écrit en utilisant une clé de jointure composite. De même, s'il est nécessaire de sélectionner des propriétés à la fois à gauche et à droite, le LINQ peut être écrit comme suit

var result = context.Periods
    .Where(p => p.companyid == 100)
    .GroupJoin(
        context.Facts,
        p => new {p.id, otherid = 17},
        f => new {id = f.periodid, f.otherid},
        (p, f) => new {p, f})
    .SelectMany(
        pf => pf.f.DefaultIfEmpty(),
        (pf, f) => new MyJoinEntity
        {
            Id = pf.p.id,
            Value = f.value,
            // and so on...
        });

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X