Wykonywanie niestandardowych operacji sprzęganiaPerform custom join operations

W tym przykładzie pokazano, jak wykonywanie operacji łączenia, które nie są możliwe dzięki join klauzuli.This example shows how to perform join operations that aren't possible with the join clause. W wyrażeniu zapytania join klauzula jest ograniczona do i zoptymalizowane pod kątem, equijoins, które są najbardziej typowe rodzaj operacji tworzenia sprzężenia.In a query expression, the join clause is limited to, and optimized for, equijoins, which are by far the most common type of join operation. Wykonując sprzężenie będzie prawdopodobnie zawsze uzyskać najlepszą wydajność, za pomocą join klauzuli.When performing an equijoin, you will probably always get the best performance by using the join clause.

Jednak join nie można używać klauzuli w następujących przypadkach:However, the join clause cannot be used in the following cases:

  • Gdy sprzężenia jest uzależniona na wyrażeniu nierówności (innych niż equijoin).When the join is predicated on an expression of inequality (a non-equijoin).

  • Gdy sprzężenia jest uzależniona na więcej niż jedno wyrażenie równości lub nierówności.When the join is predicated on more than one expression of equality or inequality.

  • Gdy trzeba wprowadzić zmienną zakresu tymczasowy dla sekwencji po prawej stronie (wewnętrzny) przed wykonaniem operacji dołączania.When you have to introduce a temporary range variable for the right side (inner) sequence before the join operation.

Do wykonania sprzężenia, które nie są equijoins, możesz użyć wielu from klauzule niezależnie wprowadzenie każdego źródła danych.To perform joins that aren't equijoins, you can use multiple from clauses to introduce each data source independently. Następnie Zastosuj wyrażenie predykatu w where klauzuli do zmiennej zakresu dla każdego źródła.You then apply a predicate expression in a where clause to the range variable for each source. Wyrażenie może również mieć postać wywołania metody.The expression also can take the form of a method call.

Uwaga

Nie należy mylić tego rodzaju operacji tworzenia sprzężenia niestandardowe z użyciem wielu from klauzul do dostępu do kolekcji wewnętrznego.Don't confuse this kind of custom join operation with the use of multiple from clauses to access inner collections. Aby uzyskać więcej informacji, zobacz klauzuli join.For more information, see join clause.

PrzykładExample

Pierwsza metoda w poniższym przykładzie przedstawiono prosty sprzężenie krzyżowe.The first method in the following example shows a simple cross join. Sprzężenia krzyżowego należy używać ostrożnie, ponieważ mogą one powodować bardzo dużych zestawów wyników.Cross joins must be used with caution because they can produce very large result sets. Jednakże mogą być użyteczne w niektórych scenariuszach tworzenia źródłowych sekwencji, wobec których są uruchamiane dodatkowe zapytania.However, they can be useful in some scenarios for creating source sequences against which additional queries are run.

Druga metoda generuje sekwencję wszystkie produkty, których identyfikator kategorii znajduje się na liście kategorii po lewej stronie.The second method produces a sequence of all the products whose category ID is listed in the category list on the left side. Zwróć uwagę na użycie let klauzuli i Contains metodę, aby utworzyć tablicę tymczasową.Note the use of the let clause and the Contains method to create a temporary array. Również jest możliwe, aby utworzyć tablicę przed zapytania i wyeliminować pierwszy from klauzuli.It also is possible to create the array before the query and eliminate the first from clause.

    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($"{v.ID,-5}{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($"{v.CategoryID,-5}{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.
     */

PrzykładExample

W poniższym przykładzie zapytanie należy przyłączyć dwie sekwencje oparta na zgodności kluczy, które w przypadku sekwencji wewnętrzny (prawa strona), nie można uzyskać przed klauzuli join, sam.In the following example, the query must join two sequences based on matching keys that, in the case of the inner (right side) sequence, cannot be obtained prior to the join clause itself. Jeśli wykonano to połączenie przy użyciu join klauzuli, a następnie Split metody musi być wywoływana dla każdego elementu.If this join were performed with a join clause, then the Split method would have to be called for each element. Korzystanie z wielu from klauzule Umożliwia zapytanie, aby uniknąć obciążenie związane z wywołania metody powtórzony.The use of multiple from clauses enables the query to avoid the overhead of the repeated method call. Jednak ponieważ join jest zoptymalizowana w tym konkretnym przypadku nadal może być szybsze niż użycie wielu from klauzul.However, since join is optimized, in this particular case it might still be faster than using multiple from clauses. Wyniki będą się różnić w zależności przede wszystkim na sposób kosztownych jest wywołanie metody.The results will vary depending primarily on how expensive the method call is.

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 {student.FirstName} {student.LastName} is 
                {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.
 */

Zobacz takżeSee also