Internet Explorer로 Oracle Enterprise Manager에 접속 할 경우 아래와 같은 페이지가 뜨고 접속이 안 되는 경우 해결법.





원인.

마이크로 소프트의 보안 인증서 관련 업데이트(KB 2661254)로 인하여 모든 운영체제에서 1024bit 보다

짧은 키를 가진 RSA 인증서는 차단되도록 변경되었습니다.

하지만 오라클 EM의 기본 인증서 Bit Length는 512bit 이기 때문에 익스플로러에서는 원천 차단되어 넘어가지 않는 것 입니다.


해결법은 3가지가 존재합니다.

1. certutil을 이용하여 익스플로러의 차단 인증서 길이를 1024 에서 512로 내리는 방법.

2. 오라클의 인증서를 1024로 재 발급 하는 방법.

3. 다른 브라우저를 사용한다.


2번의 방법은 추후 포스팅 하기로 하고 먼저 1번 certutil을 이용한 방법을 포스팅 하겠습니다.

3번은 제일 간단하니 뭐... 포스팅하지 않겠습니다.


1. certutil을 이용한 방법


certutil은 윈도우 vista 이상이면 기본으로 포함되어 있는 도구이므로 그 이하 버전의 윈도우를 제외하곤 따로 설치 하실 필요는 없습니다.

certutil을 지원하지 않는 버전의 윈도우에서는 레지스트리 직접수정으로 해결 가능합니다.

마소에서 작업 전 레지스트리 백업을 권장 하는것으로 보아 사실 certutil 자체가 인증관련 레지스트리 수정을 보조하는 도구로서

win7이나 win2008 등 certutil을 지원하는 버전의 윈도우도 레지수정으로 처리를 해도 관계 없을 것으로 보입니다.


- 레지스트리 경로

HKEY_LOCAL_MACHINE\Software\Microsoft\Cryptography\OID\EncodingType 0\CertDLLCreateCertificateChainEngine\Config


- 작성 키 값

  MinRsaPubKeyBitLength

  EnableWeakSignatureFlags

  WeakSignatureLogDir

  WeakRsaPubKeyTime


이 포스팅에서는 MinRsaPubKeyBitLength와 EnableWeakSignatureFlags로 1024이하의 인증서를 계속 허용하는 방법만 다루겠습니다.

자세한 세팅에 대해 궁금하신분은 참조 URL의 "Microsoft 보안 공지: 최소 인증서 키 길이 업데이트" 를 참조 하세요.


- 각 키의 설명

MinRsaPubKeyBitLength

허용되는 최소 RSA 키 길이를 정의하는 값입니다. 기본 RSA 키 길이는 1024입니다.

본 포스팅에서는 오라클 인증서의 길이인 512로 설정합니다.

ex) certutil -setreg chain\minRSAPubKeyBitLength 512


EnableWeakSignatureFlags

2, 4, 6, 8의 네 가지 값을 가질 수 있으며 설정에 따라 차단 기능 작동 방식이 달라집니다.

4나 8 로 설정 했을 경우에는 WeakSignatureLogDir의 설정이 필수입니다.

본 포스팅에서는 2로 설정합니다.

ex) certutil -setreg chain\EnableWeakSignatureFlags 2


    설명 

 키 길이가 1024비트보다 작은 RSA 루트 인증서를 사용

4

 키 길이가 1024비트보다 작은 인증서를 계속 차단하면서 로깅을 사용

6

 루트 인증서 아래에서 키 길이가 1024비트보다 작은 RSA 인증서의 로깅을 사용하도록 설정

8

 로깅만 사용하도록 설정하고 길이가 1024비트보다 작은 키를 차단하지 않음


WeakSignatureLogDir
 키 길이가 1024비트보다 짧은 인증서가 기록될 폴더를 지정합니다.
ex) certutil -setreg chain\WeakSignatureLogDir "c:\Under1024KeyLog"

WeakRsaPubKeyTime

구성된 날짜 및 시간 전에 코드에 설명하는 데 사용된 인증서는 키 길이가 1024비트보다 짧은지 확인되지 않습니다.

라고 설명은 되어있는데 설정해봤지만 별 반응이 없어서 다음에 다시 해보기로 했습니다.

마소에서는 certutil -setreg chain\WeakRsaPubKeyTime @01/15/2011+00:18 라고 입력하라고 되어있는데 이렇게 입력하면 오류가 납니다.

날짜를 각 언어 로케일대로 입력해야하는데 한글의 경우엔 YYYY-MM-DD로 입력

ex) certutil -setreg chain\WeakRsaPubKeyTime @2013-05-01+00:01


- 작업 순서


1. 커맨드 창을 엽니다.

ex) 윈도우키 + R -> cmd (엔터)


2. 아래 명령어를 입력합니다.

certutil -setreg chain\minRSAPubKeyBitLength 512

certutil -setreg chain\EnableWeakSignatureFlags 2


레지스트리 수정의 경우엔 위의 레지스트리 경로로 가서 아래와 같은 DWORD키를 만드세요.

키 : minRSAPubKeyBitLength         값 : 512

키 : EnableWeakSignatureFlags     값 : 2



변경 사항 어쩌고는 신경쓰시지 않아도 됩니다. 익스플로러만 껐다 키면 문제 없습니다.


3. 설정이 잘 되었는지 확인합니다.

certutil -getreg chain



4. 설정을 해제 할 때 입력 명령어

certutil -delreg chain\minRSAPubKeyBitLength

certutil -delreg chain\EnableWeakSignatureFlags

레지 수정의 경우엔 해당 경로로 가서 키를 삭제 하세요.



여기까지 설정을 마쳤다면 익스플로러를 닫은 후 다시 열고 EM에 접속해보세요.

같은 화면 뜨지만 빨간 박스 안의 링크가 추가되거나 원래 추가되어 있던 사람은 링크를 눌렀을때 정상적으로 접속이 됩니다.







이렇게 설정을 해놓은 상태에서는 계속 512비트의 인증서도 받아들이도록되니 보안수준이 낮아지겠죠.

레지스트리 파일이나 배치파일로 만들어두고 설정을 켯다 껐다 하는것이 낫지 않을까 합니다.



--------------------------------------------------------------------------------------------------------


- 참조 URL

Microsoft 보안 공지: 최소 인증서 키 길이 업데이트 : http://support.microsoft.com/kb/2661254/ko


변수 는 바인드 변수 입니다.

db 프로그래밍을 짜실 때 꼭 한가지를 기억하세요.. 저는 자바로 설명하겠지만 이건 꼭 언어를 따지는 건 아닙니다. 

가령 이런 SQL문이 있다고 하죠. 

select * from tab where id = 1; 

그리고 시스템에서 id 를 입력받아서 매번 각 id에 대해 위의 쿼리를 실행한다고 하죠. 

그러면 다음과 같이 쿼리를 생성해서는 안됩니다. 

Connection con = null; 
Statement stmt = null; 

String query = "select * from tab where id = " + id; 

stmt = con.createStatement(stmt); 
ResultSet rs = stmt.executeQuery(); 

이와같이 코딩하면 SQL문장이 DBMS에 개별적으로 들어갑니다. 
즉, 
select * from tab where id = 1; 
select * from tab where id = 2; 
select * from tab where id = 3; 
select * from tab where id = 4; 
select * from tab where id = 5; 

가 전부다 다른 문장으로 처리된다는 말이죠. 다른 문장으로 처리된다는 말은 매번 저 문장이 
파싱되야한다는 말입니다. 

그러나 쿼리를 

select * from tab where id = ? 

와 같이 만들고 ? 자리에 값을 넣는 방법이 어떤 언어든지 있습니다. 

이럴때 ? 를 바인드 변수라고 하는데, 바인드 변수를 사용하면 위의쿼리는 

select * from tab where id = :id_val; 

이렇게 치환되죠.. 

그리고 개별 쿼리시마다 동일한 sql문을 수행하되 id_val의 값이 바뀌게 됩니다. 

자바의 경우는 이것이 PreparedStatement입니다. 또 어떤분은 PreparedStatement를 쓰긴 
하는데 정말 이상하게 씁니다. 가령 다음과 같은 식이죠. 

String query = "select * from tab where id = " + id; 
PreparedStatement pstmt = con.prepareStatement(query); 

이런식이죠... 이렇게 해서야 바인드 변수가 쓰이지 않습니다. 

String query = "select * from tab where id = ?" 
PreparedStatement pstmt = con.prepareStatement(query); 
pstmt.setInt(1, id); 

이런식으로 코딩해야합니다.. 

그러면 DBMS에는 하나의 쿼리인 select * from tab where id = ? 만 들어가고 
DBMS는 이 문장만 파싱한뒤에 바인드 변수값 바뀌는 처리는 파싱을 제외하고 해줄 수 있습니다.. 

파싱이 뭐 대단하겠냐 하겠지만 파싱이 많으면 CPU점유율이 높아집니다.. 매일 CPU한계치에 
도달해서 뺑뺑이 치는 DBMS에서 이와같이 PreparedStatement를 쓰고 안쓰고의 차이는 
심지어 CPU 차지율을 20% 이상 차이나게 합니다.. 

(말이 20% 지 평상시에 40% 의 CPU가 사용되는 시스템은 60% CPU를 먹게 만든단 뜻입니다..) 

Statement로만 도배를 해놓은 경우 급하면 급한대로 오라클의 init{SID}.ora파일에서
CURSOR_SHARING=FORCE로 선언해주면 급하게 해결을 볼 수 있습니다..

그러나 오라클에서는 DSS나 QUERY REWRITE를 사용하는 환경에서는
이 매개변수를 FORCE로 고치지 말라고 경고하고 있습니다.

얼핏 생각해봐도 아시겠지만, CURSOR_SHARING=FORCE로 해주면 대부분의 쿼리를 오라클이 임의로 수정하여
바인드 변수를 쓰게 만들어 실행계획이 매번 동일하게 돌아가는 효과를 낳습니다.
따라서 QUERY REWRITE와 DSS에서 필요한 그때 그때에 맞는 정확한 플랜이 안나오게
될 수 있다는 것이죠.

QUERY REWRITE를 쓰는지 안쓰는지 모르겠다고 그러시면 안쓰는 것입니다. 제가 알기로는
QUERY REWRITE가 8i 이하에서는 Materialized View외에서는 사용되지 않습니다..
9i 부터는 모르겠군요. 

만약 자신의 데이터베이스에서 얼마나 많은 재파싱이 일어나고 싶은지를 알고 싶다면,
다음 스크립트를 sys로 접근한뒤 실행해보세요.



prompt
prompt * SQL문 parsing time 구하기
prompt
SELECT NAME,
VALUE
FROM V$SYSSTAT
WHERE NAME = 'parse time cpu'
or NAME = 'parse time elapsed'
or NAME like 'parse count%';

결과는 대략 다음과 같이 나타납니다.

NAME VALUE
------------------------------ ------------
parse time cpu 0
parse time elapsed 0
parse count (total) 322,128
parse count (hard) 662

SQL>

여기서 total 은 전체 파싱된 sql문장의 수 이며 (hard)는 하드 파싱, 즉 새로
파싱된 문장의 수를 의미합니다.

이 비율과 parse time cpu 에 나타난 parsing 에 소요된 cpu 시간을 사용해
접근하시면 됩니다.

from:
http://javastudy.co.kr/bbs/read.jsp?bbs=lecadvancebbs&id=70

 

 

----------------------------------------------------------------------------------------

 

EXAMPLE  2.:

 

----------------------------------------------------------------------------------------
-- subject : 바인드변수를 사용하라
-- summary : pl/sql 또는 sql로직에서 바인드변수를 사용한 로직과 사용하지않은
  로직을 각각돌려보면 실행시간이 확실히 차이가난다.
  이 원인은, 바인드변수를 사용하면 shared pool내에서 최초 한번 실행한 sql은
  다음의 똑같은 sql이 왔을때 다시 hard parse를 하지 않고 이미 parse된 sql을 사용하게된다.
----------------------------------------------------------------------------------------


SQL> alter system flush shared_pool;

-- shared_pool내용을 비우는 역활을 한다. 
-- 이렇게 하는 이유는 이후 실행할 쿼리에 대한 parse결과를 메모리상에서 없애기 위해서이다.



SQL> declare
 type rc is ref cursor;
 l_rc rc;
 l_dummy all_objects.object_name%type;
 l_start number default dbms_utility.get_time;
begin
 for i in 1 .. 1000
 loop
  open l_rc for
  'select object_name from all_objects where object_id = ' || i;
  fetch l_rc into l_dummy;
  close l_rc;
 end loop;
 dbms_output.put_line ( round( (dbms_utility.get_time - l_start)/100, 2) || 'seconds');
end;
/

23.17seconds
PL/SQL 처리가 정상적으로 완료되었습니다.
경   과: 00:00:23.02

 

SQL> declare
 type rc is ref cursor;
 l_rc rc;
 l_dummy all_objects.object_name%type;
 l_start number default dbms_utility.get_time;
begin
 for i in 1 .. 1000
 loop
  open l_rc for
  'select object_name from all_objects where object_id = :x' using i; --바인드변수를 사용함
  fetch l_rc into l_dummy;
  close l_rc;
 end loop;
 dbms_output.put_line ( round ( (dbms_utility.get_time - l_start) / 100, 2) || 'seconds..');
end;
/

1.71seconds..
PL/SQL 처리가 정상적으로 완료되었습니다.
경   과: 00:00:01.07

 

 pl/sql 또는 sql로직에서 바인드변수를 사용한 로직과 사용하지않은 로직을 각각돌려보명 실행시간이  확실히 차이가난다.
  

이 원인은, 바인드변수를 사용하면 shared pool내에서 최초 한버실행한 sql은 다음의 똑같은 sql이 왔을때 다시 hard parse를 하지 않고 이미 parse된 쿼리로 사용하게 되기 때문이다.

 

고로 하드 파싱을 줄이는것이 핵심이라고 할수 있다.

 

 

----------------------------------------------------------------------------------------

EXAMPLE  3.:

 

바인드 변수의 사용시 조건절의 컬럼에 인덱스가 있더라도 varchar2형에 숫자형을 바인드변수로 비교하게되면
인덱스가 있는 컬럼쪽이 to_number()로 형변환이 일어나게 된다. 따라서 인덱스를 사용할수 없고 테이블
풀스캔으로 시도한다.

ex) select empno, ename
from emp
where empno=:v1;


empno를 number로 타입을 바꾸거나,

차선책으로 v1 바인드 변수를 to_char()로 형변환하여 인덱스 스캔이 가능하도록 한다.

 

----------------------------------------------------------------------------------------

EXAMPLE  4.:

 

 

바인드의 가치 

Mark A. Williams

 



 바인드 변수와 ODP.NET을 사용한 .NET 애플리케이션의 성능 향상

 

.NET 애플리케이션에서 Oracle Database의 데이터에 액세스하는 방법에는 여러 가지가 있습니다. 그러나 기능과 성능의 관점에서 볼 때 .NET 애플리케이션을 Oracle Database와 연결하는 방법으로는 Oracle Data Provider for .NET(ODP.NET)가 가장 좋은 선택입니다.

본 컬럼에서는 ODP.NET 10g의 핵심 기능을 통해 .NET 애플리케이션을 향상시킬 수 있는 방법을 중심으로 살펴보기로 하겠습니다. 첫 번째 주제는 ODP.NET 10g와 함께 바인드 변수를 사용하는 방법에 대한 것입니다.

바인드 변수 사용

ODP.NET 10g가 효율적으로 작동하고 확장되도록 만들기 위한 가장 간편하고 효과적인 방법 중 하나는 바인드 변수를 적절히 사용하는 것입니다. 바인드 변수는 다양한 유형의 애플리케이션 성능 향상에 도움이 되지만 여기서는 ODP.NET 10g와 함께 사용하는 경우에 대해 살펴보겠습니다.

바인드 변수는 SQL 문의 위치 표시자입니다. 예를 들어 Oracle Database와 함께 제공되는 HR 샘플 스키마를 사용할 경우, 바인드 변수를 사용하지 않는 일반적인 SELECT 문은 다음과 같습니다.

select country_name from hr.countries where country_id = 'UK' 

보는 것처럼 문의 WHERE 절에 리터럴 텍스트를 ‘UK’로 지정하였습니다. 간단히 한 가지를 변경하여 바인드 변수를 사용하도록 문을 변환할 수 있습니다.

select country_name from hr.countries where country_id = :country_id 

바인드 변수를 사용하는 문으로 변환하기 위해 리터럴 텍스트‘UK’를 :country_id로 식별되는 위치 표시자로 바꾸었습니다. 이 바인드 변수 식별자는 단일 콜론(":")으로 시작되는데 이것은 바인드 변수가 SQL 문에서 어떻게 나타나는지를 보여줍니다.

여기서는 SELECT 문을 사용하여 바인드 변수의 용도를 예시하였으나 바인드 변수는 UPDATE, INSERT, DELETE 문에서도 사용될 수 있으며 또 사용해야 합니다. 다음은 같은 countries 테이블을 사용하여UPDATE 문에서 바인드 변수를 사용한 예입니다.

update hr.countries 
set country_name = :country_name 
where country_id = :country_id 

바인드 변수가 중요한 이유

Oracle Database 10g가 SQL 문으로 제시되면 메모리 영역인 공유 풀을 확인하여 해당 문이 이미 존재하는지, 그 문이 메모리에 저장되어 있는지를 확인합니다. 해당 문이 메모리에 이미 존재하고 Oracle Database 10g가 해당 문을 재사용할 수 있으면 데이터베이스는 이 문의 구문 분석과 최적화 작업을 생략하고 건너뛸 수 있습니다. 바인드 변수를 사용하는 경우에는 SQL 문이 메모리에 저장될 가능성이 매우 높아지므로 SQL 문을 필요로 하는 후속 작업에서 이 문을 신속하게 사용할 수 있습니다.

공유 풀에 해당 문이 존재하지 않는 경우, 데이터베이스는 해당 문에 대한 구문 분석과 최적화 작업을 수행해야 하므로 성능이 저하될 수 있습니다. 문의 구문 분석과 최적화 작업에는 CPU 주기가 소모되며 소모되는 CPU 주기가 많을수록 작업은 더 느려지게 됩니다. 구문 분석과 최적화 작업은 또한 래치라는 메커니즘을 통해 공유 풀의 다양한 부분을 잠금 상태로 두게 됩니다. 한 번에 하나의 프로세스만 래치를 유지할 수 있으므로 공유 풀에 래칭이 증가하면 데이터베이스 안에서 경합이 벌어지게 됩니다.

단일 사용자 시스템에서는 래치가 발생하는 양과 문의 구문 분석 및 최적화에 사용되는 시간이 별로 중요하지 않게 보일 수 있습니다. 그러나 시스템에 사용자가 계속 추가되거나 애플리케이션의 추가 복사본이 실행되면 이러한 이벤트는 순식간에 늘어날 수 있으며 심지어는 시스템을 사용불능 상태로 만들 수도 있습니다.

ODP.NET 10g에서 바인드 변수 구현

ODP.NET 10g 프로그램에서 바인드 변수를 사용하려면 OracleParameter 클래스를 사용하여 .NET 코드로 각 바인드 변수를 표시합니다. OracleParameterCollection 클래스는 그 이름으로 알 수 있듯이 각 문에 대한 OracleCommand 객체와 관련된 OracleParameter 객체를 포함하는 수집 클래스입니다.OracleCommand 클래스는 SQL 문을 데이터베이스에 전달하고 그 결과를 애플리케이션에 반환합니다.

ODP.NET 10g에서는 bind by position(기본값) 또는 bind by name이라는 바인드 변수를 사용하는 두 가지 모드를 사용할 수 있습니다. OracleCommand의 부울 속성 BindByName(기본값은 false)이 모드를 설정합니다. bind by position 모드를 사용할 경우 SQL 문에 보이는 것과 같은 순서로OracleParameterCollection 모음에 이 매개변수를 추가해야 합니다. 명령어 객체에 대한 모음에 매개변수를 추가하려면 Parameters 속성에서 Add 메소드를 사용합니다. bind by name 모드를 사용할 경우는 이 모음에 어떤 순서로 매개변수를 추가해도 관계 없으나, 해당 매개변수 객체에 대한ParameterName 속성을 SQL 문의 바인드 변수 식별자와 같은 값으로 설정해야 합니다.

바인딩 모드(by position 또는 by name) 외에도 일반적으로 각 매개변수 객체에 대해 다음과 같은 몇 가지 속성을 설정합니다.

  • Direction
  • OracleDbType
  • Size
  • Value

바인드 변수는 출력, 입력 또는 입/출력 매개변수로 사용될 수 있습니다. 각 매개변수에 대해 적절한 방향을 표시하려면 Direction 속성을 사용합니다. Direction 속성의 기본값은 Input.입니다. 이 매개변수가 숫자, 날짜, VARCHAR2 등 가운데 어떤 것인지 표시하려면 OracleDbType 속성을 사용합니다. VARCHAR2데이터 유형과 같은 변수 길이 데이터 유형을 사용할 경우, 매개변수가 가지고 있는 데이터의 최대 크기를 표시하려면 Size 속성을 사용합니다. Value 속성에는 문 실행 전(입력 매개변수의 경우), 실행 후(출력 매개변수의 경우), 또는 전후(입/출력 매개변수의 경우)의 매개변수 값이 들어있습니다.

목록 1의 Main 메소드는 이러한 개념들을 하나로 묶어주며 SELECT 문에서 바인드 변수를 사용합니다. 다음은 Main 메소드의 핵심 부분입니다.

OracleCommand cmd = new OracleCommand(); 

?명령 객체(OracleCommand) 생성

OracleParameter p_country_id = new OracleParameter(); 

?매개변수 객체(OracleParameter) 생성

p_country_id.OracleDbType = OracleDbType.Varchar2; p_country_id.Value = "UK"; 

?매개변수 객체의 OracleDbType 및 Value 속성 설정

다음 단계

ODP.NET에 대한 추가 정보
Oracle Data Provider for .NET 개발자 가이드

다운로드
이 컬럼에 대한 샘플 애플리케이션 
ODP.NET 10g 
Oracle Developer Tools for Visual Studio .NET

Direction 속성이 기본값 Input을 사용하며 Size 속성은 설정되어 있지 않은 점에 주의하십시오. 이 객체는 입력 매개변수이므로 속성을 설정할 필요가 없습니다. Data Provider가 해당 값의 크기를 결정할 수 있기 때문입니다.

cmd.Parameters.Add(p_country_id); 

?모음에 매개변수 추가

목록 1의 샘플 애플리케이션을 실행하려면 .NET 콘솔 애플리케이션을 생성하고 메소드의 본문으로서 목록 1 코드를 사용하여 Main 메소드를 생성합니다. 프로젝트에 ODP.NET 10g 어셈블리에 대한 참조를 추가하고 코드 모듈 상단에 Oracle .DataAccess.Client 이름 공간을 포함시켜야 합니다. 참조를 추가하려면 Microsoft Visual Studio .NET 2003 메뉴 표시줄에서 Project -> Add Reference...를 선택한 다음 Add Reference 대화상자에서 Oracle .DataAccess.dll을 선택합니다. 이름 공간을 포함시키려면 using Oracle.DataAccess.Client;를 코드 모듈 상단에 추가합니다. 출력 결과는 다음과 같습니다.

C:\VTB\bin\Release>ValuesThatBind.exe Country Name: United Kingdom 

코드 목록 1: 바인드 변수 사용

static void Main(string[] args) { 
 string constr = "User Id=hr; 
 Password=hr; Data Source=oramag"; 
 OracleConnection con = new OracleConnection(constr); 
 con.Open(); 
 StringBuilder sbSQL = new StringBuilder(); 
 sbSQL.Append("select country_name "); 
 sbSQL.Append("from countries "); 
 sbSQL.Append("where country_id = :country_id"); 
 OracleCommand cmd = new OracleCommand(); 
 cmd.Connection = con; cmd.CommandText = sbSQL.ToString(); 
 OracleParameter p_country_id = new OracleParameter(); 
 p_country_id.OracleDbType = OracleDbType.Varchar2; 
 p_country_id.Value = "UK"; cmd.Parameters.Add(p_country_id); 
 OracleDataReader dr = cmd.ExecuteReader(); 


 if (dr.Read()) 
 { 
 Console.WriteLine("Country Name: {0}", dr.GetOracleString(0)); 
 } 
 
 dr.Dispose(); 
 p_country_id.Dispose(); 
 cmd.Dispose(); 
 con.Dispose(); 
} 

바인드 변수: 계속 사용 권장

본 컬럼에서는 .NET 애플리케이션에서 ODP.NET 10g와 함께 간단한 바인드 변수를 사용하는 방법에 대해 설명했습니다. 바인드 변수를 사용할 때와 그렇지 않은 경우에 대해 각 애플리케이션을 테스트하여 바인드 변수의 이점을 확인해 보시기 바랍니다..

 

 

----------------------------------------------------------------------------------------

EXAMPLE  5.:

 

 

Bind 변수를 활용하자

다음과 같은 SQL 구문에 대해 생각해 봅시다.

SELECT * FROM customer WHERE custid=1 
SELECT * FROM customer WHERE custid=249 
SELECT * FROM customer WHERE custid=6380 

오라클은 기본적으로 각각의 구문 별로 다른 실행 계획을 사용합니다. 이 쿼리들이 모두 동일한 구문을 기반으로 하는 것임을 감안하면, 이러한 방법은 시간과 리소스 면에서 낭비일 수 있습니다. 그 대신, 쿼리에 바인드 변수(bind variable; “:var”)를 적용하고 1, 249, 6380의 값을 적용하는 방법을 사용할 수 있습니다. 이렇게 하면 모든 SQL 구문이 동일한 실행 계획을 공유하도록 할 수 있습니다:

변수에 바인드 된 값SQL
1
249SELECT * FROM customer WHERE custid=:var
6380

$value를 :var 에 바인딩 PHP 코드 예제가 다음과 같습니다:

OCIParse($conn, 'SELECT * FROM customer WHERE custid=:var'); 
OciBindByName($stmt, ":var", $value,32); 
# 32 is an arbitrary large size $value = 1; 
OCIExecute($stmt); 
DisplayResults($stmt); 
$value = 249; 
OCIExecute($stmt); 
DisplayResults($stmt); 
$value = 6380; OCIExecute($stmt); 
DisplayResults($stmt); 

바인딩은 “SQL injection” 공격의 피해를 줄일 수 있다는 또 다른 장점이 있습니다 (공격자들은 SQL 구문을 마음대로 조작하기 어렵게 됩니다).

Cursor sharing. 바인딩을 사용하지 않는 SQL 구문이 이미 많이 사용되고 있다면, Oracle8 이후 버전에서 제공되는 CURSOR_SHARING 매개변수를 사용하여 컴파일 과정에 수반되는 오버헤드를 줄일 수 있습니다. 아래 SQL 구문을 실행하는 경우 오라클은 모든 쿼리를 바인드 변수를 사용하도록 재작성합니다:

ALTER SESSION SET CURSOR_SHARING='FORCE' 

따라서 아래 구문은 파서(parser)에 전달되기 전에 바인딩이 가능한 구문으로 자동 변환됩니다:

변경 전: SELECT * FROM customer WHERE custid=6380 
변경 후: SELECT * FROM customer WHERE custid=:SYS_B_0 

CURSOR_SHARING을 이용하면 실행 계획의 공유 및 재활용 수준을 개선할 수 있습니다. 하지만 그 효과는 제한적입니다. 각 구문 별로 직접 바인드 변수를 적용하는 것이 더 효과적입니다. 또 SQL injection 공격의 방지 효과를 기대할 수 없다는 문제도 있습니다.

Oracle9i 이후 버전에서는 CURSOR_SHARING='SIMILAR'를 활용할 수 있습니다. 이 매개변수는 보다 지능적인 알고리즘을 사용하며, 바인드 변수의 적용여부를 결정하기 전에 테이블 통계정보를 참고합니다.

ALTER SESSION 명령은 현재 세션에만 영향을 미칩니다. 따라서 데이타베이스에 연결을 설정할 때마다 다시 호출되어야 합니다. 영구적으로 변경 사항을 적용하려면 DBA에게 요청하여 데이타베이스 매개변수 파일(init.ora 또는 spfile)을 변경해야 합니다.

다음 섹션에서는, 벤치마크 테스트를 통해 바인드 변수 적용에 따른 성능의 차이를 비교해 보도록 하겠습니다. 예제로 사용된 INSERT 구문에서 100% 이상의 성능 개선이 있었음을 확인하실 수 있을 것입니다. 

오라클에서 10g 다운로드를 중단했다...


10g 서버를 깔아야하는 일이 발생했는데 당췌 구할수가 없었는데


마지막으로 들어간 블로그에서 결국 발견했다.


ORACLE 11G - 32, 64 bits database, client download

ORACLE 10G - 32, 64 bits database, client download

ORACLE 9i - client download

ORACLE 8i - client download


Download Link

http://smartuu.blogspot.kr/2013/04/download-oracle-all-version-10g-64bit.html


이곳도 결국 막혔네요.

테이블 명세 쿼리.sql

출처는 까먹음.... -_-

혹시 자료주인이 보신다면 댓글 부탁드립니다.

위 쿼리는 원본이고 위 쿼리에 nchar, nvarchar 컬럼은 길이가 2배로 나오는 버그가 있다.

아래 쿼리는 버그를 수정하고 mssql2000 쿼리는 삭제해놓은 쿼리.



<권한 생성>
grant all privileges on 디비명.테이블명 to 사용자@'접속지 주소' identified by '암호' (with grant option);
'with grant option'는 grant 명령을 사용할 수 있는 권한까지 부여하는 것으로 보통 사용할 필요 없음.

# root 사용자가 암호 pass1 으로 localhost 에서 db1 디비의 table1에 접속하도록 허용
grant all privileges on root.db1 to root@'localhost' identified by 'pass1';

# root 사용자가 암호 pass1 으로 111.222.333.444 에서 db1 디비의 모든 테이블에 접속하도록 허용
grant all privileges on root.* to root@'111.222.333.444' identified by 'pass1';

# root 사용자가 암호 pass1 으로 111.222.333.0/24 에서 모든 디비와 테이블에 접속하도록 허용
grant all privileges on *.* to root@'111.222.333.%' identified by 'pass1';
 
# root 사용자가 암호 pass1 으로 모든 컴퓨터에서 모든 디비와 테이블에 접속하도록 허용
grant all privileges on *.* to root identified by 'pass1';

<권한 제거>
revoke all on 디비명.테이블명 from 사용자;

Mysql 사용자 계정 생성

 

Mysql 처음 사용하는 초보자분들이 혼동하시는 부분이 사용자 계정 생성부분입니다.

 

우선 Mysql 설치하시게 되면 root 계정은 리눅스 계정과 같습니다.

 

1. Mysql 사용자계정 추가

 

우선 간단히 Mysql 계정 생성방법에 대해서 설명드리겠습니다.

 

Mysql DB서버에는 여러 Database가 존재할것이고 또 많은 User가 존재할겁니다.

 

예를 들자면

 

User에는  최상위 유저인  Root , 일반유저인 mis13, test   존재한다고 보고

 

Database에는 Mis13, Testdb, Database가 존재합니다.

 

아래 그림을 보시면 Root는 3개의 DB에 접속할 권한을 모두 가지고 있고

 

mis13 유저는 Mis13이란 DB만, test 유저는 TestDB, Databases 두 DB 접속할 권한이 있습니다.

 

자 그럼 mis13, test의 유저들의 권한을 줘보겠습니다.

 

우선 사용자를 생성하기 위해서 Root 권한으로 Mysql에 접속을 합니다.

 

#> Mysql  -u root -p

Enter Password : ######

 

mis13 이란 유저가 Mis13이란 DB의 권한을 가지게 해보겠습니다. (암호는 mis1313)

 

mysql> GRANT ALL PRIVILEGES ON Mis13.* to mis13@localhost IDENTIFIED BY 'mis1313' WITH GRANT OPTION;

 

mysql> flush privileges;

 

작성해주시면 끝... mis13으로 접속을 하시면 Mis13이란 DB만 보이실겁니다.

 

2. 그 밖에 외부에서도 접속하기, 모든 DB권한 주기

 

어떤 DB도 접속 가능하게 권한을 주는것은

mysql> GRANT ALL PRIVILEGES on *.*  to mis13@localhost IDENTIFIED BY 'mis1313' WITH GRANT OPTION;

mysql> flush privileges;

 

슈퍼유저로 (즉 root 권한으로) 권한을 주는것은

mysql> GRANT ALL PRIVILEGES on  *.*  to mis13@"%" IDENTIFIED BY 'mis1313' WITH GRANT OPTION;

mysql > flush privileges;

 

GRANT 명령어 말고 Mysql DB 안에 User, DB 테이블에 넣는 법도 있지만 초보분이 사용하시기엔

이 방법이 편하실겁니다.

 

3. 모든 권한이 아닌 부분별 골라서 주기

 

예) mis13 이라는 계정이 전체 데이터베이스에 대해서 select, insert, delete, update 권한만 주고 싶을 경우

mysql>GRANT select, insert, delete, update on *.* to mis13@localhost IDENTIFIED BY 'mis1313' WITH GRANT OPTION;

mysql> flush privileges;

 

예) mis13 이라는 계정이 Mis13 이라는 데이터베이스에 대해서 select, insert, update 권한만 주고 싶을 경우

mysql>GRANT select, insert, update on Mis13.* to mis13@localhost IDENTIFIED BY 'mis1313' WITH GRANT OPTION;

mysql>flush privileges;

 

PS - 함부로 권한을 주진 맙시다.

 


-- SQL Server 2005 이상에서사용할수있습니다.

-- DELETE문을사용하므로대용량테이블이존재한다면이스크립트는부적합할수있습니다.

-- Ctrl-Shift-M을눌러데이터베이스이름과삭제하지않을테이블을설정한후실행합니다.

 

USE <Database Name,sysname,>;

GO

 

SET NOCOUNT ON;

GO

 

DECLARE

    @nvcStmt nvarchar(max),

    @nvcIgnoreTables nvarchar(max);

 

DECLARE @tblTargetTables table (

    [object_id] int NOT NULL PRIMARY KEY,

    schemaName sysname NOT NULL,

    tableName sysname NOT NULL

);

 

DECLARE @tblIdentityTables table (

    tableName sysname NOT NULL,

    seed_value int NOT NULL,

    increment_value int NOT NULL,

    last_value int NULL

);

 

SET @nvcIgnoreTables = N'<삭제제외테이블을comma를구분자로나열,,>';

 

-- 삭제대상테이블정보를수집합니다.

INSERT @tblTargetTables ([object_id], schemaName, tableName)

SELECT T.[object_id], S.name, T.name

FROM sys.tables T

    INNER JOIN sys.schemas S ON T.[schema_id] = S.[schema_id]

WHERE T.type = 'U' AND NOT EXISTS (

    SELECT *

    FROM (

        SELECT LTRIM(RTRIM(SUBSTRING(

              N',' + @nvcIgnoreTables + N','

            , number + 1

            , CHARINDEX(N',', N',' + @nvcIgnoreTables + N',', number + 1) - number - 1

        ))) AS tableName

        FROM master.dbo.spt_values

        WHERE [type] = 'P' AND number < (DATALENGTH(N',' + @nvcIgnoreTables + N',') / 2)

            AND SUBSTRING(N',' + @nvcIgnoreTables + N',', number, 1) = N','

    ) S

    WHERE tableName = T.name

);

 

-- 삭제대상테이블의identity 속성컬럼정보를수집합니다.

INSERT @tblIdentityTables (tableName, seed_value, increment_value, last_value)

SELECT T.tableName, CAST(I.seed_value AS int), CAST(I.increment_value AS int)

    , CAST(I.last_value AS int)

FROM @tblTargetTables T

    INNER JOIN sys.identity_columns I ON T.[object_id] = I.[object_id];

 

-- CONSTRAINTTRIGGER를비활성화하는구문생성

SET @nvcStmt = N'DISABLE TRIGGER ALL ON DATABASE;

EXEC sp_MSForEachTable ''ALTER TABLE ? NOCHECK CONSTRAINT ALL'';

EXEC sp_MSForEachTable ''ALTER TABLE ? DISABLE TRIGGER ALL'';';

 

-- 테이블DELETE 구문생성

SELECT @nvcStmt = @nvcStmt + NCHAR(13) + NCHAR(10) +

    N'DELETE ' + QUOTENAME(schemaName) + N'.' + QUOTENAME(tableName) + N';'

FROM @tblTargetTables;

 

-- identity 속성의컬럼을초기화하는구문생성

SELECT @nvcStmt = @nvcStmt + NCHAR(13) + NCHAR(10) +

    N'DBCC CHECKIDENT(''' + tableName + ''', RESEED, ' +

    CASE

        WHEN last_value IS NULL THEN CAST(seed_value AS nvarchar(10))

        ELSE CAST(seed_value - increment_value AS nvarchar(10))

    END + N');'

FROM @tblIdentityTables;

 

SET @nvcStmt = @nvcStmt + '

EXEC sp_MSForEachTable ''ALTER TABLE ? WITH CHECK CHECK CONSTRAINT ALL'';

EXEC sp_MSForEachTable ''ALTER TABLE ? ENABLE TRIGGER ALL'';

ENABLE TRIGGER ALL ON DATABASE;';

 

-- PRINT @nvcStmt;

 

EXEC sp_executesql @nvcStmt;



출처 : http://youngsam.kr/696


공용 테이블 식
John Papa

코드 다운로드 위치: DataPoints2007_10.exe (150 KB)
Browse the Code Online
개발자가 작업 중인 프로젝트 중에는 기본적인 SELECT/FROM/WHERE 문의 유형을 벗어난 복잡한 SQL 문 작성을 포함하는 것이 많습니다. 이러한 시나리오 중 하나로 FROM 절 내에서 인라인 뷰라고도 하는 파생 테이블을 사용하는 Transact-SQL(T-SQL) 쿼리를 작성하는 예가 있습니다. 개발자는 이러한 일반적인 방법으로 하나의 SELECT 문에서 행 집합을 얻은 다음, 즉시 이 행 집합을 다른 테이블, 뷰 및 사용자 정의 함수에 조인할 수 있습니다. 다른 옵션은 파생 테이블 대신 뷰를 사용하는 것입니다. 이러한 옵션은 각기 장단점이 있습니다.
SQL Server™ 2005를 사용하는 경우 필자는 CTE(공용 테이블 식)이라는 세 번째 옵션을 선호합니다. CTE를 사용하면 성능을 저해하지 않고도 코드 가독성과 관리 용이성을 개선할 수 있습니다. 또한 SQL Server 이전 버전에 비해 T-SQL로 재귀 코드를 작성하기가 훨씬 쉬워졌습니다.
이번 달 칼럼에서는 CTE를 사용하여 일반적인 개발 시나리오를 해결하는 데 초점을 맞추겠습니다. 먼저 CTE의 작동 방식과 CTE를 사용하여 해결할 수 있는 시나리오를 설명하겠습니다. 그런 다음 파생 테이블, 뷰 및 사용자 지정 프로시저와 같은 기존 T-SQL 구문에 비해 CTE를 사용할 때의 장점을 알아볼 것입니다. 칼럼 전반에서 예를 제공하고 이러한 예를 어디에 어떻게 사용해야 하는지 설명할 것입니다. 또한 CTE가 재귀 논리를 처리하는 방법을 설명하고 재귀적 CTE가 작동하는 방법을 정의할 것입니다. 이 칼럼에서 설명하는 모든 코드는 MSDN® Magazine 웹 사이트에서 다운로드할 수 있으며, SQL Server 2005와 함께 제공되는 Northwind 및 AdventureWorks 데이터베이스를 사용합니다.

뷰, 파생 테이블 및 CTE
CTE는 쿼리가 데이터베이스 내에 테이블로 존재하지 않는 데이터 집합에서 선택해야 하는 경우에 유용합니다. 예를 들어 고객 및 고객 주문을 바탕으로 값을 계산하는 집계된 데이터의 집합에 대해 쿼리를 작성하기를 원할 수 있습니다. 집계된 데이터를 Customers, Orders 및 Order Details 테이블을 함께 조인하고 합계를 계산한 다음 주문의 평균값을 계산할 수 있습니다. 그리고 집계된 행 집합에 대해 쿼리를 실행할 수 있습니다. 한 가지 솔루션은 먼저 집계된 데이터를 수집하는 뷰를 만든 다음 이 뷰를 대상으로 작업할 쿼리를 작성하는 것입니다. 다른 옵션은 파생 테이블을 사용하여 집계된 데이터에 대해 쿼리를 수행하는 것입니다. 이를 위해서 SQL 문을 FROM 절로 옮기고 이에 대해 쿼리를 수행할 수 있습니다.
일반적으로 뷰는 큰 쿼리를 나누어 알아보기 방법으로 쿼리를 수행하기 위해 사용됩니다. 예를 들어 테이블 10개를 조인하고, 수십 개의 열을 선택하며, 연관된 논리 집합을 바탕으로 열을 필터링하는 SELECT 문을 뷰로 나타낼 수 있습니다. 이러한 뷰는 데이터베이스 전체에 걸쳐 다른 SELECT 문에서 쿼리할 수 있습니다. 추상화를 통해 뷰가 나타내는 행 집합에 대한 손쉬운 액세스가 제공되며 데이터를 복제하거나 임시 테이블에 저장할 필요도 없습니다.
뷰는 사용 권한이 허락한다는 가정하에 데이터베이스 전체에 걸쳐 다시 사용할 수 있습니다. 예를 들어 그림 1에서는 뷰를 만든 다음 다른 T-SQL 문에서 다시 사용하고 있습니다. 그러나 데이터를 수집하고 한 번만 사용하려는 경우에는 뷰가 최적의 솔루션이 아닐 수 있습니다. 뷰는 데이터베이스에 존재하며 모든 일괄 처리에서 사용할 수 있는 데이터베이스 개체이므로 단일 T-SQL 일괄 처리에서만 사용할 뷰를 만드는 것은 과잉 처리입니다.
CREATE VIEW vwMyView AS
SELECT
    EmployeeID, COUNT(*) AS NumOrders,  MAX(OrderDate) AS MaxDate
FROM Orders
GROUP BY EmployeeID
GO

SELECT 
    e.EmployeeID, oe.NumOrders, oe.MaxDate, e.ReportsTo AS ManagerID, 
    om.NumOrders, om.MaxDate
FROM 
    Employees AS e
    INNER JOIN vwMyView AS oe ON e.EmployeeID = oe.EmployeeID
    INNER JOIN vwMyView AS om ON e.ReportsTo = om.EmployeeID

인라인 뷰라고 하는 파생 테이블을 만드는 다른 옵션이 있습니다. 파생 테이블을 만들려면 간단히 SELECT 문을 괄호로 감싸고 FROM 절 내부로 옮기면 됩니다. 이렇게 생성된 파생 테이블은 쿼리의 대상으로 사용하거나 테이블 또는 뷰와 마찬가지로 조인할 수 있습니다. 그림 2에서는 뷰가 아닌 파생 테이블을 사용하여 그림 1에서 해결한 것과 동일한 쿼리를 해결하고 있습니다. 파생 테이블은 이를 만드는 문 내에서만 액세스할 수 있으므로 일반적으로 쿼리를 읽고 유지 관리하기 어렵게 만듭니다. 이러한 문제는 동일한 일괄 처리 내에서 파생 테이블을 여러 번 사용하려는 경우에 가중됩니다. 다시 사용할 파생 테이블을 여러 번 복사하고 붙여 넣어야 하기 때문입니다.
SELECT 
    e.EmployeeID, oe.NumOrders, oe.MaxDate,  e.ReportsTo AS ManagerID, 
    om.NumOrders, om.MaxDate
FROM 
    Employees AS e
    INNER JOIN 
        (SELECT EmployeeID, COUNT(*), MAX(OrderDate)
         FROM Orders
         GROUP BY EmployeeID) AS oe(EmployeeID, NumOrders, MaxDate)
        ON e.EmployeeID = oe.EmployeeID
    LEFT JOIN 
        (SELECT EmployeeID, COUNT(*), MAX(OrderDate)
         FROM Orders
         GROUP BY EmployeeID) AS om(EmployeeID, NumOrders, MaxDate)
        ON e.ReportsTo = om.EmployeeID

CTE는 뷰와 비슷하게 T-SQL을 훨씬 읽기 쉽게 만들어 주므로 이러한 시나리오에 잘 맞습니다. 또한 동일한 일괄 처리 내에서 곧 이은 후속 쿼리에서 다시 사용할 수도 있습니다. 물론 이 범위를 벗어나면 사용할 수 없게 됩니다. 또한 CTE는 언어 수준 구문이므로 SQL Server가 내부적으로 임시 또는 가상 테이블을 만들 필요가 없습니다. CTE의 기본 쿼리는 곧 이은 후속 쿼리에서 참조될 때마다 호출됩니다.
앞서 살펴본 것과 동일한 시나리오를 그림 3과 같이 CTE를 사용하여 작성할 수 있습니다. 집계된 데이터를 수집하는 EmpOrdersCTE는 CTE 바로 다음에 있는 쿼리에서 사용됩니다. CTE를 사용하는 그림 3의 코드는 쿼리를 매우 읽기 쉽게 만들어 주면서도 메타데이터를 저장하기 위한 시스템 개체를 만들지 않습니다.
;WITH EmpOrdersCTE (EmployeeID, NumOrders, MaxDate) AS
(
  SELECT EmployeeID, COUNT(*), MAX(OrderDate)
  FROM Orders
  GROUP BY EmployeeID
)

SELECT 
    e.EmployeeID,  oe.NumOrders, oe.MaxDate,
    e.ReportsTo AS ManagerID,  om.NumOrders, om.MaxDate
FROM 
    Employees AS e
    INNER JOIN EmpOrdersCTE oe ON e.EmployeeID = oe.EmployeeID
    LEFT JOIN EmpOrdersCTE om ON e.ReportsTo = om.EmployeeID


CTE의 구조
다음은 간단한 CTE 예를 통해서 CTE를 작성하는 방법을 살펴보겠습니다. CTE는 WITH 키워드로 시작됩니다. 그러나 CTE가 일괄 처리의 첫 번째 문이 아닌 경우에는 WITH 키워드 앞에 세미콜론을 붙여야 합니다. 필자는 최선의 방법으로 모든 CTE 앞에 세미콜론을 붙이는 것을 선호합니다. 세미콜론이 필요한지 기억하는 것보다는 이렇게 하는 것이 훨씬 일관성이 있습니다.
WITH 키워드 다음에는 CTE의 이름을 지정하고 그 다음에는 선택적인 열 별칭 목록을 지정합니다. 열 별칭은 CTE 내의 SELECT 문에서 반환하는 열에 해당합니다. 선택적인 열 별칭 다음에는 필수 항목인 AS 키워드를 지정합니다. 그리고 AS 키워드 다음에는 CTE를 정의하는 쿼리 식을 괄호로 감싸고 지정합니다.
다음 예를 살펴보겠습니다.
;WITH myCTE (CustID, Co) AS
(
  SELECT CustomerID, CompanyName FROM Customers
)
SELECT CustID, Co FROM myCTE
CustomerID와 CompanyName열에는 CustID와 Co라는 별칭을 지정했으며, 곧이어 오는 CTE는 해당 열 별칭을 사용하여 CTE를 참조하는 SELECT 문입니다.

CTE의 이해
CTE 설계를 시작하기 전에 CTE가 작동하는 방식과 어떤 규칙을 따라야 하는지 이해해야 합니다. 이 섹션에서는 CTE를 사용할 수 있는 용도와 함께 CTE 내부에서 수행할 수 있는 작업과 그렇지 못한 작업에 대해 살펴보겠습니다. 우선 CTE는 T-SQL 일괄 처리, 사용자 정의 함수, 저장 프로시저, 트리고 및 뷰 내에서 만들고 사용할 수 있습니다.
CTE 바로 다음에 오는 문에서만 CTE를 참조할 수 있습니다. 이것은 CTE를 사용하려면 T-SQL 일괄 처리에서 CTE 다음에 즉시 CTE를 참조하는 쿼리를 작성해야 한다는 것을 의미합니다. 다름 들어 다음 일괄 처리는 오류가 발생합니다.
;WITH myCTE (CustID, Co) AS
(
  SELECT CustomerID, CompanyName FROM Customers
)
SELECT CompanyName FROM Customers WHERE CustomerID = 'ALFKI'
SELECT CustID, Co FROM myCTE
이 코드에서 myCTE는 바로 다음에 있는 첫 번째 쿼리에서만 사용할 수 있습니다. 두 번째 쿼리에서 myCTE를 참조하면 CTE는 범위를 벗어나며 "개체 이름 'myCTE'이(가) 잘못되었습니다."라는 예외가 발생합니다.
CTE는 어떤 방식으로든 데이터를 다시 처리하는 다른 쿼리에서 참조하기 위한 목적으로 사용되므로 CTE의 쿼리는 ORDER나 COMPUTE와 같은 문을 포함할 수 없습니다. 그러나 FOR XML과 같은 복잡한 문은 CTE를 정의하고 CTE에서 작동하도록 사용할 수 있습니다. 예를 들어 다음과 같이 FOR XML 절을 사용하여 CTE를 쿼리하고 해당 결과를 반환할 수 있습니다.
;WITH myCTE AS
(
  SELECT c.CustomerID, c.CompanyName, o.OrderID, o.OrderDate
  FROM Customers c 
  INNER JOIN Orders o ON c.CustomerID = o.CustomerID
)

SELECT CustomerID, CompanyName, OrderID, OrderDate 
FROM myCTE FOR XML AUTO
일단 CTE를 정의한 뒤에는 다음에 오는 첫 번째 쿼리에서 이를 여러 번 참조할 수 있습니다. 이러한 특성은 쿼리가 CTE를 두 번 이상 참조해야 하는 경우 특히 유용합니다. 그림 3의 코드 샘플에서는 쿼리에서 EmpOrdersCTE를 두 번 참조하여 직원과 해당 직원의 관리자를 얻는 방법을 보여 줍니다. 쿼리를 복제하는 것보다는 CTE를 두 번 참조하는 것이 더 간단하므로 이러한 방법은 동일한 행 집합을 두 번 이상 참조해야 하는 경우 매우 유용합니다.
CTE를 SELECT 문에서만 사용해야 하는 것은 아니며 CTE가 생성하는 행 집합을 참조하는 어떤 문에서나 CTE를 사용할 수 있습니다. 이것은 CTE 다음에 CTE를 사용하는 SELECT, INSERT, UPDATE 또는 DELETE 문이 올 수 있음을 의미합니다. CTE를 사용하는 쿼리에 전진 전용 및 스냅샷 커서를 사용할 수도 있습니다.
또는 CTE 다음에 다른 CTE가 오는 것도 가능합니다. 이 기술은 중간 결과를 행 집합에 수집하려는 경우 CTE로 다른 CTE를 만드는 데 사용할 수 있습니다. 다른 CTE로 구성된 CTE를 만들 때는 CTE 정의를 쉼표로 분리합니다.
그림 4에서는 직원의 목록과 각 직원별 전체 주문 횟수를 수집하는 EmpOrdersCTE를 정의합니다. MinMaxOrdersCTE라는 두 번째 CTE는 첫 번째 EmpOrdersCTE를 쿼리하고 행 집합에 집계 함수를 수행하여 직원의 평균, 최소 및 최대 주문 횟수를 확인합니다.
;WITH 
EmpOrdersCTE (EmployeeID, NumOrders)
AS
(
  SELECT EmployeeID, COUNT(*)
  FROM Orders
  GROUP BY EmployeeID
),
MinMaxOrdersCTE (Mn, Mx, Diff)
AS
(
  SELECT MIN(NumOrders), MAX(NumOrders), AVG(NumOrders)
  FROM EmpOrdersCTE
)
SELECT Mn, Mx,    Diff
FROM MinMaxOrdersCTE

WITH 키워드 다음 쉼표로 분리하여 여러 개의 CTE를 정의할 수 있습니다. 이 경우 각 CTE는 다음 CTE에서 참조되어 새로운 CTE를 구성합니다. CTE 정의 다음에 오는 DML(데이터 조작 언어) 문에서는 WITH 절 내에 정의된 모든 CTE를 참조할 수 있습니다.

재귀의 규칙
재귀 알고리즘을 구현하는 데도 CTE를 사용할 수 있습니다. 재귀 논리는 스스로를 호출하는 알고리즘을 작성해야 할 때 유용하며, 데이터의 중첩된 집합을 탐색하는 데 자주 사용됩니다. T-SQL과 같은 언어에서 재귀 논리를 작성하기는 특히 까다롭지만 CTE를 설계한 목적 중 하나는 바로 이러한 재귀 논리를 해결하기 위한 것이었습니다. 재귀 CTE를 작성하는 기본 공식은 다음과 같습니다.
  1. 최상위 수준(앵커 멤버)을 반환하는 쿼리를 작성합니다.
  2. 재귀 쿼리(재귀 멤버)를 작성합니다.
  3. 첫 번째 쿼리에 재귀 번째 쿼리로 UNION을 수행합니다.
  4. 행이 반환되지 않는 경우에 대비합니다. 이것이 종료 검사입니다.
다음은 재귀 CTE의 예입니다.
;WITH myRecursiveCTE(col1, col2, ... coln) AS
( 
  -- Anchor Member Query
  UNION ALL
  -- Recursive Member Query that references myRecursiveCTE
)
CTE를 포함하지 않는 사용자 지정 재귀 프로시저를 작성할 때는 반드시 명시적 종료 절을 추가해야 합니다. 종료 절은 최종적으로는 재귀 알고리즘을 종료하고 재귀 호출 스택을 원상 복귀하는 임무를 담당합니다. 이러한 절이 없으면 코드에 무한 루프가 발생합니다.
CTE에는 종료 절의 처리를 도울 수 있는 두 가지 측면이 있습니다. 첫 번째는 재귀 멤버가 레코드 0개를 반환하는 경우에 해당하는 암시적인 종료 절입니다. 이러한 경우 재귀 멤버 쿼리는 CTE를 재귀적으로 호출하지 않고 호출 스택을 원상 복귀합니다. 두 번째는 명시적으로 MAXRECURSION 수준을 설정할 수 있다는 것입니다.
MAXRECURSION 수준은 CTE를 포함하는 일괄 처리 내에서 명시적으로 설정하거나 서버 쪽 설정을 통해 설정할 수 있습니다. 서버 차원 설정의 기본값은 변경하지 않은 경우 100입니다. 이 설정은 CTE가 자신을 재귀적으로 호출할 수 있는 횟수를 제한합니다. 한계에 다다르면 예외가 발생합니다. MAXRECURSION 수준을 설정하는 구문은 다음과 같이 CTE 다음의 SELECT 문에서 OPTION 절을 사용하는 것입니다.
-- DEFINE YOUR CTE HERE
SELECT * FROM EmpCTE
OPTION (MAXRECURSION 7)
이 밖에도 재귀 CTE를 설계할 때 염두에 두어야 할 몇 가지 다른 규칙이 있습니다. 재귀 CTE는 앵커 멤버와 재귀 멤버를 모두 포함해야 합니다. 두 멤버에는 같은 수의 열이 있어야 하며 두 멤버에 속한 열은 데이터 형식이 일치해야 합니다. 재귀 멤버는 CTE를 한 번만 참조할 수 있으며 멤버에 다음 절이나 키워드를 사용할 수 없습니다.
  • SELECT DISTINCT
  • GROUP BY
  • HAVING
  • TOP
  • LEFT/RIGHT OUTER JOIN

간단한 재귀 연습
데이터와 행 집합의 관점에서 재귀는 동일한 데이터 집합에 대해 다른 조건을 적용하여 동일한 논리를 반복적으로 수행해야 하는 문제를 해결하는 데 사용됩니다. 예를 들어 모든 판매 사원을 검색하고 해당 사원의 관리자를 찾아 계층 순서대로 데이터를 반환해야 한다고 가정해 보겠습니다. 그림 5에서는 재귀를 사용하여 영업 부사장 밑에서 일하는 직원의 목록을 수집하는 CTE를 사용하는 솔루션을 보여 주고 있습니다.
;WITH EmpCTE(EmployeeID, EmployeeFirstName,
 EmployeeLastName, MgrID, SalesLevel)
AS
( 
  -- Anchor Member 
  SELECT EmployeeID, FirstName, LastName, ReportsTo, 0
  FROM Employees
  WHERE EmployeeID = 2 -- Start with the VP of Sales

  UNION ALL
  
  -- Recursive Member 
  SELECT 
    e.EmployeeID, e.FirstName, e.LastName, e.ReportsTo, m.SalesLevel+1
  FROM 
    Employees AS e
    INNER JOIN EmpCTE m ON e.ReportsTo = m.EmployeeID
)

 -- Using the CTE
SELECT EmployeeID, EmployeeFirstName, EmployeeLastName, 
       MgrID, SalesLevel  
FROM EmpCTE

몇 가지 추가적인 측면을 제외하고 그림 5에 있는 재귀 CTE는 표준 CTE와 매우 비슷합니다. 표준 CTE는 행 집합을 정의하는 쿼리 한 개를 포함하는데 반해, 재귀 CTE는 두 개의 쿼리 정의를 정의합니다. 첫 번째 쿼리 정의인 앵커 멤버는 CTE가 호출될 때 실행될 쿼리를 정의합니다. 두 번째 쿼리 정의인 재귀 멤버는 앵커 멤버와 동일한 열과 데이터 형식을 반환하는 쿼리를 정의합니다. 재귀 멤버는 또한 재귀적으로 CTE에 콜백을 수행하는 데 사용할 값을 검색합니다. 쿼리의 결과는 UNION 문을 사용하여 함께 가져오게 됩니다.
그림 5의 EmpCTE는 영업 부사장(EmployeeID = 2)에 대한 직원 레코드를 가져오는 앵커 멤버를 보여 줍니다. 앵커 멤버 쿼리의 마지막 열은 계층의 0번째 수준, 즉 최상위를 나타내는 0 값을 반환합니다. 재귀 멤버의 쿼리는 이전 직원 밑에서 근무하는 직원의 목록을 얻습니다. 이를 위해 Employees 테이블을 EmpCTE에 조인합니다.
재귀 멤버에서도 동일한 열이 검색되지만 SalesLevel 열은 현재 직원의 관리자를 얻고, 관리자의 SalesLevel을 얻은 다음, 이를 1만큼 증가시켜서 계산됩니다. m.SalesLevel+1 식은 우리의 앵커 멤버로부터 검색한 영업 부사장 바로 밑에서 근무하는 모든 직원에게 SalesLevel 1 값을 할당합니다. 이러한 직원 밑에서 일하는 모든 직원은 SalesLevel 2 값을 가집니다. SalesLevel 값은 영업 조직 계층의 이어지는 각 수준에서 이와 같이 점차적으로 증가합니다.

요약
쿼리 내에서 복잡한 파생 테이블을 사용하거나 T-SQL 일괄 처리 외부에 정의가 있는 뷰를 참조하는 것이 비하면 CTE는 T-SQL을 훨씬 읽기 쉽게 작성하는 방법을 제공합니다. CTE는 또한 재귀 알고리즘을 사용하는 데 따르는 어려움을 완화하는 데 도움을 주는 훨씬 개선된 도구를 제공합니다. 표준 CTE 또는 재귀 CTE 중 어떤 것을 사용하는지에 관계없이 CTE를 통해 여러 일반적인 개발 시나리오의 문제를 해결하고 성능을 저하시키지 않고도 가독성을 높일 수 있게 될 것입니다.

John에게 질문이나 의견이 있으면 다음 전자 메일 주소로 보내시기 바랍니다: mmdata@microsoft.com.


John Papa는 ASPSOFT(aspsoft.com)의 선임 .NET 컨설턴트로, 여름이면 밤마다 애견 Kadi를 데리고 가족과 함께 양키스 팀을 열광적으로 응원하는 야구광입니다. John은 C# MVP이며 ADO, XML 및 SQL Server에 대한 여러 권의 책을 저술했습니다. 그는 VSLive와 같은 업계 회의에서 자주 강연하며 codebetter.com/blogs/john.papa라는 블로그를 운영하고 있습니다.


출처 : http://msdn.microsoft.com/ko-kr/magazine/cc163346.aspx
관련 MSDN : http://msdn.microsoft.com/ko-kr/library/ms175972.aspx

해당 데이터베이스에 연결된 사용자가 있기 때문에 발생하는 오류

 실제 현재로 사용하고 있는 사용자가 있는 상태에서 데이터베이스 복원을 진행하기 위해서는

ALTER DATABASE 데이터베이스명
SET SINGLE_USER WITH ROLLBACK IMMEDIATE

명령을 사용해서 모든 사용자에 대한 연결을 끊은 상태에서 진행하고

작업을 마친후

ALTER DATABASE DB명
SET MULTI_USER

로 되돌린다.

출처 : 네이버 지식인
http://kin.naver.com/qna/detail.nhn?d1id=1&dirId=10205&docId=122985369&qb=bXNzcWwgMjAwNSDrs7Xsm5A=&enc=utf8&section=kin&rank=7&search_sort=0&spq=0&sp=1&pid=gU300doi5TVssZz4RINsss--097953&sid=TX300UnZfU0AAEzIGnE


<<정규화(Normalization)>>

 

* 함수의 종속성(Functional Dependency)
- 데이터들이 어떤 기준값에 의해 종속되는 현상을 지칭

- 결정자(determinent) --> 종속자(dependent)

  ex> 주민등록번호 --> (이름, 출생지, 주소)

 

 

1. 제1 정규화
- 복수의 속성값을 갖는 속성의 분리
- 즉, 테이블 하나의 컬럼에는 여러 개의 데이터 값이 중복되어 나타나지 않아야 한다.

- 모든 엔티티 타입의 속성에는 하나의 속성 값만을 가지고 있어야 하며 반복되는 속성 값의 집단은 별도의 엔티티 타입으로 분리한다.

- 로우(Row) 단위로 1차 정규화가 안된 모델은 PK의 유일성이 확보되지 않으므로 인해 실전 프로젝트에서는 거의 찾아보기 힘들다.

- 반면 로우 단위로 중복된 내용을 컬럼 단이로 펼쳐 중복하는 경우가 아주 많이 발견되는데 추가 업무 발생시 모델을 변경해야 하는 치명적인 결함이 있다.

 

 

2. 제2 정규화
- 주식별자에 종속적이지 않은 속성 분리
- 주식별자를 구성하는 일부 속성에 종속적인 속성(부분 종속 속성)을 분리하는 것.
- 주식별자가 둘 이상의 컬럼으로 이루어져 있을때 모두에게 종속적이어야 한다.

- 반드시 자신의 테이블을 주식별자를 구성하는 속성이 복합 식별자일 경우에만 대상이 되고 단일 식별자일 경우에는 2차 정규화 대상이 아니다.

- 코드 유형의 엔티티 타입들이 2차 정규화가 되지 않고 하나의 엔티티 타입으로 표현되는 경우가 많다.

 

 

3. 제3 정규화
- 주식별자가 아닌 속성에 종속적인 속성 분리.
- 주식별자에 의해 종속적인 속성 중에서 다시 속성 간에 종속관계(이전 종속)가 발생되는 경우

- 결정자 역할을 하는 일반 속성이 존재하고, 결정자 역할 속성에 의존하는 의존자가 존재하는 엔티티 타입이 대상이다.

 

 

4. 보이스-코드 정규화(BCNF)
- 테이블에 존재하는 식별자가 여러개 존재할 경우 식별자가 중복되어 나타나는 현상을 제거

- 주식별자 속성 중에 주식별자의 유일성을 확보하는 최소한의 속성이 아닌 쓸데없이 추가된 속성을 분리하는 것.

 

 

5. 제 4정규화
- 특정 속성값에 따라 선택적인 속성의 분리

- 하나의 테이블에 두 개 이상의 독립적인 다가속성(multi-valued attribute)이 존재하는 경우에 다가종속(multi-valued dependency)이 발생되어 문제가 생긴다. 다가종속이란 하나의 속성 값에 두개 이상의 의미를 가지는 값을 가지는 것을 의미한다.

- 실제로 2정규화나 BCNF보다 더 많이 발생된다.

- 불필요한 관계때문에 새로운 값을 채울 경우에 값을 기본값(Default value)으로 지정해버리는 경우가 많이 있다. 참조무결성(FK)를 걸지 않는 경우에 가능한데 좋지 않은 경우이며 이와 같은 경우 데이터모델에 나타난 관계가 실제 데이터에서 불가피하게 단절되어 나타나므로 무결성 체크가 불가능해진다.







원래 정규화란 중복성을 최소화하고 정보의 일관성을 보장하기 위한 관계형 데이터 베이스에서 제일 중요한 개념인데요

제1정규화는  < repeating group 제거>


여러 값을 가진 컬럼이 존재할 수 없다. 즉 반복되는 그룹이 존재해서는 안 된다. 각 행과 열에는 하나의 값만이 올수 있다.
예를 들면

고객번호(P.K) 고객명 취미
1 노무현 영화
여행
2 이화숙 등산

이런 테이블이 있을수는 없고 이걸 수정한다면

고객번호(P.K) 고객명
1 노무현
2 이화숙

하고 또하나

고객번호(P.K) 일련번호(P.K) 취미
1 1 영화
1 2 여행
2 1 등산

이런식으로 두개로 분리해야 한다는거죠

제2정규화 < 부분종속 제거 >


모든키가 아닌 컬럼은 기본 키 전체에 의존적이여야 한다. 기본키의 일부분에 의존적이어서는 안 된다.

예를 들어

사번(p.k) 프로젝트번호(p.k) 부서 프로젝트역할 고과율
1 a 전산 팀장 A
1 b 전산 조원 C
1 c 전산 부팀장 B
2 c 경리 팀장 A
3 c 기획 팀장 A

이 테이블의 기본키는 사번과 프로젝트번혼데 부서 컬럼은 사번에만 의존적이다.
이것은 2정규화를 거치면

사번(p.k) 프로젝트번호(p.k) 프로젝트역할 고과율
1 a 팀장 A
1 b 조원 C
1 c 부팀장 B
2 c 팀장 A
3 c 팀장 A

요렇게 하고 부서 테이블을 다시 따로 때네서

사번 부서
1 전산
2 경리
3 기획

요렇게 해서 릴레이션을 걸면 됩니다.

그리고 3정규화 < 이행종속 제거 >


키가아닌 컬럼은, 다른 키가 아닌 컬럼에 의존적일어서는 안된다.
예를 들어

사번(p.k) 프로젝트번호(p.k) 프로젝트역할 고과율
1 a 팀장 A
1 b 조원 C
1 c 부팀장 B
2 c 팀장 A
3 c 팀장 A

요 테이블을 다시 보면 고과율은 프로젝트 역할에 의존적인걸 알수 있습니다.

이것을 또 3정규화 거치면

사번(p.k) 프로젝트번호(p.k) 프로젝트역할
1 a 팀장
1 b 조원
1 c 부팀장
2 c 팀장
3 c 팀장

프로젝트역할 고과율
팀장 A
조원 C
부팀장 B

요렇게 또 따로 때네줘야 합니다

제4정규화는
2정규화 된 테이블은 다대다 관계를 가질수 없다.

이건 따로 예를 안들어도 되겠죠?ㅋㅋ

+ Recent posts