LINQ란 Language Integrated Query 라고해서 특정 데이터들에서 Query를 하여 데이터를 빠르고 편리하게 추출하는 방식이라 할 수 있다. 해당기능은 C# 3.0부터 추가가 되기 시작한 문법이다. 기본적으로 람다표현식을 사용하여 간결하고 가독성 좋게 작성 가능하다. Query를 하는데에는 SQL을 사용한다. SQL 이란 Structured Query Language의 약자이다. 


LINQ는 이러한 SQL의 문법을 가지고 다양한 쿼리를 통해 데이터를 가공하고 집계하는 등에 사용된다. 전통적인 방싱그로 for문과 if문을 가지고 특정 데이터들을 가공하고 집계내는 것도 가능하다. 하지만 LINQ를 이용하면 빠르고 정확하게 데이터를 찾는 것이 가능하다. 그리고 더욱 중요한 가독성이 좋다. 문장을 서술 하듯 질의를 하기 때문에 for와 if문을 사용하는 방식보다 가독성이 좋아 실수를 줄이고 유지보수가 쉽다.


LINQ에 자세한 내용은 아래 링크를 참고하자.

http://hijuworld.tistory.com/56



LINQ는 기본적으로 쿼리를 실행하는데 1개의 스레드를 사용한다. 즉 cpu 하나만을 사용해서 처리한다.

하지만 병렬 LINQ(PLINQ)는 여러개의 스레드를 사용해서 쿼리를 실행한다. cpu에 갯수가 많을수록 많은 스레드를 만들어 처리한다.


물론 멀티 스레드가 싱글 스레드를 사용하는 것보다 속도가 더 빠르지 않다. 그래서 무조건 병렬 Linq를 사용하는 것보단 실제 테스트를 해보고 상황에 맞게 판단해서 사용하도록 하자.


아래예제는 1부터 5억까지의 수를 각각 2.1로 나누어 하나의 배열로 만드는 LINQ를 작성하고 해당 LINQ에 처리 시간을 측정하도록 했다. 싱글 스레드를 사용하는 방식이다.


시간측정에 Stopwatch 클래스를 사용했다. 시간측정에관한 자세한 내용은 아래 링크를 참고하자.

C# Stopwatch 클래스를 사용하여 프로그램 실행 시간 측정 하기 : http://hijuworld.tistory.com/18 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace LINQ_TEST
{
    class Program
    {
        static void Main(string[] args)
        {
            Stopwatch stopwatch = new Stopwatch();//시간측정 객체
            IEnumerable<int> bigNumber = Enumerable.Range(1, 500_000_000);
            Console.Write("아무키나 입력하시오.");
            Console.ReadLine(); //키입력받기
            stopwatch.Start(); //시간측정 시작            
            var result = bigNumber.Select(number => number/2.1).ToArray();
            stopwatch.Stop(); //시간측정 끝
            System.Console.WriteLine("time : " +
                           stopwatch.ElapsedMilliseconds + "ms");
        }
    }
}
 
cs

출력결과
아무키나 입력하시오.
time : 3693ms

아래 리소스 사용량을 보자. 스래드를 8개까지 사용할 수 있지만 4번째 하나만을 사용한 것을 볼 수 있다. 








병렬LINQ를 만들기 위해서는 AsParallel 을 사용하면 된다. 아래 예제를보자. 이전에 예제와 거의 같지만 bigNumber 다음에 AsParallel 함수를 사용하여 병렬처리되도록 한것을 볼 수 있다. 나머지 코드는 모두 동일하다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace Ch09_PLINQ
{
    class Program
    {
        static void Main(string[] args)
        {
            Stopwatch stopwatch = new Stopwatch();//시간측정 객체
            IEnumerable<int> bigNumber = Enumerable.Range(1, 500_000_000);
            Console.Write("아무키나 입력하시오.");
            Console.ReadLine(); //키입력받기
            stopwatch.Start(); //시간측정 시작            
            var result = bigNumber.AsParallel()
                            .Select(number => number/2.1).ToArray();
            stopwatch.Stop(); //시간측정 끝
            System.Console.WriteLine("time : " +
                           stopwatch.ElapsedMilliseconds + "ms");
        }
    }
}
 
cs

출력결과
아무키나 입력하시오.
time : 17602ms

아래 cpu 리소스 사용량을 보자. 8개의 스래드가 정상적으로 동작하는 것을 볼 수 있다.


하지만 이상하게 출력결과를 보면 병렬 링큐를 사용했을때 오히려 시간이 더 걸리는 것을 볼 수 있다. 데이터의 갯수를 다르게해도 결과는 같다. 정확한 이유는 알 수 없지만 무조건 병렬 처리를 한다고해서 속도가 향상되는것은 아닌 것을 알 수 있다.

정확한 원인을 알게되면 다음에 포스팅하도록 하겠다.


자료 참고 : 크로스 플랫폼 개발을 위한 c#과 닷넷 코어 2.0(에이콘 출판) 서적 


Posted by 꿈만은공돌
,


C#의 LINQ를 사용하면 데이터를 가공하고 추출하는데 편리하고 빠른 장점이 있다. 기본적으로 SQL을 사용해봤다면 직관적으로 쉽게 이해할 수 있을 것이다. 코딩을 배워보지 않은 사람도 쉽게 이해할 수 있도록 만들어진 언어가 SQL 이기 때문에 쉽게 배워 사용할 수 있다. 그리고 병렬로 처리도 가능하기 때문에 일반적으로 코드를 작성해서 데이터를 추출하는 가공방식보다 많은 이점이 있다. 


LINQ에 기본적인 질의방식에 대해서는 아래링크를 참고하자


- C# LINQ 사용 방법 및 예제(from, select, where, orderby) : http://hijuworld.tistory.com/56


우선 기본적인 합(Sum), 최대값(Max), 최소값(Min), 평균값(Average), 데이터 갯수(Count) 에 대해서 알아보자.


아래 예제는 1부터 9까지의 데이터를 가지고 있는 numbers라는 변수가 존재하는데 Linq 함수를 이용하여 집계를 하는 것을 볼 수 있다. 해당 예제는 배열을 가지고 작성했지만 List와 같은 C#에서 제공해주는 다양한 제너릭 클래스들에서 사용이 가능하다. 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
using System;
using System.Linq;
namespace test
{
    class Program
    {
        static void Main(string[] args)
        {
            int[] numbers = { 12345678};
            Console.WriteLine("sum : " + numbers.Sum());
            Console.WriteLine("max : " + numbers.Max());
            Console.WriteLine("min : " + numbers.Min());
            Console.WriteLine("average : "+ numbers.Average());
            Console.WriteLine("count : " + numbers.Count());
        }
    }
}
cs


네임스페이스인 using System.Linq 를 반드시 명시해줘야 한다.




Aggregate는 사용자가 직접 해당 데이터들을 어떻게 가공할지 명시 할 수 있다.


인자들의 합이나 최대값을 구하는 것이 아닌 가장 큰수와 가장 작은 수의 합이라던지 모든 수의 곱셈이라던지 등에 다양한 방식에 집계를 사용자가 직접 작성할 수 있는 장점이 있다. 잘만 사용하면 응용하여 사용할 수 있는 곳이 정말 많다.

람다식을 사용하여 함수의 내용을 쉽게 넘겨줄 수 있다. 


아래 예제는 모든 인자들의 곱을 계산하는 예제이다. 첫번째 인자와 두번째 인자를 곱해서 다시 첫번재 인자에 놓고 다시 그다음 번째 인자를 두번째 인자로 놓고 곱하는것을 반복한다. 


1
2
3
4
5
6
static void Main(string[] args)
{
    int[] numbers = { 1234};
    var data = numbers.Aggregate((num1, num2) => num1 * num2);
    Console.WriteLine(data);
}
cs

출력결과 : 120



아래 예제는 위의 예제와 동일하지만 Aggregate 함수에 파라미터를 앞에 하나 추가하였다.


첫번째 인자 하나는 고정하는 기능을 한다.


100 * 1 * 2 * 3 * 4 * 5 의 결과가 출력되게 된다. 해당 기능은 대소 비교를 할때 가장 처음에 기준값을 정할 때에도 사용이 가능하다.


1
2
3
4
5
6
static void Main(string[] args)
{
    int[] numbers = { 1234};
    var data = numbers.Aggregate(100, (num1, num2) => num1 * num2);
    Console.WriteLine(data);
}
cs


출력결과 : 12000



아래 예제는 첫번째 글자와 두번째 글자를 콤마를 사이에두고 연결한다.


1
2
3
4
5
6
static void Main(string[] args)
{
    String[] names = { "abc""kim""lee""han""park" };
    var data = names.Aggregate((str1,str2)=>str1+", "+str2);
    Console.WriteLine(data);
}
cs

출력 결과 : abc, kim, lee, han, park




이제는 좀더 응용해서 사용해보도록 하자.


아래는 학생들의 이름과 점수를 담고있는 객체들을 List로 가지고있는 데이터에서 학생들의 평균 점수를 구하는 예제이다. 클래스를 사용하여 학생의 이름과 점수를 저장할 수 있도록 하였다. 그리고 LINQ를 사용하여 데이터를 가공하는 것을 볼 수 있다. 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
using System;
using System.Collections.Generic;
using System.Linq;
namespace test
{
    public class Student
    {
        public string name { get; set; }
        public int score { get; set; }
    }
    class Program
    {
        static void Main(string[] args)
        {
            List<Student> students = new List<Student>
            {
                new Student { name = "zzz", score = 88 },
                new Student { name = "kim", score = 50 },
                new Student { name = "han", score = 70 },
                new Student { name = "park", score = 45 },
                new Student { name = "peter", score = 84 },
                new Student { name = "lee", score = 98 }
            };
            var result = (from student in students
                        select student).Average(student => student.score);
            Console.WriteLine("전체학생의 평균 : " + result);
        }
    }
}
 
 
cs


결과 : 전체학생의 평균 : 72.5



아래는 좀더 응용한 것으로 학생들의 이름이 4글자 이상인 학생들만 추출하고 그 학생들의 점수의 합을 구하는 예제이다. from으로 학생 하나씩 선택하여 where로 이름이 4글자인 학생만 추출하고 select함수로 학생의 정보를 받는다. 그리고 Sum 함수로 이름이 4글자인 학생에 점수들을 모두 합한다. 복잡해보이지만 하나하나 차근차근 보면 쉽게 이해가 갈 것이다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
using System;
using System.Collections.Generic;
using System.Linq;
namespace test
{
    public class Student
    {
        public string name { get; set; }
        public int score { get; set; }
    }
    class Program
    {
        static void Main(string[] args)
        {
            List<Student> students = new List<Student>
            {
                new Student { name = "zzz", score = 88 },
                new Student { name = "kim", score = 50 },
                new Student { name = "han", score = 70 },
                new Student { name = "park", score = 45 },
                new Student { name = "peter", score = 84 },
                new Student { name = "lee", score = 98 }
            };
            var result = (from student in students
                          where student.name.Length >= 4
                          select student).Sum(student => student.score);
            Console.WriteLine("이름이 4글자 이상인 학생들의 점수 합 : " + result);
        }
    }
}
 
 
cs


출력 결과 : 이름이 4글자 이상인 학생들의 점수 합 : 129


아래 예제는 group by 키워드를 사용하여 50점이상 학생과 미만 학생으로 나눈후 해당 학생들의 숫자와 평균, 최대값을 구하는 코드이다. Group by는 특정 조건으로 데이터들을 그룹을 지어주는 기능을 한다. 예를들어 1~100까지의 수가 있어서 group by를 사용하여 50이상과 미만으로 나누어 각각의 평균을 구할 수 있다. 자주 사용되기 때문에 알아두면 좋다. 그룹으로 나눈 각각의 그룹은 into 키워드를 사용하여 g로 나타낸다. 그래서 g.key 사용하여 참인지 거짓인지를 가지고 어느 그룹의 데이터인지 알 수 있다. 그래서 각 그룹의 평균과 최대값 등을 알아 낼 수 있다. 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
using System;
using System.Collections.Generic;
using System.Linq;
namespace test
{
    public class Student
    {
        public string name { get; set; }
        public int score { get; set; }
    }
 
    class Program
    {
        static void Main(string[] args)
        {
            List<Student> students = new List<Student>
            {
                new Student { name = "zzz", score = 88 },
                new Student { name = "kim", score = 20 },
                new Student { name = "han", score = 70 },
                new Student { name = "park", score = 45 },
                new Student { name = "peter", score = 84 },
                new Student { name = "lee", score = 98 }
            };
 
            var result = from student in students
                         group student by student.score >= 50 into g //50점을 기준으로 나눔
                         select new
                         {
                             key = g.Key==true ?"50점이상":"50점미만"//50점이상데이터인지
                             count = g.Count(), //해당 구간의 학생수
                             avr = g.Average(student => student.score), //해당 구간의 평균점수
                             max = g.Max(student => student.score) // 해당 구간에 최대값
                         };
            
            foreach (var data in result)
            {
                Console.WriteLine(data.key + " 학생 수 : " + data.count);
                Console.WriteLine(data.key + " 중 최대점수 : " + data.max);
                Console.WriteLine(data.key + " 학생들의 평균 : " + data.avr);
                Console.WriteLine("------------------------------------");
 
            }
        }
    }
}
cs

출력결과는 아래와 같다. 50이상의 학생과 미만의 학생의 최대값과 학생수 평균등을 출력한다.


Posted by 꿈만은공돌
,


LINQ란 Language Integrated Query 라고해서 특정 데이터들에서 Query를 하여 데이터를 빠르고 편리하게 추출하는 방식이라 할 수 있다. 해당기능은 C# 3.0부터 추가가 되기 시작한 문법이다. 기본적으로 람다표현식을 사용하여 간결하고 가독성 좋게 작성 가능하다. Query를 하는데에는 SQL을 사용한다. SQL 이란 Structured Query Language의 약자이다. 


SQL에서 가장 많이 사용하는 문법은 다음 4가지 이다.


from : 어떤 데이터에서 찾을 것인가


where : 어떤 조건으로 찾을 것인가


order by : 어떤 항목을 기준으로 정렬할 것인가


select : 어떤 항목을 추출할 것인가


LINQ는 이러한 SQL의 문법을 가지고 다양한 쿼리를 통해 데이터를 가공하고 집계하는 등에 사용된다. 전통적인 방싱그로 for문과 if문을 가지고 특정 데이터들을 가공하고 집계내는 것도 가능하다. 하지만 LINQ를 이용하면 빠르고 정확하게 데이터를 찾는 것이 가능하다. 그리고 더욱 중요한 가독성이 좋다. 문장을 서술 하듯 질의를 하기 때문에 for와 if문을 사용하는 방식보다 가독성이 좋아 실수를 줄이고 유지보수가 쉽다.

또한 병렬로 처리도 가능하기 때문에 일반적으로 코드를 작성해서 데이터를 추출하는 가공방식보다 속도가 빠를 수 있다.


LINQ는 기본적으로 아래에서 소개한 예제만 보면 어느정도 이해가 갈 것이다. 더욱 복잡한 예제들도 많이 존재 하고 사용방법도 복잡한 것들도 많지만 우선 아래 예제들을 보고 LINQ가 무엇인지 이해하자.


아래 예제는 1부터 9까지의 수중에 3의 배수를 찾는 예제이다. from 문으로 nums에 저장된 1부터 9까지의 숫자중 하나씩 추출하며 where 로 3의 배수인지를 검사한다. 검사결과가 참인 데이터인 num을 select 문으로 추출한다. 그 결과를 변수 numQuery에 저장한다. foreach문으로 결과를 출력한다. LINQ를 사용한 데이터 가공에 가장 기본적인 형태의 예제이다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
using System;
using System.Linq;
namespace test
{
    class Program
    {
        static void Main(string[] args)
        {
            int[] nums = new int[9] { 12345678};
            var numQuery = from num in nums
                           where (num%3== 0
                           select num;
            foreach(int num in numQuery) //결과 출력            
                Console.Write(num +" ");            
        }
    }
}
cs

출력결과 : 3 6 9




아래 예제는 길이가 3글자인 문자열을 찾는 것이다. where에서 string.length ==3 으로 문자길이가 3인 것을 찾는다. 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
using System;
using System.Linq;
namespace test
{
    class Program
    {
        static void Main(string[] args)
        {
            String[] names = new String[]{ "abc""kim","peter""Tistory""Z"};
            var nameQuery = from name in names
                                  where name.Length == 3
                                   select name;
            foreach(string name in nameQuery) // 결과 출력
                Console.WriteLine(name);
        }
    }
}
 
cs

출력 결과

abc 

kim




아래 예제는 60점이상 학생만 이름순으로 출력하는 것이다. student 클래스를 선언하여 이름과 점수를 저장하도록 한다. List객체를 만들어서 학생들의 정보를 저장한다. 그래서 해당 객체에 점수가 60점이상인 학생들을 where 문으로 검사하여 추출하고 orderby를 사용하여 이름순으로 정렬 시킨다. 그리고 select 에서 학생의 이름만을 추출한다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
using System;
using System.Collections.Generic;
using System.Linq;
namespace test
{
    public class Student
    {
        public string name { get; set; }
        public int score { get; set; }
    } 
 
    class Program
    {
        static void Main(string[] args)
        {
            List<Student> students = new List<Student>
            {
                new Student { name = "zzz", score = 88},
                new Student { name = "kim", score = 50},
                new Student { name = "han", score = 76 },
                new Student { name = "park", score = 45 },
                new Student { name = "peter", score = 84 },
                new Student { name = "lee", score = 98 }
            };
            var smart = from student in students
                        where student.score >= 60 // 60점이상만
                        orderby student.name ascending//이름순으로 정렬
                        select student.name; //이름만 추출
 
            foreach(string studentName in smart)     //결과 출력        
                Console.WriteLine(studentName);
        }
    }
}
cs

출력 결과

han

lee

peter

zzz


orderby는 orderby 정렬기준 ascending 이런식으로 사용을 한다.

기본으로 오름차순(ascending)이며 내림차순으로 하고 싶다면 descending 을 써주면 된다. 디폴트는 오름차순인 ascending이기 때문에 반드시 명시할 필요는 없다.


이외에도 LINQ를 이용하면 합(Sum), 최대값(Max), 최소값(Min), 평균값(Average), 데이터 갯수(Count)들을 쉽게 계산할 수 있다. 아래 링크를 참고하도록 하자.

C# LINQ 집계함수(Sum,MAX,MIN,Average,count,Aggregate 등) : http://hijuworld.tistory.com/57

Posted by 꿈만은공돌
,