2013-04-25 4 views
3

관계 부분은 Codd의 프리미티브 관계 연산자 중 하나이며, 모든 부분을 제공하는 공급자로 구어체로 알려져 있습니다. 예를 들어 SQL에 다양한 번역이있었습니다. Celko는 the pilots who can fly all the planes in the hangar의 예를 사용하여 몇 가지 접근법에 대해 설명합니다.LINQ의 관계 구분?

내가 선호하는 것은 "나머지와 함께"(즉, 윌슨도 F-17 전투기를 비행 할 수 있지만 격납고에는 하나도 없기 때문입니다.)

WITH PilotSkills 
    AS 
    (
     SELECT * 
     FROM (
       VALUES ('Celko', 'Piper Cub'), 
        ('Higgins', 'B-52 Bomber'), ('Higgins', 'F-14 Fighter'), 
        ('Higgins', 'Piper Cub'), 
        ('Jones', 'B-52 Bomber'), ('Jones', 'F-14 Fighter'), 
        ('Smith', 'B-1 Bomber'), ('Smith', 'B-52 Bomber'), 
        ('Smith', 'F-14 Fighter'), 
        ('Wilson', 'B-1 Bomber'), ('Wilson', 'B-52 Bomber'), 
        ('Wilson', 'F-14 Fighter'), ('Wilson', 'F-17 Fighter') 
      ) AS T (pilot_name, plane_name) 
    ), 
    Hangar 
    AS 
    (
     SELECT * 
     FROM (
       VALUES ('B-1 Bomber'), 
        ('B-52 Bomber'), 
        ('F-14 Fighter') 
      ) AS T (plane_name) 
    ) 
SELECT DISTINCT pilot_name 
    FROM PilotSkills AS P1 
WHERE NOT EXISTS (
        SELECT plane_name 
        FROM Hangar 
        EXCEPT 
        SELECT plane_name 
        FROM PilotSkills AS P2 
        WHERE P1.pilot_name = P2.pilot_name 
       ); 

가 지금은 객체에 LINQ에서이 작업을 수행 할 필요가 : 제수가 빈 세트 (격납고가 비어 즉 때 모든 조종사가 반환)입니다. 제안 된 직접 번역본은 다음과 같습니다.

var hangar = new [] 
{ 
    new { PlaneName = "B-1 Bomber" }, 
    new { PlaneName = "F-14 Fighter" }, 
    new { PlaneName = "B-52 Bomber" } 
}.AsEnumerable(); 

var pilotSkills = new [] 
{ 
    new { PilotName = "Celko", PlaneName = "Piper Cub" }, 
    new { PilotName = "Higgins", PlaneName = "B-52 Bomber" }, 
    new { PilotName = "Higgins", PlaneName = "F-14 Fighter" }, 
    new { PilotName = "Higgins", PlaneName = "Piper Cub" }, 
    new { PilotName = "Jones", PlaneName = "B-52 Bomber" }, 
    new { PilotName = "Jones", PlaneName = "F-14 Fighter" }, 
    new { PilotName = "Smith", PlaneName = "B-1 Bomber" }, 
    new { PilotName = "Smith", PlaneName = "B-52 Bomber" }, 
    new { PilotName = "Smith", PlaneName = "F-14 Fighter" }, 
    new { PilotName = "Wilson", PlaneName = "B-1 Bomber" }, 
    new { PilotName = "Wilson", PlaneName = "B-52 Bomber" }, 
    new { PilotName = "Wilson", PlaneName = "F-14 Fighter" }, 
    new { PilotName = "Wilson", PlaneName = "F-17 Fighter" } 
}.AsEnumerable(); 

var actual = pilotSkills.Where 
(
    p1 => hangar.Except 
    ( 
     pilotSkills.Where(p2 => p2.PilotName == p1.PilotName) 
        .Select(p2 => new { p2.PlaneName }) 
    ).Any() == false 
).Select(p1 => new { p1.PilotName }).Distinct(); 

var expected = new [] 
{ 
    new { PilotName = "Smith" }, 
    new { PilotName = "Wilson" } 
}; 

Assert.That(actual, Is.EquivalentTo(expected)); 

LINQ is supposedly based on the relational algebra 다음은 직접적인 번역입니다. 그러나 더 나은 '기본'LINQ 접근 방식이 있습니까? @Daniel Hilgarth의 대답에 반영


, .NET 땅에있는 데이터로 시작하는 '그룹화'될 가능성이 :

var pilotSkills = new [] 
{ 
    new { PilotName = "Celko", 
      Planes = new [] 
      { new { PlaneName = "Piper Cub" }, } }, 
    new { PilotName = "Higgins", 
      Planes = new [] 
      { new { PlaneName = "B-52 Bomber" }, 
       new { PlaneName = "F-14 Fighter" }, 
       new { PlaneName = "Piper Cub" }, } }, 
    new { PilotName = "Jones", 
      Planes = new [] 
      { new { PlaneName = "B-52 Bomber" }, 
       new { PlaneName = "F-14 Fighter" }, } }, 
    new { PilotName = "Smith", 
      Planes = new [] 
      { new { PlaneName = "B-1 Bomber" }, 
       new { PlaneName = "B-52 Bomber" }, 
       new { PlaneName = "F-14 Fighter" }, } }, 
    new { PilotName = "Wilson", 
      Planes = new [] 
      { new { PlaneName = "B-1 Bomber" }, 
       new { PlaneName = "B-52 Bomber" }, 
       new { PlaneName = "F-14 Fighter" }, 
       new { PlaneName = "F-17 Fighter" }, } }, 
}; 

... 그냥 이름을 투사하는 만들기, 임의 훨씬 더 간단 잠재적 인 솔루션 :

// Easy to understand at a glance: 
var actual1 = pilotSkills.Where(x => hangar.All(y => x.Planes.Contains(y))); 

// Potentially more efficient: 
var actual = pilotSkills.Where(x => !hangar.Except(x.Planes).Any()); 
+0

당신은 배열

+0

커드의 부문 원시적되지는 IEnumerable있다는'AsEnumerable을()'이 필요하지 않습니다. 그리고 비공식적으로 그것은 적어도 하나의 부품을 공급하는 "모든 부품을 공급하는 공급 업체"*와 *를 반환합니다. (비록 당신의 비공식적 인 표현이 슬프게도 공통적 인 것이라는 것에 동의하지만) 실제로 그렇게 유용하지는 않습니다. "접근법"은 원본의 "번역"이 아니며 원본 * 및 다른 그러나 연상되는 * 연산자의 번역입니다. (베스트/관계형 하위 집합 연산자의 관점에서 가장 쉬운 이유는 다음 대수학/미적분으로 변환합니다.) 추신 : "LINQ는 아마도 관계형 대수학을 기반으로합니다"- 하하. 영감을 얻어, OK. – philipxy

+0

@philipxy 내가 인용 한 기사에서 인용 : "이 기사에서는 관계형 대수학의 일반화로서 모나드와 LINQ를 설명합니다." 당신이 논박에 대한 표창장을 가지고 있다면 나는 매우 관심이있을 것입니다. 마찬가지로, 나는 분열이 원시적이라고 암시하려는 것이 아니고 내가 가지고 있다고 생각하지 않는다. 내가 링크 된 기사는 (다소 비공식적으로) Todd와 Romley를 언급합니다. 그러나 부서가 기본 운영자가 아닌 이유에 대해 읽으려는 무언가가 있다면 감사하게 받아 들여야합니다. – onedaywhen

답변

5

다음은 동일한 결과를해야 다음

이것은 격납고의 모든 비행기를 조종 할 수있는 조종사 당 하나의 그룹을 반환합니다.
그룹의 열쇠는 조종사의 이름이고, 조종사가 날 수있는 모든 비행기, 격납고에없는조차 조종사의 내용입니다.

이제 조종사 만 원하는 경우 .Select(g => new { PilotName = g.Key })을 쿼리 끝에 추가 할 수 있습니다. 그것은 단지 한 번 g을 반복 처리하기 때문에,

pilotSkills.GroupBy(x => x.PilotName, x => new { x.PlaneName }) 
      .Where(g => !hangar.Except(g).Any()); 

이 두 번째 쿼리가 잠재적으로 더 나은;


가까운 영업 이익의 원본을 만드는 Except와 함께 위의 방법을 사용 Contains의 첫 번째 쿼리는 N 번 반복하고 격납 장치의 평면 수를 N으로합니다. 가까이 내 원래에 @Daniel Hilgarth의 대답하지만 방법을 사용

+0

* $ (* % £ "대답에 나를 때려 눕히면 OP와 정확히 같은 결과를 얻기 위해 결국 선택이 필요합니다. ... –

+1

@BobVale : 예, 10 분 동안 당신을 이길 수 있습니다. p 그리고 네, 이미 셀렉트를 추가했습니다. 격납고에있는 비행기뿐만 아니라 모든 비행기가 반환되었음을 증명하는 것이 더 중요했습니다. –

+0

네, 제출을 누르는 것처럼 새로운 대답으로 나타납니다. .. –

1

:

var actual = pilotSkills 
       .GroupBy(x => x.PilotName, x => new { x.PlaneName }) 
       .Where(g => !hangar.Except(g).Any()) 
       .Select(x => new { PilotName = x.Key }); 
+0

'Except'가있는 해결책은'g'를 한 번만 반복하기 때문에 잠재적으로 더 좋습니다. 내 솔루션은'Contains'를 N 번 반복하여 격납고에있는 비행기의 수를 N으로 반복합니다. –