2011-08-01 3 views
0

두 개의 오브젝트 (테이블) A와 B를 조인해야합니다. 모든 A에 대해 0에서 많은 B가있을 수 있습니다. 쿼리는 A 당 하나의 행을 반환해야합니다.Linq를 사용하여 "복잡한"조인을 수행하는 방법

B 조 특정 조건 다음에 B에서 필요한 행을 선택할 수 있도록 조인 전에 주문하고 싶습니다. B가 컬럼 타입을 가지고 있다고 해. 타입 1이 있다면 그것은 내가 필요로하는 B입니다. 타입 2를 선택해야합니다.

이제는 T-SQL에서 어떻게 할 것인가에 대해 잘 모르겠습니다. 나는이 같은 생각 :

SELECT A.* 
FROM A LEFT JOIN (
    SELECT * FROM B AS B1 WHERE B1.Type = (SELECT TOP 1 B2.Type FROM B AS B2 
             WHERE B2.JoinID = B1.JoinID 
             ORDER BY B2.Type) 
) AS B ON B.JoinID = A.JoinID 

가 [편집] sgtz의 대답

나는 그것이 작동하도록 시도했습니다. 주문하려는 필드가 없기 때문에 추가 단계를 만들어야합니다. 1 단계에서이 필드를 추가합니다. 2 단계에서 3 단계에서 모든 키를 선택하고 조인합니다. 그러나 "조인 절에있는 식 중 하나의 형식이 잘못되었습니다. 유형 추론에 실패했습니다. 'GroupJoin'에 대한 호출. " 가입에

var adressen1 = from a in db.Adres 
        select new 
        { 
         RelatieAdres = a, 
         Sortering = (int)(a.AdresType.Code == codeVestAdres ? 
              1 : a.AdresType.Code == codePostAdres ? 
               2 : (100 + (int)a.AdresType.Code.ToCharArray()[0])) 
        }; 

    var adressen2 = from b in adressen1 
        group b by new { RelatieID = b.RelatieAdres.RelatieID } into p 
        let TopAdresType = p.Min(at => at.Sortering) 
        select new { TopRelatieID = p.Key.RelatieID, TopAdresType }; 

    var q = from k in db.Klants 
      join b in adressen2 on k.RelatieID equals b.TopRelatieID into b_join 
      from b in b_join.DefaultIfEmpty() 
      join a in adressen1 on new { b.TopRelatieID, b.TopAdresType } equals new { a.RelatieAdres.RelatieID, a.Sortering } into a_join 
      from a in a_join.DefaultIfEmpty() 
+0

이 쿼리를 사용하면 A 행의 모든 ​​행을 반환합니다. 각 행은 B 행의 행이 한 번 또는 여러 번 반복되며, 행과 연결되어 있고 WHERE 조건을 만족합니다. 이는 매우 이상하게 보입니다. .. –

+0

@Stefan : 답변을 게시했습니다. – sgtz

+0

@MichaelSagalovich 이상하게 들릴지 모르지만, 목적이 무엇인지 설명하면되지 않습니다. A = 고객 및 B = 주소. 나는 단지 하나의 주소를 가진 모든 고객을 원할 것입니다. 주소가 존재한다면 그렇지 않으면 청구서 수신 주소가 될 것입니다 ... –

답변

0

다음은 작동하는 예제입니다. 나는 그것을 두 단계로 수행했다. 의견에

[Test] 
    public void Test333() 
    { 
     List<Order> O; 
     var M = Prepare333Data(out O); 

     var OTop = from o in O 
        group o by new {id=o.id, orderid=o.orderid} 
        into p 
        let topType = p.Min(tt => tt.type) 
        select new Order(p.Key.id, p.Key.orderid, topType); 

     var ljoin = from m in M 
        join t in OTop on m.id equals t.id into ts 
        from u in ts.DefaultIfEmpty() 
        select new {u.id, u.orderid, u.type}; 
    } 

    public class Manufacturer 
    { 
     public Manufacturer(int id, string name) 
     { 
      this.id = id; 
      this.name = name; 
     } 

     public int id { get; set; } 
     public string name { get; set; } 
    } 

    public class Order 
    { 
     public Order(int id, int orderid, int type) 
     { 
      this.orderid = orderid; 
      this.id = id; 
      this.type = type; 
     } 

     public int orderid { get; set; } 
     public int id { get; set; } 
     public int type { get; set; } 
    } 


    private List<Manufacturer> Prepare333Data(out List<Order> O) 
    { 
     var M = new List<Manufacturer>() {new Manufacturer(1, "Abc"), new Manufacturer(2, "Def")}; 
     O = new List<Order>() 
       { 
        new Order(1, 1, 2), 
        new Order(1, 2, 2), 
        new Order(1, 2, 3), 
        new Order(2, 3, 1) 
        , 
        new Order(2, 3, 1) 
        , 
        new Order(2, 3, 2) 
       }; 
     return M; 
    } 

응답 :

당신의 "새로운 {"새 익명 형식을 만듭니다. 차이 프로세스에 의해 생성 된 두 가지 익명 형식은 형식이 동일한 순서로 선언되고 형식 정의가 동일한 경우 (즉, int가 int와 일치하지 않고 int가 짧음) 동일한 서명이 있다고합니다. 이 시나리오는 LINQ에서 광범위하게 테스트하지 않았습니다.

그래서 내가 실제 구체적인 클래스로 작업했기 때문에 JOIN 부분에 anon 유형이 없습니다. 아마도 순수한 LINQ로 다시 작성하는 방법이있을 것입니다.하지만 그게 뭔지 모르겠습니다. 아직입니다. 그것이 나에게 일어나는 경우 나는 당신에게 대답을 게시 할 것이다.

구체적인 클래스를 사용하는 것이 좋습니다.
즉 대신, 항상 ... 안전 그것은 조금 더, 그러나 안전

*new CLASSNAME(){prop1="abc",prop2="123"* 

를 사용하여 조인 할 때

*new {* 

의 우리가 LINQ 내부에서 무슨 일이 진행되고 있는지 해결 될 때까지 적어도 내부.

+0

차가워서, 많은 도움이됩니다. 한 가지는 : 어떻게하면 B의 다른 구성원을 얻을 수 있습니까 (귀하의 경우 O)? 이 예제는 키 조합과 최소 유형을 검색 만하지만 다른 멤버를 얻는 방법은 무엇입니까? –

+0

나는 생각했다. "SELECT TOP 1 ... ORDER BY ..."가하고 있었던 것이었다. 어떤 다른 멤버들 - 모두들? – sgtz

+0

죄송합니다, 아주 좋지 않아. 당신의 해결책은 좋습니다! –

0

의미하기 위해, 당신은 결과뿐만 아니라 A.를 조회하기 위해 적어도 무언가를 추가해야 "... 새 {b.TopRelatieID에 adressen1에 가입"*. 그렇지 않으면 일부 행이 중복 될 수있는 A 사본을 갖게됩니다.

SELECT DISTINCT A.*, B.Type 
FROM A LEFT JOIN 
(SELECT TOP (1) JoinID, Type 
FROM B 
ORDER BY Type 
GROUP BY JoinID, Type 
) AS B ON A.JoinID = B.JoinID 

LINQ로 번역, 그것이 (업데이트])

(from a in As 
join b in 
(from b1 in Bs 
orderby b1.Type 
group b1 by b1.JoinID into B1 
from b11 in B1 
group b11 by b11.Type into B11 
from b111 in B11 
select new { b111.JoinID, b111.Type }).Take(1) 
on a.JoinID equals b.JoinID into a_b 
from ab in a_b.DefaultIfEmpty()    
select new { a_b.JoinID, /*all other a properties*/ a_b.Type }).Distinct() 

LINQ는 100 % 정확한 작동하지 않을 수 있습니다,하지만 당신은 잡아해야합니다 내가 제대로 질문을 이해하면,이 SQL 쿼리 작업을해야합니다 아이디어.

+0

이것은 내가 찾고있는 것이 아닌 여러 조합 A와 B가 될 것입니다. 하나의 레코드 A에 하나의 (또는 제로) 레코드 B가 필요합니다. –

+0

@Stefan 나는 TOP 1을 잊어 버렸습니다, 미안 해요. 수정 됨. –

관련 문제