Procedura: eseguire degli inner join (Guida per programmatori C#)

In termini di database relazionale, un inner join produce un set di risultati dove ogni elemento del primo insieme viene visualizzato una volta per ogni elemento corrispondente nel secondo insieme. Se un elemento nel primo insieme non dispone di elementi corrispondenti, non viene visualizzato nel set di risultati. Il metodo Join, chiamato dalla clausola join in C#, implementa un inner join.

In questo argomento viene illustrato come eseguire quattro varianti di un inner join:

  • Inner join semplice che correla elementi da due origini dati in base a una chiave semplice.

  • Inner join che correla elementi da due origini dati in base a una chiave composta, ovvero una chiave costituita da più di un valore, che consente di correlare elementi in base a più di una proprietà.

  • Join multiplo nel quale le operazioni di join successive vengono aggiunte l'una all'altra.

  • Inner join implementato utilizzando un group join.

Esempio

Esempio di join di chiave semplice

Nell'esempio seguente vengono creati due insiemi che contengono oggetti di due tipi definiti dall'utente, Person e Pet. La query utilizza la clausola join in C# per associare gli oggetti Person con oggetti Pet per i quali il valore di Owner corrisponde a tale oggetto Person. La clausola select in C# definisce l'aspetto degli oggetti risultanti. In questo esempio gli oggetti risultanti sono tipi anonimi costituiti del nome del proprietario e dal nome dell'animale domestico.

        class Person
        {
            public string FirstName { get; set; }
            public string LastName { get; set; }
        }

        class Pet
        {
            public string Name { get; set; }
            public Person Owner { get; set; }
        }

        /// <summary>
        /// Simple inner join.
        /// </summary>
        public static void InnerJoinExample()
        {
            Person magnus = new Person { FirstName = "Magnus", LastName = "Hedlund" };
            Person terry = new Person { FirstName = "Terry", LastName = "Adams" };
            Person charlotte = new Person { FirstName = "Charlotte", LastName = "Weiss" };
            Person arlene = new Person { FirstName = "Arlene", LastName = "Huff" };
            Person rui = new Person { FirstName = "Rui", LastName = "Raposo" };

            Pet barley = new Pet { Name = "Barley", Owner = terry };
            Pet boots = new Pet { Name = "Boots", Owner = terry };
            Pet whiskers = new Pet { Name = "Whiskers", Owner = charlotte };
            Pet bluemoon = new Pet { Name = "Blue Moon", Owner = rui };
            Pet daisy = new Pet { Name = "Daisy", Owner = magnus };

            // Create two lists.
            List<Person> people = new List<Person> { magnus, terry, charlotte, arlene, rui };
            List<Pet> pets = new List<Pet> { barley, boots, whiskers, bluemoon, daisy };

            // Create a collection of person-pet pairs. Each element in the collection
            // is an anonymous type containing both the person's name and their pet's name.
            var query = from person in people
                        join pet in pets on person equals pet.Owner
                        select new { OwnerName = person.FirstName, PetName = pet.Name };

            foreach (var ownerAndPet in query)
            {
                Console.WriteLine("\"{0}\" is owned by {1}", ownerAndPet.PetName, ownerAndPet.OwnerName);
            }
        }

        // This code produces the following output:
        //
        // "Daisy" is owned by Magnus
        // "Barley" is owned by Terry
        // "Boots" is owned by Terry
        // "Whiskers" is owned by Charlotte
        // "Blue Moon" is owned by Rui

Si noti che l'oggetto Person il cui LastName è "Huff" non viene visualizzato nel set di risultati perché non è disponibile alcun oggetto Pet il cui Pet.Owner sia uguale a tale Person.

Esempio di join di chiave composta

Invece di correlare gli elementi in base a una sola proprietà, è possibile utilizzare una chiave composta per confrontare gli elementi in base a più proprietà. A questo scopo, specificare la funzione del selettore di chiavi in modo che ogni insieme restituisca un tipo anonimo costituito dalle proprietà che si desidera confrontare. Se si assegna un'etichetta alle proprietà, l'etichetta deve essere la medesima nel tipo anonimo di ogni chiave. Le proprietà devono inoltre essere visualizzate nello stesso ordine.

Nell'esempio seguente viene utilizzato un elenco di oggetti Employee e un elenco di oggetti Student per determinare quali dipendenti sono anche studenti. Entrambi questi tipi presentano una proprietà FirstName e LastName di tipo String. Le funzioni create dalle chiavi di join dagli elementi di ogni elenco restituiscono un tipo anonimo costituito dalle proprietà FirstName e LastName di ogni elemento. L'operazione di join verifica l'uguaglianza di queste chiavi composte e restituisce coppie di oggetti da ogni elenco dove sia il nome che il cognome corrispondono.

        class Employee
        {
            public string FirstName { get; set; }
            public string LastName { get; set; }
            public int EmployeeID { get; set; }
        }

        class Student
        {
            public string FirstName { get; set; }
            public string LastName { get; set; }
            public int StudentID { get; set; }
        }

        /// <summary>
        /// Performs a join operation using a composite key.
        /// </summary>
        public static void CompositeKeyJoinExample()
        {
            // Create a list of employees.
            List<Employee> employees = new List<Employee> {
                new Employee { FirstName = "Terry", LastName = "Adams", EmployeeID = 522459 },
                 new Employee { FirstName = "Charlotte", LastName = "Weiss", EmployeeID = 204467 },
                 new Employee { FirstName = "Magnus", LastName = "Hedland", EmployeeID = 866200 },
                 new Employee { FirstName = "Vernette", LastName = "Price", EmployeeID = 437139 } };

            // Create a list of students.
            List<Student> students = new List<Student> {
                new Student { FirstName = "Vernette", LastName = "Price", StudentID = 9562 },
                new Student { FirstName = "Terry", LastName = "Earls", StudentID = 9870 },
                new Student { FirstName = "Terry", LastName = "Adams", StudentID = 9913 } };

            // Join the two data sources based on a composite key consisting of first and last name,
            // to determine which employees are also students.
            IEnumerable<string> query = from employee in employees
                                        join student in students
                                        on new { employee.FirstName, employee.LastName }
                                        equals new { student.FirstName, student.LastName }
                                        select employee.FirstName + " " + employee.LastName;

            Console.WriteLine("The following people are both employees and students:");
            foreach (string name in query)
                Console.WriteLine(name);
        }

        // This code produces the following output:
        //
        // The following people are both employees and students:
        // Terry Adams
        // Vernette Price

Esempio di join multiplo

È possibile aggiungere un numero indeterminato di operazioni di join per eseguire un join multiplo. Ogni clausola join in C# mette in correlazione un'origine dati specificata con i risultati del join precedente.

Nell'esempio seguente vengono creati tre insiemi: un elenco di oggetti Person, un elenco di oggetti Cat e un elenco di oggetti Dog.

La prima clausola join in C# associa le persone e i gatti in base a un oggetto Person corrispondente a Cat.Owner. Restituisce una sequenza di tipi anonimi contenenti l'oggetto Person e Cat.Name.

La seconda clausola join in C# mette in correlazione i tipi anonimi restituiti dal primo join con gli oggetti Dog nell'elenco fornito di cani, in base a una chiave composta costituita dalla proprietà Owner del tipo Persone dalla prima lettera del nome dell'animale. Restituisce una sequenza di tipi anonimi contenenti le proprietà Cat.Name e Dog.Name da ogni coppia corrispondente. Poiché si tratta di un inner join, vengono restituiti solo gli oggetti della prima origine dati che presentano una corrispondenza nella seconda origine dati.

        class Person
        {
            public string FirstName { get; set; }
            public string LastName { get; set; }
        }

        class Pet
        {
            public string Name { get; set; }
            public Person Owner { get; set; }
        }

        class Cat : Pet
        { }

        class Dog : Pet
        { }

        public static void MultipleJoinExample()
        {
            Person magnus = new Person { FirstName = "Magnus", LastName = "Hedlund" };
            Person terry = new Person { FirstName = "Terry", LastName = "Adams" };
            Person charlotte = new Person { FirstName = "Charlotte", LastName = "Weiss" };
            Person arlene = new Person { FirstName = "Arlene", LastName = "Huff" };
            Person rui = new Person { FirstName = "Rui", LastName = "Raposo" };
            Person phyllis = new Person { FirstName = "Phyllis", LastName = "Harris" };

            Cat barley = new Cat { Name = "Barley", Owner = terry };
            Cat boots = new Cat { Name = "Boots", Owner = terry };
            Cat whiskers = new Cat { Name = "Whiskers", Owner = charlotte };
            Cat bluemoon = new Cat { Name = "Blue Moon", Owner = rui };
            Cat daisy = new Cat { Name = "Daisy", Owner = magnus };

            Dog fourwheeldrive = new Dog { Name = "Four Wheel Drive", Owner = phyllis };
            Dog duke = new Dog { Name = "Duke", Owner = magnus };
            Dog denim = new Dog { Name = "Denim", Owner = terry };
            Dog wiley = new Dog { Name = "Wiley", Owner = charlotte };
            Dog snoopy = new Dog { Name = "Snoopy", Owner = rui };
            Dog snickers = new Dog { Name = "Snickers", Owner = arlene };

            // Create three lists.
            List<Person> people =
                new List<Person> { magnus, terry, charlotte, arlene, rui, phyllis };
            List<Cat> cats =
                new List<Cat> { barley, boots, whiskers, bluemoon, daisy };
            List<Dog> dogs =
                new List<Dog> { fourwheeldrive, duke, denim, wiley, snoopy, snickers };

            // The first join matches Person and Cat.Owner from the list of people and
            // cats, based on a common Person. The second join matches dogs whose names start
            // with the same letter as the cats that have the same owner.
            var query = from person in people
                        join cat in cats on person equals cat.Owner
                        join dog in dogs on 
                        new { Owner = person, Letter = cat.Name.Substring(0, 1) }
                        equals new { dog.Owner, Letter = dog.Name.Substring(0, 1) }
                        select new { CatName = cat.Name, DogName = dog.Name };

            foreach (var obj in query)
            {
                Console.WriteLine(
                    "The cat \"{0}\" shares a house, and the first letter of their name, with \"{1}\".", 
                    obj.CatName, obj.DogName);
            }
        }

        // This code produces the following output:
        //
        // The cat "Daisy" shares a house, and the first letter of their name, with "Duke".
        // The cat "Whiskers" shares a house, and the first letter of their name, with "Wiley".

Esempio di inner join utilizzando il join raggruppato

Nell'esempio seguente viene illustrato come implementare un inner join utilizzando un group join.

In query1, l'elenco di oggetti Person è unito tramite group join all'elenco di oggetti Pet in base all'oggetto Person corrispondente alla proprietà Pet.Owner. Il group join crea un insieme di gruppi intermedi, dove ogni gruppo è costituito da un oggetto Person e da una sequenza di oggetti Pet corrispondenti.

Aggiungendo una seconda clausola from alla query, questa sequenza di sequenze viene combinata (o appiattita) in una sequenza più lunga. Il tipo degli elementi della sequenza finale è specificato dalla clausola select. In questo esempio, tale tipo è un tipo anonimo costituito dalle proprietà Person.FirstName e Pet.Name per ogni coppia corrispondente.

Il risultato di query1 è equivalente al set di risultati che sarebbe stato ottenuto utilizzando la clausola join senza la clausola into per eseguire un inner join. La variabile query2 dimostra questa query equivalente.

        class Person
        {
            public string FirstName { get; set; }
            public string LastName { get; set; }
        }

        class Pet
        {
            public string Name { get; set; }
            public Person Owner { get; set; }
        }

        /// <summary>
        /// Performs an inner join by using GroupJoin().
        /// </summary>
        public static void InnerGroupJoinExample()
        {
            Person magnus = new Person { FirstName = "Magnus", LastName = "Hedlund" };
            Person terry = new Person { FirstName = "Terry", LastName = "Adams" };
            Person charlotte = new Person { FirstName = "Charlotte", LastName = "Weiss" };
            Person arlene = new Person { FirstName = "Arlene", LastName = "Huff" };

            Pet barley = new Pet { Name = "Barley", Owner = terry };
            Pet boots = new Pet { Name = "Boots", Owner = terry };
            Pet whiskers = new Pet { Name = "Whiskers", Owner = charlotte };
            Pet bluemoon = new Pet { Name = "Blue Moon", Owner = terry };
            Pet daisy = new Pet { Name = "Daisy", Owner = magnus };

            // Create two lists.
            List<Person> people = new List<Person> { magnus, terry, charlotte, arlene };
            List<Pet> pets = new List<Pet> { barley, boots, whiskers, bluemoon, daisy };

            var query1 = from person in people
                         join pet in pets on person equals pet.Owner into gj
                         from subpet in gj
                         select new { OwnerName = person.FirstName, PetName = subpet.Name };

            Console.WriteLine("Inner join using GroupJoin():");
            foreach (var v in query1)
            {
                Console.WriteLine("{0} - {1}", v.OwnerName, v.PetName);
            }

            var query2 = from person in people
                         join pet in pets on person equals pet.Owner
                         select new { OwnerName = person.FirstName, PetName = pet.Name };

            Console.WriteLine("\nThe equivalent operation using Join():");
            foreach (var v in query2)
                Console.WriteLine("{0} - {1}", v.OwnerName, v.PetName);
        }

        // This code produces the following output:
        //
        // Inner join using GroupJoin():
        // Magnus - Daisy
        // Terry - Barley
        // Terry - Boots
        // Terry - Blue Moon
        // Charlotte - Whiskers
        //
        // The equivalent operation using Join():
        // Magnus - Daisy
        // Terry - Barley
        // Terry - Boots
        // Terry - Blue Moon
        // Charlotte - Whiskers

Compilazione del codice

  • Creare un nuovo progetto Applicazione console in Visual Studio.

  • Aggiungere un riferimento a System.Core.dll, se non vi è già fatto riferimento.

  • Includere lo spazio dei nomi System.Linq.

  • Copiare e incollare il codice dall'esempio nel file program.cs, sotto il metodo Main. Aggiungere una riga di codice al metodo Main per chiamare il metodo in cui è stata eseguita l'operazione Incolla.

  • Eseguire il programma.

Vedere anche

Attività

Procedura: eseguire dei join raggruppati (Guida per programmatori C#)

Procedura: eseguire dei left outer join (Guida per programmatori C#)

Procedura: unire due raccolte tramite join (C#) (LINQ to XML)

Riferimenti

Join

GroupJoin

Tipi anonimi (Guida per programmatori C#)

Concetti

Operazioni di join

Anonymous Types