LINQ to SQL-Unión externa izquierda con múltiples condiciones de unión


Tengo el siguiente SQL, que estoy tratando de traducir a 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

He visto la implementación típica de la unión externa izquierda (ie. into x from y in x.DefaultIfEmpty() etc.) pero no estoy seguro de cómo introducir la otra condición de unión (AND f.otherid = 17)

EDITAR

¿Por qué es la condición AND f.otherid = 17 parte de la UNIÓN en lugar de en la cláusula WHERE? Porque f puede no existir para algunas filas y todavía quiero que estas filas se incluyan. Si la condición se aplica en la cláusula where, después de la ÚNETE - entonces no tengo el comportamiento que quiero.

Desafortunadamente esto:

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

Parece ser equivalente a esto:

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

Que no es exactamente lo que busco.

Author: RustyTheBoyRobot, 2009-07-14

5 answers

Necesita introducir su condición de unión antes de llamar DefaultIfEmpty(). Simplemente usaría la sintaxis del método de extensión:

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

O puedes usar una subconsulta:

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
Author: dahlbyk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-05-08 17:03:18

Esto también funciona, ...si tiene múltiples columnas joins

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
Author: ZenXavier,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-03-16 20:59:24

Sé que es " un poco tarde ", pero por si acaso, si alguien necesita hacer esto en la sintaxis del Método LINQ (es por eso que encontré este post inicialmente ), esta sería la forma de hacerlo:

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
Author: Prokurors,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-18 17:36:16

Otra opción válida es repartir las uniones entre múltiples cláusulas LINQ, como sigue:

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
Author: MAbraham1,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-06-14 19:58:33

Me parece que hay valor en considerar algunas reescrituras a su código SQL antes de intentar traducirlo.

Personalmente, escribiría una consulta como una unión (¡aunque evitaría nulos por completo!):

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

Así que supongo que estoy de acuerdo con el espíritu de la respuesta de @MAbraham1 (aunque su código parece no estar relacionado con la pregunta).

Sin embargo, parece que la consulta está expresamente diseñada para producir un resultado de una sola columna que comprende filas duplicadas! ¡de hecho, nulos duplicados! Es difícil no llegar a la conclusión de que este enfoque es defectuoso.

 -1
Author: onedaywhen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-05-08 13:57:13