다음을 통해 공유


쿼리 식 기본 사항

이 문서에서는 C#의 쿼리 식과 관련된 기본 개념을 소개합니다.

쿼리란 무엇이며 어떻게 합니까?

쿼리 지정된 데이터 원본(또는 원본)에서 검색할 데이터와 반환된 데이터가 가져야 하는 셰이프 및 조직을 설명하는 지침 집합입니다. 쿼리는 생성되는 결과와 다릅니다.

일반적으로 원본 데이터는 논리적으로 동일한 종류의 요소 시퀀스로 구성됩니다. 예를 들어 SQL 데이터베이스 테이블에는 행 시퀀스가 포함됩니다. XML 파일에는 XML 요소의 "시퀀스"가 있습니다(XML 요소는 트리 구조에서 계층적으로 구성되지만). 메모리 내 컬렉션에는 개체 시퀀스가 포함됩니다.

애플리케이션의 관점에서 원본 데이터의 특정 형식과 구조는 중요하지 않습니다. 애플리케이션은 항상 원본 데이터를 IEnumerable<T> 또는 IQueryable<T> 컬렉션으로 봅니다. 예를 들어 LINQ to XML에서 원본 데이터는 IEnumerable<XElement>표시됩니다.

이 소스 시퀀스를 고려할 때 쿼리는 다음 세 가지 중 하나를 수행할 수 있습니다.

  • 요소의 하위 집합을 검색하여 개별 요소를 수정하지 않고 새 시퀀스를 생성합니다. 쿼리는 다음 예제와 같이 반환된 시퀀스를 다양한 방식으로 정렬하거나 그룹화할 수 있습니다(scoresint[]가정).

    IEnumerable<int> highScoresQuery =
        from score in scores
        where score > 80
        orderby score descending
        select score;
    
  • 이전 예제와 같이 요소 시퀀스를 검색하지만 새 형식의 개체로 변환합니다. 예를 들어 쿼리는 데이터 원본의 특정 고객 레코드에서 패밀리 이름만 검색할 수 있습니다. 또는 전체 레코드를 검색한 다음 이를 사용하여 최종 결과 시퀀스를 생성하기 전에 다른 메모리 내 개체 형식 또는 XML 데이터를 생성할 수 있습니다. 다음 예제에서는 int에서 string으로의 프로젝션을 보여줍니다. 새로운 유형의 highScoresQuery에 주목하십시오.

    IEnumerable<string> highScoresQuery2 =
        from score in scores
        where score > 80
        orderby score descending
        select $"The score is {score}";
    
  • 다음과 같이 원본 데이터에 대한 싱글톤 값을 검색합니다.

    • 특정 조건과 일치하는 요소의 수입니다.

    • 값이 가장 많거나 가장 낮은 요소입니다.

    • 조건과 일치하는 첫 번째 요소 또는 지정된 요소 집합의 특정 값 합계입니다. 예를 들어 다음 쿼리는 scores 정수 배열에서 80보다 큰 점수 수를 반환합니다.

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

      이전 예제에서는 Enumerable.Count 메서드를 호출하기 전에 쿼리 식 주위에 괄호를 사용합니다. 새 변수를 사용하여 구체적인 결과를 저장할 수도 있습니다.

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

이전 예제에서는 쿼리가 Count호출에서 실행됩니다. CounthighScoresQuery반환된 요소 수를 확인하기 위해 결과를 반복해야 하기 때문입니다.

쿼리 식이란?

쿼리 식 쿼리 구문으로 표현된 쿼리입니다. 쿼리 식은 일류 언어 구문입니다. 이는 다른 식과 같으며 C# 식이 유효한 컨텍스트에서 사용할 수 있습니다. 쿼리 식은 SQL 또는 XQuery와 유사한 선언적 구문으로 작성된 절 집합으로 구성됩니다. 각 절에는 하나 이상의 C# 식이 포함되며, 이러한 식 자체는 쿼리 식이거나 쿼리 식을 포함할 수 있습니다.

쿼리 식은 절의 으로 시작해야 하고, 선택 절 또는 그룹 절로 끝나야 합니다. 첫 번째 절과 마지막 또는 절 사이에는 선택적으로 다음과 같은 절 하나 이상을 포함할 수 있습니다:에서 ,으로 orderby,과 join,으로 let 그리고 또 다른에서 절. 키워드를 사용하여 join 또는 group 절의 결과를 동일한 쿼리 식에서 추가 쿼리 절의 소스로 사용할 수 있습니다.

쿼리 변수

LINQ에서 쿼리 변수는 쿼리의 결과 대신 쿼리 저장하는 모든 변수입니다. 더 구체적으로 말하자면, 쿼리 변수는 항상 foreach 문에서 반복되거나 해당 IEnumerator.MoveNext() 메서드를 직접 호출할 때 요소 시퀀스를 생성하는 열거 가능한 형식입니다.

메모

이 문서의 예제에서는 다음 데이터 원본 및 샘플 데이터를 사용합니다.

record City(string Name, long Population);
record Country(string Name, double Area, long Population, List<City> Cities);
record Product(string Name, string Category);
static readonly City[] cities = [
    new City("Tokyo", 37_833_000),
    new City("Delhi", 30_290_000),
    new City("Shanghai", 27_110_000),
    new City("São Paulo", 22_043_000),
    new City("Mumbai", 20_412_000),
    new City("Beijing", 20_384_000),
    new City("Cairo", 18_772_000),
    new City("Dhaka", 17_598_000),
    new City("Osaka", 19_281_000),
    new City("New York-Newark", 18_604_000),
    new City("Karachi", 16_094_000),
    new City("Chongqing", 15_872_000),
    new City("Istanbul", 15_029_000),
    new City("Buenos Aires", 15_024_000),
    new City("Kolkata", 14_850_000),
    new City("Lagos", 14_368_000),
    new City("Kinshasa", 14_342_000),
    new City("Manila", 13_923_000),
    new City("Rio de Janeiro", 13_374_000),
    new City("Tianjin", 13_215_000)
];

static readonly Country[] countries = [
    new Country ("Vatican City", 0.44, 526, [new City("Vatican City", 826)]),
    new Country ("Monaco", 2.02, 38_000, [new City("Monte Carlo", 38_000)]),
    new Country ("Nauru", 21, 10_900, [new City("Yaren", 1_100)]),
    new Country ("Tuvalu", 26, 11_600, [new City("Funafuti", 6_200)]),
    new Country ("San Marino", 61, 33_900, [new City("San Marino", 4_500)]),
    new Country ("Liechtenstein", 160, 38_000, [new City("Vaduz", 5_200)]),
    new Country ("Marshall Islands", 181, 58_000, [new City("Majuro", 28_000)]),
    new Country ("Saint Kitts & Nevis", 261, 53_000, [new City("Basseterre", 13_000)])
];

다음 코드 예제에서는 하나의 데이터 원본, 하나의 필터링 절, 하나의 순서 지정 절 및 소스 요소의 변환이 없는 간단한 쿼리 식을 보여 줍니다. select 절은 쿼리를 종료합니다.

// 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 (var testScore in scoreQuery)
{
    Console.WriteLine(testScore);
}

// Output: 93 90 82 82

이전 예제에서 scoreQuery쿼리 변수이며, 때때로쿼리라고도 불립니다. 쿼리 변수는 foreach 루프에서 생성되는 실제 결과 데이터를 저장하지 않습니다. 또한 foreach 문이 실행되면 쿼리 결과가 쿼리 변수 scoreQuery통해 반환되지 않습니다. 대신 반복 변수 testScore통해 반환됩니다. 두 번째 foreach 루프에서 scoreQuery 변수를 반복할 수 있습니다. 데이터 원본과 그것이 모두 수정되지 않으면 동일한 결과를 생성합니다.

쿼리 변수는 쿼리 구문 또는 메서드 구문으로 표현되는 쿼리 또는 둘의 조합을 저장할 수 있습니다. 다음 예제에서는 queryMajorCitiesqueryMajorCities2 모두 쿼리 변수입니다.

City[] cities = [
    new City("Tokyo", 37_833_000),
    new City("Delhi", 30_290_000),
    new City("Shanghai", 27_110_000),
    new City("São Paulo", 22_043_000)
];

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

// Execute the query to produce the results
foreach (City city in queryMajorCities)
{
    Console.WriteLine(city);
}

// Output:
// City { Name = Tokyo, Population = 37833000 }
// City { Name = Delhi, Population = 30290000 }

// Method-based syntax
IEnumerable<City> queryMajorCities2 = cities.Where(c => c.Population > 30_000_000);
// Execute the query to produce the results
foreach (City city in queryMajorCities2)
{
    Console.WriteLine(city);
}
// Output:
// City { Name = Tokyo, Population = 37833000 }
// City { Name = Delhi, Population = 30290000 }

반면에 다음 두 예제에서는 각각 쿼리를 사용하여 초기화되더라도 쿼리 변수가 아닌 변수를 보여 줍니다. 결과를 저장하기 때문에 쿼리 변수는 아닙니다.

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

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

var highScore = scoreQuery.Max();
// the following returns the same result
highScore = scores.Max();
var 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;
var largeCitiesList2 = largeCitiesQuery.ToList();

쿼리 변수의 명시적 및 암시적 입력

이 설명서는 일반적으로 쿼리 변수와 select 절간의 형식 관계를 표시하기 위해 쿼리 변수의 명시적 형식을 제공합니다. 그러나 var 키워드를 사용하여 컴파일러가 컴파일 시간에 쿼리 변수(또는 다른 지역 변수)의 형식을 유추하도록 지시할 수도 있습니다. 예를 들어 이 문서의 앞에 표시된 쿼리 예제는 암시적 입력을 사용하여 표현할 수도 있습니다.

var queryCities =
    from city in cities
    where city.Population > 100000
    select city;

앞의 예제에서 var의 사용은 선택 사항입니다. queryCities 암시적 또는 명시적으로 형식화되었는지 여부에 관계없이 IEnumerable<City>.

쿼리 식을 시작하기

쿼리 식은 from 절로 시작해야 합니다. 범위 변수와 함께 데이터 원본을 지정합니다. 범위 변수는 원본 시퀀스가 트래버스될 때 소스 시퀀스의 각 연속 요소를 나타냅니다. 범위 변수는 데이터 원본의 요소 형식에 따라 강력하게 형식화됩니다. 다음 예제에서는 countriesCountry 개체의 배열이므로 범위 변수도 Country형식으로 지정됩니다. 범위 변수는 강력한 형식이므로 점 연산자를 사용하여 형식의 사용 가능한 멤버에 액세스할 수 있습니다.

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

쿼리가 세미콜론이나 연속 절로 종료될 때까지 범위 변수는 유효합니다.

쿼리 식에는 여러 from 절이 포함될 수 있습니다. 소스 시퀀스의 각 요소가 컬렉션이거나 컬렉션이 포함된 경우 더 많은 from 절을 사용합니다. 예를 들어, Country 개체의 컬렉션이 있다고 가정합시다. 이 개체 각각은 Cities라는 이름의 City 개체 컬렉션을 포함하고 있습니다. 각 CountryCity 개체를 쿼리하려면, 여기에 표시된 대로 두 개의 from 절을 사용하십시오.

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

자세한 내용은절의 을(를) 참조하세요.

쿼리 식 종료

쿼리 식은 group 절 또는 select 절로 끝나야 합니다.

그룹 조항

group 절을 사용하여 지정한 키로 구성된 그룹 시퀀스를 생성합니다. 키는 모든 데이터 형식일 수 있습니다. 예를 들어 다음 쿼리는 하나 이상의 Country 개체를 포함하고 키가 국가 이름의 첫 글자인 값이 있는 char 형식인 그룹 시퀀스를 만듭니다.

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

그룹화에 대한 자세한 내용은 그룹 절참조하세요.

select 절

select 절을 사용하여 다른 모든 유형의 시퀀스를 생성합니다. 간단한 select 절은 데이터 원본에 포함된 개체와 동일한 유형의 개체 시퀀스를 생성합니다. 이 예제에서 데이터 원본에는 Country 개체가 포함됩니다. orderby 절은 요소를 새 순서로 정렬하고 select 절은 순서가 다시 지정된 Country 개체의 시퀀스를 생성합니다.

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

select 절을 사용하여 원본 데이터를 새 형식의 시퀀스로 변환할 수 있습니다. 또한 이 변환의 이름은 프로젝션. 다음 예제에서 select 절은 원래 요소의 필드 하위 집합만 포함하는 무명 형식의 시퀀스를 투영합니다. 새 개체는 개체 이니셜라이저를 사용하여 초기화됩니다.

var queryNameAndPop =
    from country in countries
    select new
    {
        Name = country.Name,
        Pop = country.Population
    };

따라서 이 예제에서는 쿼리가 익명 형식을 생성하기 때문에 var 필요합니다.

절을 사용하여 원본 데이터를 변환하는 모든 방법에 대한 자세한 내용은select 절 참조하세요.

에서로의 연속성

select 또는 group 절에서 into 키워드를 사용하여 쿼리를 저장하는 임시 식별자를 만들 수 있습니다. 그룹화 또는 선택 작업 후에 쿼리에 대해 추가 쿼리 작업을 수행해야 하는 경우 into 절을 사용합니다. 다음 예제에서 countries은 인구 1,000만 명 단위로 그룹화됩니다. 이러한 그룹을 만든 후, 일부 그룹을 필터링하는 더 많은 절을 사용하고, 그런 다음 그룹을 오름차순으로 정렬합니다. 이러한 추가 작업을 수행하려면 연속성을 나타내는 countryGroup이 필요합니다.

// percentileQuery is an IEnumerable<IGrouping<int, Country>>
var percentileQuery =
    from country in countries
    let percentile = (int)country.Population / 1_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);
    }
}

자세한 내용은 에서로 참조하세요.

필터링, 순서 지정 및 조인

시작 from 절과 끝 select 또는 group 절 사이에 다른 모든 절(where, join, orderby, from, let)은 선택 사항입니다. 선택적 절은 쿼리 본문에서 0번 또는 여러 번 사용될 수 있습니다.

where 절

where 절을 사용하여 하나 이상의 조건 식을 기반으로 원본 데이터에서 요소를 필터링합니다. 다음 예제의 where 절에는 두 가지 조건이 있는 하나의 조건자가 있습니다.

IEnumerable<City> queryCityPop =
    from city in cities
    where city.Population is < 15_000_000 and > 10_000_000
    select city;

자세한 내용은 에서 절을 참조하세요.

orderby 절

orderby 절을 사용하여 결과를 오름차순 또는 내림차순으로 정렬합니다. 보조 정렬 순서를 지정할 수도 있습니다. 다음 예제에서는 Area 속성을 사용 하 여 country 개체에 기본 정렬을 수행 합니다. 그런 다음 Population 속성을 사용하여 보조 정렬을 수행합니다.

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

ascending 키워드는 선택 사항입니다. 지정된 순서가 없는 경우 기본 정렬 순서입니다. orderby 절에 대한 자세한 내용은 참조하세요.

join 절

join 절을 사용하여 각 요소의 지정된 키 간의 같음 비교를 기반으로 한 데이터 원본의 요소를 다른 데이터 원본의 요소와 연결 및/또는 결합합니다. LINQ에서 조인 작업은 요소가 다른 형식인 개체 시퀀스에 대해 수행됩니다. 두 시퀀스를 조인한 후에는 select 또는 group 문을 사용하여 출력 시퀀스에 저장할 요소를 지정해야 합니다. 익명 형식을 사용하여 연결된 각 요소 집합의 속성을 출력 시퀀스의 새 형식으로 결합할 수도 있습니다. 다음 예제에서는 Category 속성이 categories 문자열 배열의 범주 중 하나와 일치하는 prod 개체를 연결합니다. Category categories 문자열과 일치하지 않는 제품은 제거됩니다. select 문은 속성이 catprod에서 가져온 새 형식을 생성합니다.

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

into 키워드를 사용하여 join 작업의 결과를 임시 변수에 저장함으로써 그룹 조인을 수행할 수도 있습니다. 자세한 내용은 조인 절참조하십시오.

let 절

let 절을 사용하여 메서드 호출과 같은 식의 결과를 새 범위 변수에 저장합니다. 다음 예제에서 Split에 의해 반환된 문자열 배열의 첫 번째 요소를 범위 변수 firstName가 저장합니다.

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 (var s in queryFirstNames)
{
    Console.Write(s + " ");
}

//Output: Svetlana Claire Sven Cesar

자세한 내용은 let 절참조하세요.

쿼리 식의 하위 쿼리

쿼리 절 자체에는 하위 쿼리라고도 하는 쿼리 식이 포함될 수 있습니다. 각각의 하위 쿼리는 첫 번째 from 절에서 반드시 동일한 데이터 원본을 가리키지 않아도 되는 고유한 from 절로 시작합니다. 예를 들어 다음 쿼리는 select 문에서 그룹화 작업의 결과를 검색하는 데 사용되는 쿼리 식을 보여줍니다.

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

자세한 내용은 그룹화 작업하위 쿼리 수행을 참조하세요.

참고하세요