개발팁2016. 7. 2. 13:28

C# 에서 난수값을 생성하기 위해 사용하는게 Random 클래스인데,

다른 언어에 비해 사용법이 간단하다.

 

Random rnd = new Random();

int val = rnd.Next(1, 100);

 

Random 클래스의 생성자는

 

void Random();

void Random(int seed);

 

2가지가 있는데, 보통 생성자 없이 사용하면, 시간을 seed 로 사용하게 된다.

seed 값을 지정하여,

Random rnd = new Random(DateTime.Now.Millisecond);

이렇게 사용하기도 한다.

seed 값을 고정값을 사용할경우에는, 항상 생성되는 random 값이 동일한 값이 나오게 된다.

 

난수를 생성하는 메서드는 3가지가 있다.

Next() - int 타입의 난수 발생

NextDouble() - double 타입의 난수 발생

NextBytes() - byte 배열의 난수 발생

 

Next 메서드는 3가지 형태로 사용이 가능하다.

1. Next(void) - int 타입의 0 이상의 난수를 발생한다.

2. Next(int maxValue) - 0 이상 maxValue 미만의 난수를 발생한다.

3. Next(int minValue, int maxValue) - minValue 이상 maxValue 미만의 난수를 발생한다. 음수도 가능.

 

여기서 주의할 점은,

난수 범위를 지정할때, 상한범위는 위에저 지정한 maxValue 이라는 값은 절대 나오지 않는다는 것이다.

rnd.Next(1, 100) 이라고 했을때 나올수 있는 난수는 1 ~ 99 까지이다, 절대 100은 나오지 않기 때문에,

사용시에 주의해서 사용해야 한다.

 

위 3가지 int 형 난수 발생시 성능을 한번 테스트 해보았다.

0 ~ 9 까지의 난수를 1000만번 발생시키는 테스트를 해보았다.

1. val = rnd.Next(0, 10);     // 테스트 1

2. val = rnd.Next(10);        // 테스트 2

3. val = rnd.Next() % 10;    // 테스트 3

 

이 3가지는 모두 0 ~ 9 사이의 난수를 발생하는 방법이다.

 

 

// 난수 마지막값은 발생하지 않는것을 확인하기 위해, 배열길이를 11개로 설정
int[] arrVal = new int[11] {0,0,0,0,0,0,0,0,0,0,0};

Random rnd = new Random();
int val;

var stopwatch = new Stopwatch(); // elapsed time 체크를 위해 stopwatch 사용


stopwatch.Start();

 

for (int i = 0; i < 10000000; i++)
{
 val = rnd.Next(0, 10);       // 테스트 1
 //val = rnd.Next(10);        // 테스트 2
 //val = rnd.Next() % 10;    // 테스트 3

 arrVal[val]++;
}

 

stopwatch.Stop();

 

this.txtElapTime.Text = stopwatch.ElapsedMilliseconds.ToString();

this.txtVal0.Text = arrVal[0].ToString();
this.txtVal1.Text = arrVal[1].ToString();
this.txtVal2.Text = arrVal[2].ToString();
this.txtVal3.Text = arrVal[3].ToString();
this.txtVal4.Text = arrVal[4].ToString();
this.txtVal5.Text = arrVal[5].ToString();
this.txtVal6.Text = arrVal[6].ToString();
this.txtVal7.Text = arrVal[7].ToString();
this.txtVal8.Text = arrVal[8].ToString();
this.txtVal9.Text = arrVal[9].ToString();
this.txtVal10.Text = arrVal[10].ToString(); // 마지막값 10은 절대 발생하지 않는다.

 


 

1. rnd.Next(0, 10) 을 사용하여, 1000만개의 난수를 발생하는 시간은 보통 280 ~ 300ms 가 소요.

 

 

2. val = rnd.Next(10) 을 사용하여, 1000만개의 난수를 발생하는 시간은 보통 180 ~ 190ms 가 소요.

 

 

3. rnd.Next() % 10 을 사용하여, 1000만개의 난수를 발생하는 시간은 보통 145 ~ 155ms 가 소요.

 

 

 

이정도면, 큰 차이가 없다고 볼수도 있지만, 미세하게 나마 퍼포먼스 면에서 차이를 보였다.

int Next(void) 가 가장 빨랐고,

int Next(int maxValue) 가 두번째,

int Next(int minValue, int maxValue) 가 가장 느렸다.

 

테스트에 사용된 프로젝트 파일을 다운받아서 확인해보십시요.

 

RandomTest.zip

 

 

 

Posted by 헝개
개발팁2016. 6. 17. 10:00

MSSQL 캐쉬삭제 방법입니다.

 

 

-- 버퍼 풀에서 빈버퍼 모두 제거
DBCC DROPCLEANBUFFERS
GO

 

-- 프로시저 캐시에서 모든 요소 제거
DBCC FREEPROCCACHE
GO

 

Posted by 헝개
개발팁2016. 6. 10. 10:00

데이터베이스가 오래되면, 인덱스에도 조각화(Fragmentation)가 되고,
어떨때는 쿼리플랜으로 보면, 인덱스가 선택되지 않는 경우도 있고,
이래저래 데이터베이스가 좀 느려졌다 싶을때,

 

가끔 인덱스를 REBUILD 해주면, 속도가 빨라지곤한다.
데이터 양에 따라 시간이 많이 걸리기 때문에,
사용자가 없는 새벽이나 유휴시간에 작업해야 한다.

 

데이터베이스 안에 많은 양의 테이블이 있기 때문에 일일이, 테이블을 지정해서 해주면,
노가다가 되기 때문에 자동화해서, 한번의 실행으로 모든 테이블의 인덱스를 REBUILD 해주는 방법이다.

 


1.
데이터베이스 안에 모든 테이블 목록을 가져오는 쿼리다.

SELECT '[' + table_catalog + '].[' + table_schema + '].[' + table_name + ']' as tableName
FROM DB명.INFORMATION_SCHEMA.TABLES
WHERE table_type = 'BASE TABLE' AND table_name not like 'sys%'

 

2.
이를 커서를 이용해서, 모든 테이블에 대해 아래의 인덱스 리빌드 쿼리를 실행할 것이다.

ALTER INDEX ALL ON [DB명].[dbo].[TABLE명] REBUILD WITH ( fillfactor = 90)

 

 

 


아래는 전체 풀 소스 이며, 커서를 통해, 모든 테이블을 처리하며,
동적쿼리문을 작성해서 실행한다.

 

DECLARE @db_nm  varchar(500) -- DB명
DECLARE @table_nm varchar(1000) -- 테이블명
DECLARE @cmd  nvarchar(4000) -- 동적쿼리
DECLARE @FillFactor int  -- 인덱스 채우기비율

SET @FillFactor = 90   -- 인덱스의 90%를 채운다.

SET @db_nm = 'nv_blog'

-- DB에서 테이블목록을 불러서 커서에 저장 (sys로 시작하는 테이블은 제외)
-- 테이블명 구조 : [DB명].[dbo].[TABLE명]
SET @cmd = 'DECLARE curTB cursor for SELECT ''['' + table_catalog + ''].['' + table_schema + ''].['' + table_name + '']'' as tableName
FROM ' + @db_nm + '.INFORMATION_SCHEMA.TABLES
WHERE table_type = ''BASE TABLE'' AND table_name not like ''sys%''';

EXEC (@cmd)


-- TABLE 커서 오픈
OPEN curTB

FETCH NEXT FROM curTB INTO @table_nm
WHILE @@fetch_status = 0
begin
 -- 테이블 별 인덱스를 리빌드함
 SET @cmd = 'ALTER INDEX ALL ON ' + @table_nm + ' REBUILD WITH ( fillfactor = ' + convert(varchar(3), @FillFactor ) + ')';

 print 'Work - ' + @cmd

 EXEC (@cmd)

 print 'Done - ' + @cmd

 FETCH NEXT FROM curTB INTO @table_nm
end

-- TABLE 커서 종료
CLOSE curTB
DEALLOCATE curTB

 

 

 

 

 

PS. 이 쿼리는 MS-SQL 2012 이후 버전에서 실행되었습니다.

 

Posted by 헝개
개발팁2016. 6. 8. 00:30

MS-SQL 의 데이터베이스 물리적 파일인 .MDF .LDF 파일의 용량이 갈수록 커지면서,
하드디스크 용량을 많이 차지 한다면, 아래와 같은 방법으로 용량을 줄여보자.


ALTER DATABASE DB명 SET RECOVERY SIMPLE
go
DBCC SHRINKFILE(DB명_Data)
go
DBCC SHRINKFILE(DB명_Log)
go
ALTER DATABASE DB명 SET RECOVERY FULL
go

 

 

위의 쿼리로 MDF, LDF 파일의 용량을 모두 줄일수 있지만,
MDF 는 데이터 파일이라, 용량이 그다지 줄지는 않을것이다.
아래와 같이 Log 파일만 줄여도 상관이 없다.

 

ALTER DATABASE DB명 SET RECOVERY SIMPLE
go
DBCC SHRINKFILE(DB명_Log)
go
ALTER DATABASE DB명 SET RECOVERY FULL
go

 

 

 


데이터 파일과 로그파일의 논리적 이름을 적어줘야 하는데, 어떤걸로 되어 있는지 모를경우,
데이터베이스 속성에서 아래와 같이 논리적 이름을 확인할 수 있다.

 

또는,
sp_helpdb DB명

명령으로도 쉽게 확인이 가능하다.

 

 

쿼리의 실행전,실행후 용량 비교이다.

 

 

PS. 이 쿼리는 MS-SQL 2012 이후 버전에서 실행되었습니다.

Posted by 헝개