Conceptos básicos de las expresiones de consultasQuery expression basics

En este artículo se presentan los conceptos básicos relacionados con las expresiones de consulta en C#.This article introduces the basic concepts related to query expressions in C#.

¿Qué es una consulta y qué hace?What is a query and what does it do?

Una consulta es un conjunto de instrucciones que describen qué datos se recuperan de uno o varios orígenes de datos determinados y qué forma y qué organización deben tener los datos devueltos.A query is a set of instructions that describes what data to retrieve from a given data source (or sources) and what shape and organization the returned data should have. Una consulta es distinta de los resultados que genera.A query is distinct from the results that it produces.

Por lo general, los datos de origen se organizan lógicamente como una secuencia de elementos del mismo tipo.Generally, the source data is organized logically as a sequence of elements of the same kind. Por ejemplo, una tabla de base de datos SQL contiene una secuencia de filas.For example, a SQL database table contains a sequence of rows. En un archivo XML, hay una "secuencia" de elementos XML (aunque estos se organizan jerárquicamente en una estructura de árbol).In an XML file, there is a "sequence" of XML elements (although these are organized hierarchically in a tree structure). Una colección en memoria contiene una secuencia de objetos.An in-memory collection contains a sequence of objects.

Desde el punto de vista de la aplicación, el tipo y la estructura específicos de los datos de origen originales no es importante.From an application's viewpoint, the specific type and structure of the original source data is not important. La aplicación siempre ve los datos de origen como una colección IEnumerable<T> o IQueryable<T>.The application always sees the source data as an IEnumerable<T> or IQueryable<T> collection. Por ejemplo, en LINQ to XML, los datos de origen se hacen visibles como IEnumerable<XElement>.For example, in LINQ to XML, the source data is made visible as an IEnumerable<XElement>.

Dada esta secuencia de origen, una consulta puede hacer una de estas tres cosas:Given this source sequence, a query may do one of three things:

  • Recuperar un subconjunto de los elementos para generar una nueva secuencia sin modificar los elementos individuales.Retrieve a subset of the elements to produce a new sequence without modifying the individual elements. Después, la consulta puede ordenar o agrupar la secuencia devuelta de varias maneras, como se muestra en el ejemplo siguiente (supongamos que scores es int[]):The query may then sort or group the returned sequence in various ways, as shown in the following example (assume scores is an int[]):

    IEnumerable<int> highScoresQuery =
        from score in scores
        where score > 80
        orderby score descending
        select score;
    
  • Recuperar una secuencia de elementos como en el ejemplo anterior, pero transformándolos en un nuevo tipo de objeto.Retrieve a sequence of elements as in the previous example but transform them to a new type of object. Por ejemplo, una consulta puede recuperar solo los apellidos de ciertos registros de clientes de un origen de datos.For example, a query may retrieve only the last names from certain customer records in a data source. También puede recuperar el registro completo y, luego, usarlo para construir otro tipo de objeto en memoria, o incluso datos XML, antes de generar la secuencia de resultado final.Or it may retrieve the complete record and then use it to construct another in-memory object type or even XML data before generating the final result sequence. En el ejemplo siguiente muestra una proyección de int a string.The following example shows a projection from an int to a string. Observe el nuevo tipo de highScoresQuery.Note the new type of highScoresQuery.

    IEnumerable<string> highScoresQuery2 =
        from score in scores
        where score > 80
        orderby score descending
        select $"The score is {score}";
    
  • Recuperar un valor singleton sobre los datos de origen, por ejemplo:Retrieve a singleton value about the source data, such as:

    • El número de elementos que coinciden con una condición determinada.The number of elements that match a certain condition.

    • El elemento que tiene el mayor o el menor valor.The element that has the greatest or least value.

    • El primer elemento que coincide con una condición, o bien la suma de determinados valores de un conjunto de elementos especificado.The first element that matches a condition, or the sum of particular values in a specified set of elements. Por ejemplo, la consulta siguiente devuelve el número de resultados mayor que 80 de la matriz de enteros scores:For example, the following query returns the number of scores greater than 80 from the scores integer array:

      int highScoreCount =
          (from score in scores
           where score > 80
           select score)
           .Count();
      

      En el ejemplo anterior, observe el uso de los paréntesis alrededor de la expresión de consulta antes de llamar al método Count.In the previous example, note the use of parentheses around the query expression before the call to the Count method. Esto también se puede expresar mediante una nueva variable para almacenar el resultado concreto.You can also express this by using a new variable to store the concrete result. Esta técnica es más legible porque hace que la variable que almacena la consulta se mantenga separada de la consulta que almacena un resultado.This technique is more readable because it keeps the variable that stores the query separate from the query that stores a result.

      IEnumerable<int> highScoresQuery3 =
          from score in scores
          where score > 80
          select score;
      
      int scoreCount = highScoresQuery3.Count();
      

En el ejemplo anterior, la consulta se ejecuta en la llamada a Count, ya que Count debe iterar los resultados para determinar el número de elementos devueltos por highScoresQuery.In the previous example, the query is executed in the call to Count, because Count must iterate over the results in order to determine the number of elements returned by highScoresQuery.

¿Qué es una expresión de consulta?What is a query expression?

Una expresión de consulta es una consulta que se expresa en sintaxis de consulta.A query expression is a query expressed in query syntax. Una expresión de consulta es una construcción de lenguaje de primera clase.A query expression is a first-class language construct. Es igual que cualquier otra expresión y puede usarse en cualquier contexto en el que una expresión de C# sea válida.It is just like any other expression and can be used in any context in which a C# expression is valid. Una expresión de consulta consta de un conjunto de cláusulas escritas en una sintaxis declarativa similar a SQL o XQuery.A query expression consists of a set of clauses written in a declarative syntax similar to SQL or XQuery. Cada cláusula contiene una o más expresiones de C#, y estas expresiones pueden ser una expresión de consulta en sí mismas o bien contener una expresión de consulta.Each clause in turn contains one or more C# expressions, and these expressions may themselves be either a query expression or contain a query expression.

Una expresión de consulta debe comenzar con una cláusula from y debe terminar con una cláusula select o group.A query expression must begin with a from clause and must end with a select or group clause. Entre la primera cláusula from y la última cláusula select o group, puede contener una o varias de estas cláusulas opcionales: where, orderby, join, let e incluso cláusulas from adicionales.Between the first from clause and the last select or group clause, it can contain one or more of these optional clauses: where, orderby, join, let and even additional from clauses. También puede usar la palabra clave into para que el resultado de una cláusula join o group actúe como el origen de las cláusulas de consulta adicionales en la misma expresión de consulta.You can also use the into keyword to enable the result of a join or group clause to serve as the source for additional query clauses in the same query expression.

Variable de consultaQuery variable

En LINQ, una variable de consulta es cualquier variable que almacene una consulta en lugar de los resultados de una consulta.In LINQ, a query variable is any variable that stores a query instead of the results of a query. Más concretamente, una variable de consulta es siempre un tipo enumerable que generará una secuencia de elementos cuando se itere en una instrucción foreach o en una llamada directa a su método IEnumerator.MoveNext.More specifically, a query variable is always an enumerable type that will produce a sequence of elements when it is iterated over in a foreach statement or a direct call to its IEnumerator.MoveNext method.

En el ejemplo de código siguiente se muestra una expresión de consulta simple con un origen de datos, una cláusula de filtrado, una cláusula de clasificación y ninguna transformación en los elementos de origen.The following code example shows a simple query expression with one data source, one filtering clause, one ordering clause, and no transformation of the source elements. La cláusula select finaliza la consulta.The select clause ends the query.

static void Main()
{
    // Data source.
    int[] scores = { 90, 71, 82, 93, 75, 82 };

    // Query Expression.
    IEnumerable<int> scoreQuery = //query variable
        from score in scores //required
        where score > 80 // optional
        orderby score descending // optional
        select score; //must end with select or group

    // Execute the query to produce the results
    foreach (int testScore in scoreQuery)
    {
        Console.WriteLine(testScore);
    }                  
}
// Outputs: 93 90 82 82      

En el ejemplo anterior, scoreQuery es una variable de consulta, que a veces se conoce simplemente como una consulta.In the previous example, scoreQuery is a query variable, which is sometimes referred to as just a query. La variable de consulta no almacena datos de resultado reales, que se producen en el bucle foreach.The query variable stores no actual result data, which is produced in the foreach loop. Cuando se ejecuta la instrucción foreach, los resultados de la consulta no se devuelven a través de la variable de consulta scoreQuery,And when the foreach statement executes, the query results are not returned through the query variable scoreQuery. sino a través de la variable de iteración testScore.Rather, they are returned through the iteration variable testScore. La variable scoreQuery se puede iterar en un segundo bucle foreach.The scoreQuery variable can be iterated in a second foreach loop. Siempre y cuando ni esta ni el origen de datos se hayan modificado, producirá los mismos resultados.It will produce the same results as long as neither it nor the data source has been modified.

Una variable de consulta puede almacenar una consulta expresada en sintaxis de consulta, en sintaxis de método o en una combinación de ambas.A query variable may store a query that is expressed in query syntax or method syntax, or a combination of the two. En los ejemplos siguientes, queryMajorCities y queryMajorCities2 son variables de consulta:In the following examples, both queryMajorCities and queryMajorCities2 are query variables:

//Query syntax
IEnumerable<City> queryMajorCities =
    from city in cities
    where city.Population > 100000
    select city;


// Method-based syntax
IEnumerable<City> queryMajorCities2 = cities.Where(c => c.Population > 100000);

Por otro lado, en los dos ejemplos siguientes se muestran variables que no son de consulta, a pesar de que se inicialicen con una consulta.On the other hand, the following two examples show variables that are not query variables even though each is initialized with a query. No son variables de consulta porque almacenan resultados:They are not query variables because they store results:

int highestScore =
    (from score in scores
     select score)
    .Max();

// or split the expression
IEnumerable<int> scoreQuery =
    from score in scores
    select score;

int highScore = scoreQuery.Max();
// the following returns the same result
int highScore = scores.Max();

List<City> largeCitiesList =
    (from country in countries
     from city in country.Cities
     where city.Population > 10000
     select city)
       .ToList();

// or split the expression
IEnumerable<City> largeCitiesQuery =
    from country in countries
    from city in country.Cities
    where city.Population > 10000
    select city;

List<City> largeCitiesList2 = largeCitiesQuery.ToList();

Para obtener más información sobre las distintas formas de expresar consultas, vea Query syntax and method syntax in LINQ (Sintaxis de consulta y sintaxis de método en LINQ).For more information about the different ways to express queries, see Query syntax and method syntax in LINQ.

Asignación implícita y explícita de tipos de variables de consultaExplicit and implicit typing of query variables

En esta documentación se suele proporcionar el tipo explícito de la variable de consulta para mostrar las relaciones de tipo entre la variable de consulta y la cláusula select.This documentation usually provides the explicit type of the query variable in order to show the type relationship between the query variable and the select clause. Pero también se puede usar la palabra clave var para indicarle al compilador que infiera el tipo de una variable de consulta (u otra variable local) en tiempo de compilación.However, you can also use the var keyword to instruct the compiler to infer the type of a query variable (or any other local variable) at compile time. Por ejemplo, la consulta de ejemplo que se mostró anteriormente en este tema también se puede expresar mediante la asignación implícita de tipos:For example, the query example that was shown previously in this topic can also be expressed by using implicit typing:

// Use of var is optional here and in all queries.
// queryCities is an IEnumerable<City> just as 
// when it is explicitly typed.
var queryCities =
    from city in cities
    where city.Population > 100000
    select city;

Para obtener más información, vea Implicitly typed local variables (Variables locales con asignación implícita de tipos) y Type relationships in LINQ query operations (Relaciones entre tipos en las operaciones de consulta de LINQ).For more information, see Implicitly typed local variables and Type relationships in LINQ query operations.

Iniciar una expresión de consultaStarting a query expression

Una expresión de consulta debe comenzar con una cláusula from,A query expression must begin with a from clause. que especifica un origen de datos junto con una variable de rango.It specifies a data source together with a range variable. La variable de rango representa cada elemento sucesivo de la secuencia de origen a medida que esta se recorre.The range variable represents each successive element in the source sequence as the source sequence is being traversed. La variable de rango está fuertemente tipada en función del tipo de elementos del origen de datos.The range variable is strongly typed based on the type of elements in the data source. En el ejemplo siguiente, como countries es una matriz de objetos Country, la variable de rango también está tipada como Country.In the following example, because countries is an array of Country objects, the range variable is also typed as Country. Dado que la variable de rango está fuertemente tipada, se puede usar el operador punto para tener acceso a cualquier miembro disponible del tipo.Because the range variable is strongly typed, you can use the dot operator to access any available members of the type.

IEnumerable<Country> countryAreaQuery =
    from country in countries
    where country.Area > 500000 //sq km
    select country;

La variable de rango está en el ámbito hasta que se cierra la consulta con un punto y coma o con una cláusula de continuación.The range variable is in scope until the query is exited either with a semicolon or with a continuation clause.

Una expresión de consulta puede contener varias cláusulas from.A query expression may contain multiple from clauses. Use más cláusulas from cuando cada elemento de la secuencia de origen sea una colección en sí mismo o contenga una colección.Use additional from clauses when each element in the source sequence is itself a collection or contains a collection. Por ejemplo, supongamos que tiene una colección de objetos Country, cada uno de los cuales contiene una colección de objetos City denominados Cities.For example, assume that you have a collection of Country objects, each of which contains a collection of City objects named Cities. Para consultar los objetos City de cada Country, use dos cláusulas from, como se muestra aquí:To query the City objects in each Country, use two from clauses as shown here:

IEnumerable<City> cityQuery =
    from country in countries
    from city in country.Cities
    where city.Population > 10000
    select city;

Para obtener más información, vea from clause (Cláusula from).For more information, see from clause.

Finalizar una expresión de consultaEnding a query expression

Una expresión de consulta debe finalizar con una cláusula group o select.A query expression must end with either a group clause or a select clause.

group (cláusula)group clause

Use la cláusula group para generar una secuencia de grupos organizados por la clave que especifique.Use the group clause to produce a sequence of groups organized by a key that you specify. La clave puede ser cualquier tipo de datos.The key can be any data type. Por ejemplo, la siguiente consulta crea una secuencia de grupos que contienen uno o más objetos Country y cuya clave es un valor char.For example, the following query creates a sequence of groups that contains one or more Country objects and whose key is a char value.

var queryCountryGroups =
    from country in countries
    group country by country.Name[0];

Para obtener más información sobre la agrupación, vea group clause (Cláusula group).For more information about grouping, see group clause.

select (cláusula)select clause

Use la cláusula select para generar todos los demás tipos de secuencias.Use the select clause to produce all other types of sequences. Una cláusula select simple solo genera una secuencia del mismo tipo de objetos que los objetos contenidos en el origen de datos.A simple select clause just produces a sequence of the same type of objects as the objects that are contained in the data source. En este ejemplo, el origen de datos contiene objetos Country.In this example, the data source contains Country objects. La cláusula orderby simplemente ordena los elementos con un orden nuevo y la cláusula select genera una secuencia con los objetos Country reordenados.The orderby clause just sorts the elements into a new order and the select clause produces a sequence of the reordered Country objects.

IEnumerable<Country> sortedQuery =
    from country in countries
    orderby country.Area
    select country;

La cláusula select puede usarse para transformar los datos de origen en secuencias de nuevos tipos.The select clause can be used to transform source data into sequences of new types. Esta transformación también se denomina proyección.This transformation is also named a projection. En el ejemplo siguiente, la cláusula select proyecta una secuencia de tipos anónimos que solo contiene un subconjunto de los campos del elemento original.In the following example, the select clause projects a sequence of anonymous types which contains only a subset of the fields in the original element. Tenga en cuenta que los nuevos objetos se inicializan mediante un inicializador de objeto.Note that the new objects are initialized by using an object initializer.

// Here var is required because the query
// produces an anonymous type.
var queryNameAndPop =
    from country in countries
    select new { Name = country.Name, Pop = country.Population };

Para obtener más información sobre todas las formas en que se puede usar una cláusula select para transformar datos de origen, vea select clause (Cláusula select).For more information about all the ways that a select clause can be used to transform source data, see select clause.

Continuaciones con "into"Continuations with "into"

Puede usar la palabra clave into en una cláusula select o group para crear un identificador temporal que almacene una consulta.You can use the into keyword in a select or group clause to create a temporary identifier that stores a query. Hágalo cuando deba realizar operaciones de consulta adicionales en una consulta después de una operación de agrupación o selección.Do this when you must perform additional query operations on a query after a grouping or select operation. En el siguiente ejemplo se agrupan los objetos countries según su población en intervalos de 10 millones.In the following example countries are grouped according to population in ranges of 10 million. Una vez que se han creado estos grupos, las cláusulas adicionales filtran algunos grupos y, después, ordenan los grupos en orden ascendente.After these groups are created, additional clauses filter out some groups, and then to sort the groups in ascending order. Para realizar esas operaciones adicionales, es necesaria la continuación representada por countryGroup.To perform those additional operations, the continuation represented by countryGroup is required.

// percentileQuery is an IEnumerable<IGrouping<int, Country>>
var percentileQuery =
    from country in countries
    let percentile = (int) country.Population / 10_000_000
    group country by percentile into countryGroup
    where countryGroup.Key >= 20
    orderby countryGroup.Key
    select countryGroup;

// grouping is an IGrouping<int, Country>
foreach (var grouping in percentileQuery)
{
    Console.WriteLine(grouping.Key);
    foreach (var country in grouping)
        Console.WriteLine(country.Name + ":" + country.Population);
}

Para obtener más información, vea into.For more information, see into.

Filtrar, ordenar y combinarFiltering, ordering, and joining

Entre la cláusula de inicio from y la cláusula de finalización select o group, todas las demás cláusulas (where, join, orderby, from, let) son opcionales.Between the starting from clause, and the ending select or group clause, all other clauses (where, join, orderby, from, let) are optional. Cualquiera de las cláusulas opcionales puede usarse cero o varias veces en el cuerpo de una consulta.Any of the optional clauses may be used zero times or multiple times in a query body.

where (cláusula)where clause

Use la cláusula where para filtrar los elementos de los datos de origen en función de una o varias expresiones de predicado.Use the where clause to filter out elements from the source data based on one or more predicate expressions. La cláusula where del ejemplo siguiente tiene un predicado con dos condiciones.The where clause in the following example has one predicate with two conditions.

IEnumerable<City> queryCityPop =
    from city in cities
    where city.Population < 200000 && city.Population > 100000
    select city;

Para obtener más información, vea where (Cláusula).For more information, see where clause.

orderby (cláusula)orderby clause

Use la cláusula orderby para ordenar los resultados en orden ascendente o descendente.Use the orderby clause to sort the results in either ascending or descending order. También puede especificar criterios de ordenación secundaria.You can also specify secondary sort orders. En el ejemplo siguiente se realiza una ordenación primaria de los objetos country mediante la propiedad Area.The following example performs a primary sort on the country objects by using the Area property. Después, se realiza una ordenación secundaria mediante la propiedad Population.It then performs a secondary sort by using the Population property.

IEnumerable<Country> querySortedCountries =
    from country in countries
    orderby country.Area, country.Population descending
    select country;

La palabra clave ascending es opcional; es el criterio de ordenación predeterminado si no se especifica ningún orden.The ascending keyword is optional; it is the default sort order if no order is specified. Para obtener más información, vea orderby (Cláusula).For more information, see orderby clause.

join (cláusula)join clause

Use la cláusula join para asociar o combinar elementos de un origen de datos con elementos de otro origen de datos en función de una comparación de igualdad entre las claves especificadas en cada elemento.Use the join clause to associate and/or combine elements from one data source with elements from another data source based on an equality comparison between specified keys in each element. En LINQ, las operaciones de combinación se realizan en secuencias de objetos cuyos elementos son de tipos diferentes.In LINQ, join operations are performed on sequences of objects whose elements are different types. Después de combinar dos secuencias, debe usar una instrucción select o group para especificar qué elemento se va a almacenar en la secuencia de salida.After you have joined two sequences, you must use a select or group statement to specify which element to store in the output sequence. También puede usar un tipo anónimo para combinar propiedades de cada conjunto de elementos asociados en un nuevo tipo para la secuencia de salida.You can also use an anonymous type to combine properties from each set of associated elements into a new type for the output sequence. En el ejemplo siguiente se asocian objetos prod cuya propiedad Category coincide con una de las categorías de la matriz de cadenas categories.The following example associates prod objects whose Category property matches one of the categories in the categories string array. Los productos cuya propiedad Category no coincide con ninguna cadena de categories se filtran. La instrucción select proyecta un nuevo tipo cuyas propiedades se toman de cat y prod.Products whose Category does not match any string in categories are filtered out. The select statement projects a new type whose properties are taken from both cat and prod.

var categoryQuery =
    from cat in categories
    join prod in products on cat equals prod.Category
    select new { Category = cat, Name = prod.Name };

También puede realizar una combinación agrupada. Para ello, almacene los resultados de la operación join en una variable temporal mediante el uso de la palabra clave into.You can also perform a group join by storing the results of the join operation into a temporary variable by using the into keyword. Para obtener más información, vea join (Cláusula, Referencia de C#).For more information, see join clause.

let (cláusula)let clause

Use la cláusula let para almacenar el resultado de una expresión, como una llamada de método, en una nueva variable de rango.Use the let clause to store the result of an expression, such as a method call, in a new range variable. En el ejemplo siguiente, la variable de rango firstName almacena el primer elemento de la matriz de cadenas devuelta por Split.In the following example, the range variable firstName stores the first element of the array of strings that is returned by Split.

string[] names = { "Svetlana Omelchenko", "Claire O'Donnell", "Sven Mortensen", "Cesar Garcia" };
IEnumerable<string> queryFirstNames =
    from name in names
    let firstName = name.Split(' ')[0]
    select firstName;

foreach (string s in queryFirstNames)
    Console.Write(s + " ");
//Output: Svetlana Claire Sven Cesar

Para obtener más información, vea let (Cláusula).For more information, see let clause.

Subconsultas en una expresión de consultaSubqueries in a query expression

Una cláusula de consulta puede contener una expresión de consulta, en ocasiones denominada subconsulta.A query clause may itself contain a query expression, which is sometimes referred to as a subquery. Cada subconsulta comienza con su propia cláusula from que no necesariamente hace referencia al mismo origen de datos de la primera cláusula from.Each subquery starts with its own from clause that does not necessarily point to the same data source in the first from clause. Por ejemplo, la consulta siguiente muestra una expresión de consulta que se usa en la instrucción select para recuperar los resultados de una operación de agrupación.For example, the following query shows a query expression that is used in the select statement to retrieve the results of a grouping operation.

var queryGroupMax =
    from student in students
    group student by student.GradeLevel into studentGroup
    select new
    {
        Level = studentGroup.Key,
        HighestScore =
            (from student2 in studentGroup
             select student2.Scores.Average())
             .Max()
    };

Para obtener más información, vea How to: perform a subquery on a grouping operation (Cómo: Realizar una subconsulta en una operación de agrupación).For more information, see How to: perform a subquery on a grouping operation.

Vea tambiénSee also