방법: 사용자 지정 조인 작업 수행(C# 프로그래밍 가이드)

이 예제에서는 join 절에서 사용할 수 없는 조인 작업을 수행하는 방법을 보여 줍니다.쿼리 식에서 join 절은 가장 일반적인 조인 작업 형식인 동등 조인으로 제한되며 이에 맞게 최적화되어 있습니다.동등 조인을 수행하는 경우 항상 join 절을 사용하여 최상의 성능을 얻을 수 있습니다.

하지만 다음과 같은 경우에는 join 절을 사용할 수 없습니다.

  • 다름(비동등) 식에서 조인을 서술하는 경우

  • 둘 이상의 동등 또는 비동등 식에서 조인을 서술하는 경우

  • 조인 작업 전에 오른쪽(내부) 시퀀스에 대해 임시 범위 변수를 적용해야 하는 경우

동등 조인이 아닌 조인을 수행하려면 여러 개의 from 절을 사용하여 각 데이터 소스를 개별적으로 적용합니다.그런 다음 where 절의 조건자 식을 각 소스의 범위 변수에 적용합니다.식이 메서드 호출 형태를 사용할 수도 있습니다.

[!참고]

이 종류의 사용자 지정 조인 작업을 여러 개의 from 절을 사용한 내부 컬렉션 액세스와 혼동하지 마십시오.자세한 내용은 join 절(C# 참조)을 참조하십시오.

예제

다음 예제의 첫 번째 메서드는 단순한 교차 조인을 보여 줍니다.교차 조인은 매우 큰 결과 집합을 생성할 수 있으므로 주의해서 사용해야 합니다.그러나 추가 쿼리가 실행되는 소스 시퀀스를 생성하기 위한 일부 시나리오에서 유용할 수 있습니다.

두 번째 메서드는 범주 ID가 왼쪽의 범주 목록에 나열된 모든 제품의 시퀀스를 생성합니다.임시 배열을 만들기 위한 let 절과 Contains 메서드의 사용을 확인합니다.또한 쿼리 전에 배열을 만들고 첫 번째 from 절을 제거할 수도 있습니다.

     class CustomJoins
     {

         #region Data

         class Product
         {
             public string Name { get; set; }
             public int CategoryID { get; set; }
         }

         class Category
         {
             public string Name { get; set; }
             public int ID { get; set; }
         }

         // Specify the first data source.
         List<Category> categories = new List<Category>()
 { 
     new Category(){Name="Beverages", ID=001},
     new Category(){ Name="Condiments", ID=002},
     new Category(){ Name="Vegetables", ID=003},         
 };

         // Specify the second data source.
         List<Product> products = new List<Product>()
{
   new Product{Name="Tea",  CategoryID=001},
   new Product{Name="Mustard", CategoryID=002},
   new Product{Name="Pickles", CategoryID=002},
   new Product{Name="Carrots", CategoryID=003},
   new Product{Name="Bok Choy", CategoryID=003},
   new Product{Name="Peaches", CategoryID=005},
   new Product{Name="Melons", CategoryID=005},
   new Product{Name="Ice Cream", CategoryID=007},
   new Product{Name="Mackerel", CategoryID=012},
 };
         #endregion

         static void Main()
         {
             CustomJoins app = new CustomJoins();
             app.CrossJoin();
             app.NonEquijoin();

             Console.WriteLine("Press any key to exit.");
             Console.ReadKey();
         }

         void CrossJoin()
         {
             var crossJoinQuery =
                 from c in categories
                 from p in products
                 select new { c.ID, p.Name };

             Console.WriteLine("Cross Join Query:");
             foreach (var v in crossJoinQuery)
             {
                 Console.WriteLine("{0,-5}{1}", v.ID, v.Name);
             }
         }

         void NonEquijoin()
         {
             var nonEquijoinQuery =
                 from p in products
                 let catIds = from c in categories
                              select c.ID
                 where catIds.Contains(p.CategoryID) == true
                 select new { Product = p.Name, CategoryID = p.CategoryID };

             Console.WriteLine("Non-equijoin query:");
             foreach (var v in nonEquijoinQuery)
             {
                 Console.WriteLine("{0,-5}{1}", v.CategoryID, v.Product);
             }
         }
     }
     /* Output:
 Cross Join Query:
 1    Tea
 1    Mustard
 1    Pickles
 1    Carrots
 1    Bok Choy
 1    Peaches
 1    Melons
 1    Ice Cream
 1    Mackerel
 2    Tea
 2    Mustard
 2    Pickles
 2    Carrots
 2    Bok Choy
 2    Peaches
 2    Melons
 2    Ice Cream
 2    Mackerel
 3    Tea
 3    Mustard
 3    Pickles
 3    Carrots
 3    Bok Choy
 3    Peaches
 3    Melons
 3    Ice Cream
 3    Mackerel
 Non-equijoin query:
 1    Tea
 2    Mustard
 2    Pickles
 3    Carrots
 3    Bok Choy
 Press any key to exit.
      */

다음 예제에서는 쿼리가 내부(오른쪽) 시퀀스의 경우 조인 절 자체 전에 가져올 수 없는 일치하는 키를 기반으로 두 개의 시퀀스를 조인해야 합니다.join 절을 사용하여 이 조인을 수행하면 각 요소에 대해 Split 메서드가 호출되어야 합니다.여러 개의 from 절을 사용하면 쿼리가 반복 메서드 호출의 오버헤드를 방지할 수 있습니다.그러나 join이 최적화되므로 이 특별한 경우에서는 여러 개의 from 절을 사용하는 것보다 속도가 더 빠를 수 있습니다.결과는 주로 메서드 호출의 비용에 따라 달라집니다.

class MergeTwoCSVFiles
{
    static void Main()
    {
        // See section Compiling the Code for information about the data files.
        string[] names = System.IO.File.ReadAllLines(@"../../../names.csv");
        string[] scores = System.IO.File.ReadAllLines(@"../../../scores.csv");

        // Merge the data sources using a named type.
        // You could use var instead of an explicit type for the query.
        IEnumerable<Student> queryNamesScores =
            // Split each line in the data files into an array of strings.
            from name in names
            let x = name.Split(',')
            from score in scores
            let s = score.Split(',')
            // Look for matching IDs from the two data files.
            where x[2] == s[0]
            // If the IDs match, build a Student object.
            select new Student()
            {
                FirstName = x[0],
                LastName = x[1],
                ID = Convert.ToInt32(x[2]),
                ExamScores = (from scoreAsText in s.Skip(1)
                              select Convert.ToInt32(scoreAsText)).
                              ToList()
            };

        // Optional. Store the newly created student objects in memory
        // for faster access in future queries
        List<Student> students = queryNamesScores.ToList();

        foreach (var student in students)
        {
            Console.WriteLine("The average score of {0} {1} is {2}.",
                student.FirstName, student.LastName, student.ExamScores.Average());
        }

        //Keep console window open in debug mode
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }
}

class Student
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int ID { get; set; }
    public List<int> ExamScores { get; set; }
}

/* Output: 
    The average score of Omelchenko Svetlana is 82.5.
    The average score of O'Donnell Claire is 72.25.
    The average score of Mortensen Sven is 84.5.
    The average score of Garcia Cesar is 88.25.
    The average score of Garcia Debra is 67.
    The average score of Fakhouri Fadi is 92.25.
    The average score of Feng Hanying is 88.
    The average score of Garcia Hugo is 85.75.
    The average score of Tucker Lance is 81.75.
    The average score of Adams Terry is 85.25.
    The average score of Zabokritski Eugene is 83.
    The average score of Tucker Michael is 92.
 */

코드 컴파일

  • .NET Framework 3.5 이상을 대상으로 하는 Visual Studio 콘솔 응용 프로그램 프로젝트를 만듭니다.기본적으로 프로젝트에는 System.Core.dll에 대한 참조 및 System.Linq 네임스페이스에 대한 using 지시문이 있습니다.

  • Program 클래스를 이전 예제의 코드로 바꿉니다.

  • 방법: 서로 다른 파일의 콘텐츠 조인(LINQ)의 지침에 따라 scores.csv 및 names.csv 데이터 파일을 설정합니다.

  • F5 키를 눌러 프로그램을 컴파일하고 실행합니다.

  • 아무 키나 눌러 콘솔 창을 닫습니다.

참고 항목

작업

방법: Join 절 결과의 순서 정렬(C# 프로그래밍 가이드)

참조

join 절(C# 참조)

개념

LINQ 쿼리 식(C# 프로그래밍 가이드)

조인 작업