2012-10-05 3 views
3

내가 표준 GroupJoin/SelectMany/DefaultIfEmpty 접근 방식을 사용하여 LINQ에 참여를 왼쪽으로 수행하는 방법을 쓴 가입추가 하위 쿼리

var q = myDB.PurchaseOrderHeaders 
    .LeftJoin(
     myDB.PurchaseOrderLines, 
     po => po.PurchaseOrderGUID, 
     line => line.PurchaseOrderGUID, 
     (po, line) => new { PO = po, Line = line } 
    ); 

var e = q.AsEnumerable(); 

나는 이런 식으로 SQL을 예상했다 :

SELECT [t0].[PurchaseOrderGUID], ..., [t1].[PurchaseOrderLineGUID], ... 
FROM [dbo].[PurchaseOrderHeader] AS [t0] 
LEFT OUTER JOIN [dbo].[PurchaseOrderLine] AS [t1] 
    ON [t0].[PurchaseOrderGUID] = [t1].[PurchaseOrderGUID] 

그러나 이것을 얻었다 :

SELECT [t0].[PurchaseOrderGUID], ..., [t2].[test], [t2].[PurchaseOrderLineGUID], ... 
FROM [dbo].[PurchaseOrderHeader] AS [t0] 
LEFT OUTER JOIN (
    SELECT 1 AS [test], [t1].[PurchaseOrderLineGUID], ... 
    FROM [dbo].[PurchaseOrderLine] AS [t1] 
    ) AS [t2] ON [t0].[PurchaseOrderGUID] = [t2].[PurchaseOrderGUID] 

차이점은 SELECT 1 as [test] 인 하위 쿼리입니다. 왜 이것을 생성합니까? 성능에 중대한 영향을 미칠 가능성이 있습니까? 그렇다면 쿼리를 수정하여이를 제거 할 수 있습니까?

답변

2

(면책 조항 : 나는 LINQ에 대해 많이 모르는 아래 내 SQL의 지식, LINQ가 시도 된 사항에 대한 교양 추론을 기반으로합니다..) 왜 생성하는

이?

나는 1 AS [test]의 목적은 LINQ에게 "PurchaseOrderLine 하나 개 일치하는 레코드"에서 "PurchaseOrderLine에 일치하는 기록을"구별하지 할 수있는 명확하고 단순하고 일관하고 명확한 방법을 제공하는 것을 가정한다. PurchaseOrderLineGUID과 다른 필드를 살펴봄으로써 이들을 구별 할 수 있다고 생각할 수도 있습니다. 이는 아마도 귀하의 경우에 해당 될 것입니다. 일반적인 경우에 LEFT JOIN이 레코드에 성공적으로 조인되었지만 해당 레코드에서 선택된 모든 필드가 null 인 경우는 어떻게됩니까? (귀하의 경우에는 PurchaseOrderLineGUID이 nullable이 아니기 때문에 가능하지 않지만 LINQ는이를 알고 있습니다. 심지어 어떤 테이블 열이 nullable이 아닌지를 모르는 경우에도 사람 쿼리 작성자는 ON 절은 일치가 성공한 경우 [t2].[PurchaseOrderGuid]이 null이 될 수있는 가능성을 방지하기 때문에 최상위 필드 목록에서 [t2].[PurchaseOrderGuid] AS [test]을 사용하여 하위 쿼리를 피했지만 LINQ가 얼마나 명확한 지 확신 할 수 없습니다.

성능에 중요한 영향을 미칩니 까?

이 아니어야합니다. 쿼리 의미 (예 : WHERE 또는 ON 또는 GROUP BY 또는 HAVING 절)에 실제로 영향을 줄 수있는 곳에서는 1 AS [test]이 사용되지 않기 때문에 SQL Server는 ON 조건을 "의미 적 푸시 다운"조건으로 옮길 수 있어야합니다 하위 쿼리를 생성하고 PurchaseOrderHeaderPurchaseOrderLine 사이의 정규 인덱싱 된 해시 조인을 수행하여 필요한 레코드를 결정합니다. 1 AS [test]은 결과 세트를 어셈블 할 때 실제로 선택되는 PurchaseOrderLine 레코드에 대해서만 추가됩니다. 제가 위에서 언급 한 바와 같이, 때문에 SQL 서버는 술어 푸시 다운 — even in the rare cases where that turns out to be a bad thing —에 부분적으로 좋다는 것을 알고 있기 때문에

는 (나는 할 수 LINQ 이 경우에 서브 쿼리를 만드는 피할 부분이 말한다.나는 LINQ 팀이 자신들이하는 일을 알고 있다고 생각하고 하위 쿼리가 성능상의 불이익을 가질 수 있다고 생각한다면 LINQ가 특정 사례에 실제로 하위 쿼리가 필요한지 여부를 결정하기 위해 노력할 것이라고 추측합니다. LINQ는 문제가되지 않으므로 아마도 문제가 아니기 때문일 수 있습니다.)

0

LinqKit 이러한 문제를 해결하는 데 도움이됩니다. 다음 확장은 nice SQL을 생성합니다.

public static IQueryable<TResult> LeftJoin<TOuter, TInner, TKey, TResult>(
    this IQueryable<TOuter> outer, 
    IQueryable<TInner> inner, 
    Expression<Func<TOuter, TKey>> outerKeySelector, 
    Expression<Func<TInner, TKey>> innerKeySelector, 
    Expression<Func<TOuter, TInner, TResult>> result) { 

    return outer.GroupJoin(
      inner, 
      outerKeySelector, 
      innerKeySelector, 
      (a, b) => new { a, b }).AsExpandable() 
     .SelectMany(
      z => z.b.DefaultIfEmpty(), 
      (z, b) => result.Invoke(z.a, b)); 
}