LINQ to SQL - Birden çok birleştirme koşuluyla Sol Dış Birleştirme


149

LINQ'ya çevirmeye çalıştığım aşağıdaki SQL var:

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

Sol dış birleşimin tipik uygulamasını gördüm (örn. into x from y in x.DefaultIfEmpty()Vb.), Ancak diğer birleştirme koşulunu ( AND f.otherid = 17) nasıl tanıtacağımdan emin değilim

DÜZENLE

AND f.otherid = 17Koşul neden WHERE yan tümcesi yerine JOIN'in bir parçasıdır? Çünkü fbazı satırlar için mevcut olmayabilir ve yine de bu satırların dahil edilmesini istiyorum. Koşul, JOIN'den sonra WHERE yan tümcesine uygulanırsa - o zaman istediğim davranışı alamıyorum.

Maalesef bu:

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

buna eşdeğer görünüyor:

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

bu tam olarak peşinde olduğum şey değil.


Tatlı! Bunu bir süredir arıyordum ama bunu nasıl arayacağımdan emin değildim. Bu yanıta nasıl etiket ekleneceğinden emin değilim. İşte kullandığım arama kriterleri:
Join'te linq'den sql'ye filtre

Yanıtlar:


245

Aramadan önce katılma koşulunuzu belirtmeniz gerekir DefaultIfEmpty(). Ben sadece uzantı yöntemi sözdizimini kullanırdım:

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

Veya bir alt sorgu kullanabilirsiniz:

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
From .... defaultifempty ifadesindeki .Where niteleyicisini paylaştığınız için teşekkür ederiz. Bunu yapabileceğini bilmiyordum.
Frank Thomas

28

bu da işe yarar, ... birden fazla sütun birleşiminiz varsa

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

12

Bunun " biraz geç " olduğunu biliyorum, ancak herhangi birinin bunu LINQ Metodu sözdiziminde yapması gerekiyorsa ( bu yüzden başlangıçta bu yazıyı buldum ), bunu nasıl yapacağım:

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

2
Lambda versiyonunu görmek çok faydalı!
Öğrenci

2
.Select(fact.Value)olmalı.Select(f => f.Value)
Petr Felzmann

5

Başka bir geçerli seçenek, birleştirmeleri aşağıdaki gibi birden çok LINQ cümlesine yaymaktır :

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

tüm işlemi tek bir linq sorgusunda yapmaktan daha yavaş olmaz mıydı?
Umar T.

@ umar-t, Evet büyük olasılıkla, bunu yazdığım zamanın sekiz yıldan daha uzun bir süre önce olduğunu düşünürsek. Şahsen ben Dahlbyk tarafından burada öne sürülen ilgili alt sorguyu seviyorum stackoverflow.com/a/1123051/212950
MAbraham1

1
Bir "birleşim", "çapraz birleşmeden" farklı bir işlemdir. Toplama ve çarpma gibi.
Suncat2000

1
@ Suncat2000, düzeltme için teşekkür ederim. Şükran Günü kutlu olsun! 👪🦃🙏
MAbraham1

0

Bileşik birleştirme anahtarı kullanılarak yazılabilir. Ayrıca, özellikleri hem sol hem de sağ taraftan seçmeniz gerekirse, LINQ şu şekilde yazılabilir:

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

-2

Bana öyle geliyor ki, SQL kodunuzu tercüme etmeye çalışmadan önce bazı yeniden yazmaları dikkate almanın değeri var.

Şahsen, sendika olarak böyle bir sorgu yazardım (yine de tamamen boş değerlerden kaçınsam da!):

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

Sanırım @ MAbraham1'in cevabının ruhuna katılıyorum (kodlarının soruyla alakası yok gibi görünse de).

Ancak, görünüşe göre sorgu açıkça yinelenen satırlardan oluşan tek bir sütun sonucu üretmek için tasarlanmış gibi görünüyor - aslında yinelenen boşlar! Bu yaklaşımın kusurlu olduğu sonucuna varmamak zor.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.