Share via


HOW TO:執行自訂聯結作業 (C# 程式設計手冊)

更新:2007 年 11 月

下列範例顯示如何執行無法使用 join 子句完成的聯結作業。在查詢運算式中,join 子句受限於等聯結 (Equijoin),並且也針對等聯結進行最佳化,而等聯結是聯結作業最常見的類型。執行等聯結時,您可以藉由使用 join 子句而一律取得最佳效能。

但是,有些情況無法使用 join 子句:

  • 在不等比較 (非等聯結) 運算式上預測有聯結時。

  • 在多個等號比較或不等比較運算式上預測有聯結時。

  • 當您必須在聯結作業前引入右側 (內部) 序列的暫時範圍變數時。

若要執行非等聯結的聯結,您可以使用多個 from 子句個別引入每個資料來源。然後將 where 子句中的述詞 (Predicate) 運算式套用至每個來源的範圍變數。運算式也可以有方法呼叫的形式。

注意事項:

請勿將這類型的自訂聯結作業與使用多個 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 子句之前無法取得內部 (右側) 序列的情況下,查詢必須根據相符的索引鍵聯結兩個序列。如果此聯結是使用 join 子句執行,則必須針對每個項目呼叫 Split 方法。使用多個 from 子句可以讓查詢避免重複方法呼叫的負荷。但是,因為聯結已最佳化,在此特定情況中它仍然快於使用多個子句。結果主要會依據方法呼叫耗費的資源而有所不同。

class MergeTwoCSVFiles : StudentClass
{
    static void Main()
    {
        string[] names = System.IO.File.ReadAllLines(@"../../../names.csv");
        string[] scores = System.IO.File.ReadAllLines(@"../../../scores.csv");

        // Merge the data sources using a named type
        // var could be used instead of an explicit type
        IEnumerable<Student> queryNamesScores =
            from name in names
            let x = name.Split(',')
            from score in scores
            let s = score.Split(',')
            where x[2] == s[0]
            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();
    }
}
/* Output: 
    The average score of Adams Terry is 85.25.
    The average score of Fakhouri Fadi is 92.25.
    The average score of Feng Hanying is 88.
    The average score of Garcia Cesar is 88.25.
    The average score of Garcia Debra is 67.
    The average score of Garcia Hugo is 85.75.
    The average score of Mortensen Sven is 84.5.
    The average score of O'Donnell Claire is 72.25.
    The average score of Omelchenko Svetlana is 82.5.
    The average score of Tucker Lance is 81.75.
    The average score of Tucker Michael is 92.
    The average score of Zabokritski Eugene is 83.
 */

此範例也可以在文章 HOW TO:從多個來源填入物件集合 (LINQ) 中取得。若要編譯及執行此範例,請遵循該文章中的指示。

編譯程式碼

  • 建立以 .NET Framework 3.5 版為目標的 Visual Studio 專案。根據預設,專案有 System.Core.dll 的參考,以及 System.Linq 命名空間的 using 指示詞。

  • 將程式碼複製至您的專案中。

  • 按 F5 編譯和執行程式。

  • 按任何鍵離開主控台視窗。

請參閱

工作

HOW TO:排序 Join 子句的結果 (C# 程式設計手冊)

概念

LINQ 查詢運算式 (C# 程式設計手冊)

聯結作業

參考

join 子句 (C# 參考)