LINQ to SQL - Left Outer Tham gia với nhiều điều kiện tham gia


148

Tôi có SQL sau mà tôi đang cố dịch sang 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

Tôi đã thấy cách triển khai điển hình của phép nối ngoài trái ( into x from y in x.DefaultIfEmpty()v.v.) nhưng không chắc chắn làm thế nào để đưa ra điều kiện nối khác ( AND f.otherid = 17)

BIÊN TẬP

Tại sao AND f.otherid = 17phần điều kiện của THAM GIA thay vì trong mệnh đề WHERE? Bởi vì fcó thể không tồn tại cho một số hàng và tôi vẫn muốn những hàng này được đưa vào. Nếu điều kiện được áp dụng trong mệnh đề WHERE, sau THAM GIA - thì tôi không có hành vi tôi muốn.

Thật không may điều này:

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

dường như tương đương với điều này:

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

đó không phải là những gì tôi đang theo đuổi


Ngọt! Tôi đã tìm kiếm cái này một lúc nhưng không chắc chắn làm thế nào để tìm kiếm cái này. Không chắc chắn làm thế nào để thêm thẻ vào câu trả lời này. Đây là tiêu chí tìm kiếm mà tôi đã sử dụng: bộ lọc linq sang sql trong phép nối hoặc từ linq sang sql trong đó mệnh đề tham gia hoặc từ
Solburn

Câu trả lời:


243

Bạn cần phải giới thiệu điều kiện tham gia của bạn trước khi gọi DefaultIfEmpty(). Tôi sẽ chỉ sử dụng cú pháp phương thức mở rộng:

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

Hoặc bạn có thể sử dụng truy vấn con:

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

1
Cảm ơn bạn đã chia sẻ vòng loại .Where trên câu lệnh from defaultifempty. Tôi không biết bạn có thể làm điều đó.
Frank Thomas

28

điều này cũng hoạt động, ... nếu bạn có nhiều cột tham gia

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

Tôi biết đó là " hơi muộn " nhưng chỉ trong trường hợp nếu có ai cần làm điều này theo cú pháp Phương pháp LINQ ( đó là lý do tại sao tôi tìm thấy bài đăng này ban đầu ), đây sẽ là cách để làm điều đó:

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
Rất hữu ích để xem phiên bản lambda!
Học viên

2
.Select(fact.Value)nên là.Select(f => f.Value)
Petr Felzmann

5

Một tùy chọn hợp lệ khác là trải đều các phép nối trên nhiều mệnh đề LINQ , như sau:

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

nó sẽ chậm hơn so với thực hiện toàn bộ hoạt động trong một truy vấn linq?
Umar T.

@ umar-t, Có rất có thể, xem xét điều này đã hơn tám năm trước khi tôi viết nó. Cá nhân tôi thích truy vấn phụ tương quan được đăng bởi Dahlbyk tại đây stackoverflow.com/a/1123051/212950
MAbraham1

1
"Liên minh" là một hoạt động khác với "tham gia chéo". Nó giống như phép cộng so với phép nhân.
Suncat2000

1
@ Suncat2000, cảm ơn bạn đã sửa chữa. Lễ tạ ơn vui vẻ! 👪🦃🙏
MAbraham1

0

Có thể được viết bằng cách sử dụng khóa kết hợp. Ngoài ra, nếu cần phải chọn các thuộc tính từ cả hai bên trái và phải, LINQ có thể được viết là

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

-1

Dường như với tôi có giá trị trong việc xem xét một số cách viết lại mã SQL của bạn trước khi thử dịch nó.

Cá nhân, tôi sẽ viết một truy vấn như một liên minh (mặc dù tôi sẽ tránh hoàn toàn null!):

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

Vì vậy, tôi đoán rằng tôi đồng ý với tinh thần của câu trả lời của @ MAbraham1 (mặc dù mã của họ dường như không liên quan đến câu hỏi).

Tuy nhiên, có vẻ như truy vấn được thiết kế rõ ràng để tạo ra một kết quả cột duy nhất bao gồm các hàng trùng lặp - thực sự là các bản sao rỗng! Thật khó để không đi đến kết luận rằng phương pháp này là thiếu sót.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.