[C# 기초] 11. LINQ(Language INtergrated Query)


데이터베이스를 다루는 SQL 언어를 C#의 메소드 형식으로 쿼리 결과를 받을 수 있도록 지원합니다.

LINQ (Language INtergrated Query)


C# 언어에 통합된 데이터 질의기능이 있습니다. 데이터베이스에서 쿼리를 보내는 것과 유사합니다. 53개의 표준 LINQ 연산 메소드 중에서 쿼리식을 지원하는 것은 11가지 입니다. 쿼리식은 표준 LINQ 연산 메소드를 호출하는 원리로 이루어져 있기때문에, 쿼리식이 부족하다 싶을때는 함수호출을 이용하는 것도 하나의 방법입니다.

사실 SQL 언어에 익숙하신 분들이면 포스팅을 안보셔도 LINQ를 쉽게 활용하실 수 있습니다. 그렇지 않은 분들을 위해서 기본적인 내용만 정리하겠습니다.

몇 가지 쿼리만 살펴보겠습니다. 자세한 내용은 MSDN을 참고하세요

  • from : 쿼리식의 대상이 될 데이터 원본과 안에 들어있는 각 요소 데이터를 나타내는 범위 변수를 지정해주어야 합니다. from의 데이터 원본은 IEnumerable 인터페이스를 상속하는 형식이어야 합니다.
  • where : 해당 조건에 부합하는 데이터만을 걸러낼때 사용하는 연산자 입니다.
  • orderby : 데이터의 정렬을 수행하는 연산자입니다.
  • select : 최종 결과를 추출합니다.

LINQ 쿼리식 예시:

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

LINQ 메소드 호출 방식 예시:

var profileList = Profiles.
	Where ( profile=> profile.Height < 175).
	Orderby ( profile => profile.Height).
	Select ( profile =>
	new
	{
        Name = Profile.Name,
        InchHeight = profile.Height*0.393
	});

위와 같이 2가지 방식은 사실상 같습니다. 내부적으로 LINQ 표준 메소드를 호출합니다.

예제 코드:

using System.Linq;

class Program
{
    class Profile
    {
        public string name { get; set; }
        public int height { get; set; }
    }

    static void Main(string[] args)
    {
        Profile[] profiles = new Profile[5]
        {
            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 pro = from profile in profiles  // profiles 원본에서 각 요소 profile 객체에서
                    where profile.height < 175 // profile.height 값이 175 이하인 데이터를
                    orderby profile.height descending // descending 으로 정렬한다.
                    select profile;            // 정렬 결과의 profile 객체의 list를 추출한다.

        foreach(var profile in pro)
        {
            Console.WriteLine("{0} , {1}", profile.name, profile.height);
        } 
    }
}

프로그램을 만들다보면 데이터를 자주 다루게 될텐데 SQL 형식으로 이용할 수 있어서 편리합니다.

group by, 데이터 분류하기


다음 쿼리식을 통해 데이터를 분류할 수 있습니다.

사용 방법:

group A by B into C

예제 코드:

class Program
{
    class Profile
    {
        public string Name { get; set; }
        public int[] Score { get; set; }
    }

    static void Main(string[] args)
    {
        Profile[] profiles = new Profile[5]
        {
            new Profile(){ Name="김철수", Score=new int[]{77,39,55} },
            new Profile(){ Name="김영희", Score=new int[]{89,80,40} },
            new Profile(){ Name="이상훈", Score=new int[]{82,60,82} },
            new Profile(){ Name="이세상", Score=new int[]{95,88,91} },
            new Profile(){ Name="박철민", Score=new int[]{65,59,72} }
        };

        var grouping = from profile in profiles
                        group profile by profile.Score[0] < 70 into gr
                        select new { Groupkey = gr.Key, Profile = gr };

        foreach (var Group in grouping)
        {
            if(Group.Groupkey) // by에 의해 분류된 것은 Key값이 True를 갖는다.
            {
                Console.WriteLine("원하는 데이터"); // 65
            }
            else
            {
                Console.WriteLine("나머지 데이터");  // 77 89 82 95
            }

            foreach (var profile in Group.Profile)
            {
                Console.WriteLine("{0}", profile.Score[0]);
            }
        } 
    }
}

Join, 데이터 연결하기


조인을 통해 두 데이터 원본을 연결할 수 있습니다.

내부조인(Inner Join) 은 두 데이터 원본이 일치하는 데이터만 연결하여 추출합니다.

사용 방법:

from a in A
join b in B on a.XX equals by b.YY

join 절의 on 키워드는 조인 조건을 수반합니다. 이때 조인 조건은 동등(Equality)만 허용됩니다. equals라는 키워드 없이 동등연산자인 == 를 사용해도 됩니다.

외부조인(Outer Join) 은 외부조인은 일치하는 데이터가 없으면 빈값을 채워 모두 연결하여 추출합니다.

방법은 내부조인을 진행한 후에 into 키워드를 통해 임시컬렉션에 저장하고, 이 임시 컬렉션에 대해서 DefaultIfEmpty 연산을 수행하여 조인결과에 빈값을 채워 넣습니다.

조인 방식에는 왼쪽조인(Left Join), 오른쪽 조인 (Right Join), 완전 조인(Both Join) 세가지가 있지만, LINQ는 왼쪽조인만을 지원합니다.

예제 코드:

class Program
{
    class Profile
    {
        public string Name { get; set; }
        public int Height { get; set; }
    }
        
    class Product
    {
        public string Title { get; set; }
        public string Star { get; set; }
    }

    static void Main(string[] args)
    {
        Profile[] Profiles = new Profile[5]
        {
            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[] Products = new Product[6]
        {
            new Product() { Star="정우성" , Title="비트"},
            new Product() { Star="김태희" , Title="CF 다수"},
            new Product() { Star="김태희" , Title="아이리스"},
            new Product() { Star="이문세" , Title="붉은노을"},
            new Product() { Star="고현정" , Title="선덕여왕"},
            new Product() { Star="손예진" , Title="연애시대"}
        };

        var InnerJoinList = from profile in Profiles
                            join product in Products on profile.Name equals product.Star
                            select new
                            {
                                Name = profile.Name,
                                Work = product.Title,
                                Height = profile.Height
                            };

        var OuterJoinList = from profile in Profiles
                            join product in Products on profile.Name equals product.Star into ps
                            from product in ps.DefaultIfEmpty(new Product() { Title = "Null" })
                            select new
                                {
                                    Name = profile.Name,
                                    Work = product.Title,
                                    Height = profile.Height
                                };


        Console.WriteLine("===== 내부 조인 결과 ====");
        foreach( var profile in InnerJoinList)
        {
            Console.WriteLine("이름: {0} 작품: {1} 키: {2} ", profile.Name, profile.Work, profile.Height);
        }

        Console.WriteLine(Environment.NewLine+"==== 외부 조인 결과 ====");
        foreach (var profile in OuterJoinList)
        {
            Console.WriteLine("이름: {0} 작품: {1} 키: {2} ", profile.Name, profile.Work, profile.Height);
        }      
    }
}


C#은 LINQ를 통해 데이터에 관련된 작업을 보다 편리하게 다룰 수 있습니다. SQL을 모르더라도 LINQ를 하나 둘 사용하다보면 자연스럽게 익히실 수 있습니다.