LINQ

Ricon·2025년 1월 27일
0

기초 이론

목록 보기
5/6

LINQ

LINQ는 Collection을 편리하게 다루기 위한 Query 언어이다.
Data Query란 데이터에 대해 물어본다는 말로, 질문은 다음을 기본적으로 포함한다.

  • From : 어떤 데이터 집합에서 찾을 것인가?
  • Where : 어떤 값의 데이터를 찾을 것인가?
  • Select : 어떤 항목을 추출할 것인가?

예시

다음과 같은 Class가 있다고 가정해보자

class Profile
{
	pubilc string Name {get; set;}
    public int Height {get; set;}
}

//Profile 클래스를 기반한 배열
Profile[] arrProfile = {
					   		new Profile(){Name="정우성", Height=186},
					   		new Profile(){Name="김태희", Height=158},
					   		new Profile(){Name="고현정", Height=172},
					   		new Profile(){Name="이문세", Height=178},
					   		new Profile(){Name="하동훈", Height=171},
                       }
                       
// arrProfile에서 Height프로퍼티가 175 미만인 데이터만 골라 새 컬렉션으로 추출하고 싶다면
var profiles = 	from profile in arrProfile // arrProfile 안에 있는 각 데이터로부터
				where profile.Height < 175 // Height가 175미만인 객체만 골라
                orderby profile.Height	   // 키 오름차순으로 정렬하여
                select profile;		       // profile 객체를 추출

LINQ 기본

1. FROM

모든 LINQ 쿼리식은 from으로 시작한다.
쿼리식의 대상이 될 Data Source와 데이터 원본 안에 있는 각 요소 데이터를 나타내는 Range Variable를 from에서 지정해줘야한다.

이때 from의 데이터 원본은 IEnumerable<T.> interface를 상속받아야한다

int[] numbers = {1, 2, 3, 4, 5, 6, 7};

// n : Range Variable 범위 변수
// umbers : Data Source 데이터 원본
var result = from n in numbers
			 where n % 2
             orderby n
             select n;

2. WHERE

where은 필터 역할을 한다.
from에서 만든 범위 변수가 가져야할 조건을 입력하면 해당 조건에 부합하는 데이터만을 가져온다.

//아래와 같은 데이터가 있을때
Profile[] arrProfile = {
					   		new Profile(){Name="정우성", Height=186},
					   		new Profile(){Name="김태희", Height=158},
					   		new Profile(){Name="고현정", Height=172},
					   		new Profile(){Name="이문세", Height=178},
					   		new Profile(){Name="하동훈", Height=171},
                       }
                       
var profiles = 	from profile in arrProfile 
				where profile.Height < 175 // Height가 175미만인 객체만 골라서 추출
                select profile;

3. ORDER BY

orderby는 정렬을 수행한다.
기본적으로 오름차순으로 데이터를 정렬하지만, 가독성을 위해서 ascending 키워드를 명시할 수 있다.

var profiles = 	from profile in arrProfile 
				where profile.Height
                orderby profile.Height ascending // 오름차순
                select profile;
                
var profiles = 	from profile in arrProfile 
				where profile.Height
                orderby profile.Height descending // 내림차순
                select profile;

4. SELECT

select는 최종 결과를 추출한다

var profiles = 	from profile in arrProfile 
				where profile.Height
                orderby profile.Height
                select profile;

LINQ의 질의 결과는 IEnumerable<T.>로 반환하는데, 이때 형식 매개변수 T는 select에 의해 결정된다.

예를 들어 위 쿼리문에서는 profiles는 IEnumerable<Profile.>형식이 된다.
만약 select에서 이름만 가지고 오면 profiles는 IEnumerable<string.>형식으로 컴파일 된다.

select profile.Name;

또한 select는 무명 형식을 이용해서 새로운 형식을 즉석에서 만들어낼 수도 있다.
var profiles = 	from profile in arrProfile 
				where profile.Height
                orderby profile.Height
                select new {Name = profile.Name, InchHeight = profile.Height * 0.393};

예제

using System;
using System.Collections.Generic;
using System.Linq;

namespace SimpleLinq
{
    class Profile
    {
        public string Name { get; set; }
        public int Height { get; set; }
    }

    class MainApp
    {
        static void Main(string[] args)
        {
            Profile[] arrProfile =
            {
                new Profile(){Name = "정우성", Height = 186},
                new Profile(){Name = "김태희", Height = 158},
                new Profile(){Name = "고현정", Height = 172},
                new Profile(){Name = "이문세", Height = 178},
                new Profile(){Name = "하하", Height = 171}
            };

            var profiles = from profile in arrProfile
                           where profile.Height < 175
                           orderby profile.Height
                           select new
                           {
                               Name = profile.Name,
                               InchHeight = profile.Height * 0.393
                           };

            foreach (var item in profiles)
                Console.WriteLine($"{item.Name}, {item.InchHeight}");
        }
    }
}

여러 개의 데이터 원본에 질의하기

1. FROM FROM

여러 개의 Data Source에 접근하려면 from을 중첩하면 된다.

class Class
{
	public string Name {get; set;}
    public int[] Score {get; set;} //배열
}

Class[] arrClass =
{
    new Class(){Name = "연두반", Score = new int[]{99, 88, 70, 24 } },
    new Class(){Name = "분홍반", Score = new int[]{60, 45, 87, 72 } },
    new Class(){Name = "파랑반", Score = new int[]{95, 30, 85, 97 } },
    new Class(){Name = "노랑반", Score = new int[]{90, 88, 0, 17 } },
};

// 위 배열에서 점수가 60점 미만인 학생이 소속된 학급과 그 학생의 점수를 중첩한 from을 이용해서 추출
var classes = from c in arrClass
              from s in c.Score
              where s < 60
              orderby s
              select new { c.Name, Lowest = s };

우선 c를 이용해서 arrClass의 범위 변수를 뽑고, 해당 c.Score로부터 s를 범위변수로 뽑는다.
이때 s는 개별 점수를 나타낸다.
where을 통해서 s가 60보다 낮은지 걸러내고, 그 다음은 무명 형식을 선언해서 낙제점을 맞은 학생의 학급 이름과 점수를 담아낸다.

2. GROUP BY

group by는 분류 기준에 따라 데이터를 그룹화 해준다.
group by는 다음 형식으로 사용한다.

group A by B into C

A는 from에서 뽑아낸 범위 변수를, B는 분류 기준을, C는 그룹 변수를 넣는다.
이전에 사용한 예시를 분류 기준 '175미만인가 175이상인가'로 분류한다면 다음과 같다

Profile[] arrProfile =
            {
                new Profile(){Name = "정우성", Height = 186},
                new Profile(){Name = "김태희", Height = 158},
                new Profile(){Name = "고현정", Height = 172},
                new Profile(){Name = "이문세", Height = 178},
                new Profile(){Name = "하하", Height = 171}
            };
            
var profiles = 	from profile in arrProfile
				group profile by profile.Height < 175 into g
                select new {GroupKey = g.key, Profiles = g};

최족 쿼리식의 결과를 모두 담는 listProfile은 다음과 같다

Name, Height----->Gorup KeyName, Height
정우성, 186(profile.Height < 175) == true김태희, 158
김태희, 158하하, 171
고현정, 172고현정, 172
이문세, 178(profile.Height < 175) == false이문세, 178
하하, 171정우성, 186

3. JOIN

join은 두 Data Source를 연결하는 연산이다.
이때, 각 데이터 원본에서 특정 필드의 값을 비교하여 일치하는 데이터끼리 연결한다.

3-1. Inner Join

내부 조인은 교집합과 비슷하다.
두 데이터 원본 사이에서 일치하는 데이터들만 연결한 후 반환한다.

내부 조인은 첫 번째 데이터 원본과 두 번째 데이터 원본의 특정 필드를 비교해서 일치하는 데이터를 반환한다.
이때 기준은 첫번째 원본 데이터다

[A Data Source] Name, Height[B Data Source] Product, StarA.Name == B.Star 내부 조인
정우성, 186비트, 정우성정우성, 비트, 186
김태희, 158CF 다수, 김태희김태희, CF 다수, 158
고현정, 172아이리스, 김태희김태희, 아이리스, 158
이문세, 178모래시계, 고현정고현정, 모래시계, 172
하하, 171Solo 예찬, 이문세이문세, Solo 예찬, 178

내부 조인을 수행할 때 기준 데이터 원본에는 존재하지만, 연결할 데이터 원본에 존재하지 않는 데이터는 조인 결과에서 제외된다

내부 조인은 다음과 같이 실행된다.

from a in A
join b in B on a.XXX equals b.YYY

기준 데이터 a는 from에서 뽑아낸 범위 변수이고, 연결 대상 데이터 b는 join에서 뽑아낸 변수이다.
join의 on 키워드는 join 조건을 수반한다
이때 on의 조인 조건은 Equality만 허용된다.

예시

var listProfile =
	from profile in arrProfile
    join product in arrProduct on profile.Name equals product.Star
    select new 
    {
    	Name = profile.Name,
        Work = product.Title,
        Height = profile.Height
    };

3-2. Outer Join

외부 조인은 내부 조인과 비슷하지만 조인 결과에 기준이 되는 데이터 원본이 모두 포함된다.

[A Data Source] Name, Height[B Data Source] Product, StarA.Name == B.Star 외부 조인
정우성, 186비트, 정우성정우성, 비트, 186
김태희, 158CF 다수, 김태희김태희, CF 다수, 158
고현정, 172아이리스, 김태희김태희, 아이리스, 158
이문세, 178모래시계, 고현정고현정, 모래시계, 172
하하, 171Solo 예찬, 이문세이문세, Solo 예찬, 178
하하, , 171

연결할 데이터 원본에 기준 데이터 원본의 데이터와 일치하는 데이터가 없다면 그 부분은 빈 값으로 결과를 채우게 된다.

외부 조인은 join 이후에 결과인 임시 컬렉션에 DefaultIfEmpty연산을 통해 비어 있는 결과에 빈 값을 채워 넣는다

예시

var listProfile =
	from profile in arrProfile
    join product in arrProduct on profile.Name equals product.Star into ps
    from product in ps.DefaultIfEmpty(new Product(){Title="그런거 없음"})
    select new 
    {
    	Name = profile.Name,
        Work = product.Title,
        Height = profile.Height
    };

예제

using System;
using System.Linq;

namespace Join
{
    class Profile
    {
        public string Name { get; set; }
        public int Height { get; set; }
    }

    class Product
    {
        public string Title { get; set; }
        public string Star { get; set; }
    }

    class MainApp
    {
        static void Main(string[] args)
        {
            Profile[] arrProfile =
            {
                new Profile(){Name = "정우성", Height = 186},
                new Profile(){Name = "김태희", Height = 158},
                new Profile(){Name = "고현정", Height = 172},
                new Profile(){Name = "이문세", Height = 178},
                new Profile(){Name = "하하", Height = 171}
            };

            Product[] arrProduct =
            {
                new Product(){Title = "비트", Star = "정우성"},
                new Product(){Title = "CF", Star = "김태희"},
                new Product(){Title = "아이리스", Star = "김태희"},
                new Product(){Title = "시계", Star = "고현정"},
                new Product(){Title = "예찬", Star = "이문세"},
            };

            var listProfile =
                from profile in arrProfile
                join product in arrProduct on profile.Name equals product.Star
                select new
                {
                    Name = profile.Name,
                    Work = product.Title,
                    Height = profile.Height
                };

            Console.WriteLine("내부 조인 결과");
            foreach (var item in listProfile)
                Console.WriteLine($"이름 : {item.Name} 작품 : {item.Work}, 키 : {item.Height}");


            listProfile = from profile in arrProfile
                          join product in arrProduct on profile.Name equals product.Star into ps
                          from product in ps.DefaultIfEmpty(new Product() { Title = "그런거 없음" })
                          select new
                          {
                              Name = profile.Name,
                              Work = product.Title,
                              Height = profile.Height
                          };
            Console.WriteLine("외부 조인 결과");
            foreach (var item in listProfile)
                Console.WriteLine($"이름 : {item.Name} 작품 : {item.Work}, 키 : {item.Height}");

        }
    }
}
//Output
내부 조인 결과
이름 : 정우성 작품 : 비트, 키 : 186
이름 : 김태희 작품 : CF, 키 : 158
이름 : 김태희 작품 : 아이리스, 키 : 158
이름 : 고현정 작품 : 시계, 키 : 172
이름 : 이문세 작품 : 예찬, 키 : 178

외부 조인 결과
이름 : 정우성 작품 : 비트, 키 : 186
이름 : 김태희 작품 : CF, 키 : 158
이름 : 김태희 작품 : 아이리스, 키 : 158
이름 : 고현정 작품 : 시계, 키 : 172
이름 : 이문세 작품 : 예찬, 키 : 178
이름 : 하하 작품 : 그런거 없음, 키 : 171

0개의 댓글