최근 포토로그


환경변수 명령어처리(Setx Path)를 중심으로

원래 환경변수는 아래의 것을 명령프롬프트에서 넣으면 된다.

setx -m JAVA_HOME "d:/jdk"
setx path "%PATH%;%JAVA_HOME%\bin" /m
setx -m ORACLE_HOME "d:\OraClient"
setx -m NLS_LANG "KOREAN_KOREA.KO16MSWIN949"
setx -m TNS_ADMIN  "d:\OraClient"

setx path "%PATH%;%ORACLE_HOME%" /m


그런데 퍽킹 저장이 안되는 거다.

무슨일인데 안되지 하면서 계속 쳐다봤는데...

문자열이 잘린다는 소리였다.

씨붕 환경변수들 보면 mssql이 엄청나게 글자쓰고, 인텔이 엄청써서 보니깐 쓸수 있는 문자열이 1024로 짤리다니...

화딱지나서 

setx -m JAVA_HOME "d:/jdk"
setx /M PATH "%JAVA_HOME%\bin;%PATH%"
setx -m ORACLE_HOME "d:\OraClient"
setx -m NLS_LANG "KOREAN_KOREA.KO16MSWIN949"
setx -m TNS_ADMIN  "d:\OraClient"
setx path "%PATH%;%ORACLE_HOME%" /m

이걸로 바꾸어서 Path 설정했다.

이건 보니까 setX 라는 명령어가 1024 문자열 이상을 지원 안하는 버그(?) 때문이었다.
어쩔수 없이 다음에는 컴터 설치하자마자 패스부터 설정하고 나머지를 깔던지 해야겠다.


자바는 소중하니깐요.


스토어드 프로시저로 table create 명령줄 뽑아내기. MSSQL과 ORACLE

이 sp를 실행하면 테이블 구조의 내용을 script로 뽑아준다.
맨날 mssm의 수정 부분들어가서 script생성 어쩌구 눌러서 만들려면 귀찮다.


결과물은 아래 그림을 참조.



--◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆
--◆              (c) Copyright Brainnet Co..Ltd.  2005
--◆                   All rights reserved
--◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆
--◆ Procedure Name :  SP_Z_GET_TABLE_CREATE
--◆ Program Code   :  SP_Z_GET_TABLE_CREATE
--◆ Description    :  table create 명령줄 뽑아내기.
--◆ Author         :  홍길동
--◆ Date           :  2016. 06. 29.
--◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆
--◆ Modification Log
--◆
--◆ Date            In Charge    Description
--◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆
--◆ 2014. 12. 16.   softone     Initial Release
--◆
--◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆
CREATE PROCEDURE SP_Z_GET_TABLE_CREATE
(
      @P_TABLE_NAME                   nVarchar(100)
)
AS

BEGIN

    DECLARE @SQL TABLE(S VARCHAR(4000), ID INT IDENTITY)


    declare #ZSY_SYS_cur01 cursor for
      SELECT upper(name) o_name
        FROM sysobjects
       WHERE xtype = 'U'
       and name = @P_TABLE_NAME
       order by upper(name)
    OPEN #ZSY_SYS_cur01

    fetch next from #ZSY_SYS_cur01 into
                    @P_TABLE_NAME
    WHILE (@@fetch_status <> -1)
    BEGIN
        INSERT INTO  @SQL(S)
        SELECT 'CREATE TABLE ' + @P_TABLE_NAME + ' ('

        ;WITH AAA AS (
                SELECT OBJECT_NAME(o.parent_object_id) AS TABLE_NAME
                     , o.Name AS      CONSTRAINT_NAME
                     , cost_name.name COLUMN_NAME
                  FROM sys.objects o
                  JOIN sys.schemas s
                    ON o.schema_id = s.schema_id
                  JOIN sys.all_columns cost_name
                    ON o.object_id = cost_name.default_object_id
                   AND o.parent_object_id = cost_name.object_id
                 WHERE 1=1
                   AND o.type = 'D'
                   AND OBJECT_NAME(o.parent_object_id) = @P_TABLE_NAME

        )
        INSERT INTO @SQL(S)
        SELECT CASE WHEN ORDINAL_POSITION = 1 THEN '   ' ELSE ' , ' END
             + A.COLUMN_NAME
             + N' ' + DATA_TYPE +
                                  CASE WHEN CHARACTER_MAXIMUM_LENGTH > 0 THEN '(' + CONVERT(VARCHAR(MAX), CHARACTER_MAXIMUM_LENGTH) + ')'
                                       WHEN NUMERIC_PRECISION > 0 THEN '(' + CONVERT(VARCHAR(MAX), NUMERIC_PRECISION)+ ',' + CONVERT(VARCHAR(MAX), NUMERIC_SCALE) + ')'
                                       WHEN DATETIME_PRECISION > 0 THEN ''
                                  END
             + N' ' + ( CASE WHEN IS_NULLABLE = 'NO' THEN 'NOT ' ELSE '' END ) + 'NULL '
             + N' CONSTRAINT ' + CASE WHEN CONSTRAINT_NAME IS NULL THEN '' ELSE CONSTRAINT_NAME END
             + N' ' + CASE WHEN COLUMN_DEFAULT IS NULL THEN '' ELSE COALESCE('DEFAULT '+COLUMN_DEFAULT,'') END


          FROM INFORMATION_SCHEMA.COLUMNS A
          LEFT
          JOIN AAA B
            ON A.TABLE_NAME  = B.TABLE_NAME
           AND A.COLUMN_NAME = B.COLUMN_NAME
         WHERE A.TABLE_NAME  = @P_TABLE_NAME
         ORDER
            BY A.ORDINAL_POSITION
         ;
        INSERT INTO  @SQL(S)
        SELECT ' )'

        INSERT INTO  @SQL(S)
        SELECT ' ALTER TABLE ' + @P_TABLE_NAME + ' ADD CONSTRAINT ' + ' PK_' + @P_TABLE_NAME + N' PRIMARY KEY ' 
        ;WITH AAA AS (
                SELECT OBJECT_NAME(o.parent_object_id) AS TABLE_NAME
                     , o.Name AS      CONSTRAINT_NAME
                     , cost_name.name COLUMN_NAME
                  FROM sys.objects o
                  JOIN sys.schemas s
                    ON o.schema_id = s.schema_id
                  JOIN sys.all_columns cost_name
                    ON o.object_id = cost_name.default_object_id
                   AND o.parent_object_id = cost_name.object_id
                 WHERE 1=1
                   AND o.type = 'D'
                   AND OBJECT_NAME(o.parent_object_id) = @P_TABLE_NAME

        )
        INSERT INTO  @SQL(S)
         SELECT  CASE WHEN A.column_id = 1 THEN ' ( ' ELSE ' , ' END + INDEX_COL(@P_TABLE_NAME, A.INDEX_ID, A.INDEX_COLUMN_ID ) COL_NM
           FROM  sys.index_columns A
           JOIN  SYSINDEXES B
             ON  A.object_id = B.ID
            AND  B.INDID = A.INDEX_ID
          WHERE  A.object_id = OBJECT_ID( @P_TABLE_NAME )

        INSERT INTO  @SQL(S)
        SELECT ' )'
    fetch next from #ZSY_SYS_cur01 into
                    @P_TABLE_NAME
    END


    CLOSE #ZSY_SYS_cur01
    DEALLOCATE #ZSY_SYS_cur01

    SELECT * FROM @SQL

END

cross apply 셈플 MSSQL과 ORACLE

select * 
  from oqm_qc_category A
 where start_dt in ( select MAX(x.start_dt)
                       from oqm_qc_category x
                      where A.qc_category_cd = x.qc_category_cd )
   --and qc_category_cd = 'M170604542'

SELECT TA.*
FROM oqm_qc_category TA
   CROSS APPLY (SELECT TOP 1 * FROM oqm_qc_category TB
                             WHERE TB.qc_category_cd = TA.qc_category_cd
                              AND TB.start_dt <= TA.start_dt
                              ORDER BY start_dt DESC) TT
--where    TA.qc_category_cd = 'M170604542'


자바 및 오라클 환경설정 한방에 처리하기 MSSQL과 ORACLE

우선 오라클이랑 자바를 환경변수에 일일이 넣고 싶지 않으신 분들을 위하여 이 글을 바친다.


필자의 경우 오라클클라이언트랑 자바클라이언트를 항상 D드라이브에 설치한다.
왜냐면 다시 설치하기가 귀찮기 때문이다.


그래서 포멧하면 다시 환경설정 들어가서 Path치는 것이 너무나 싫다.

즉, 이거 타이핑하기가 귀찮다는 소리.


오라클 클라이언트 설치없이 쓰는 방법은 내 블로그 찾아보면 나올 것이다.
그걸 전제로 이야기를 풀어본다.

환경변수에 들어가서 일일이 클릭하고 치기가 귀찮으시면 cmd창 열어서

아래의 것을 치면 완성됨


setx -m JAVA_HOME "d:/jdk"
setx path "%PATH%;%JAVA_HOME%\bin" /m
setx -m ORACLE_HOME "d:\OraClient"
setx -m NLS_LANG "KOREAN_KOREA.KO16MSWIN949"
setx -m TNS_ADMIN  "d:\OraClient"

setx path "%PATH%;%ORACLE_HOME%" /m

폴더명칭이나 드라이브 명칭만 바꾸면 됩니다.


분산 트렌잭션 DTC, RPC 가 안되는 분들을 위한 트러블슈팅 (Windows Server 2012버전 중심으로) MSSQL과 ORACLE

젝일! 

최근 MS-SQL 두대를 링크드서버(linked-server) 를 통하여 연결한 후
분산트렌젝션을 했다.

그런데 조까튼 문제가 발생했다.

한놈은 Windows Server 2003 이고 
한놈은 Windows Server 2012 였다.


문제는 이 더러븐 놈의 셋팅이 예전에 할때는 잘 되었는데 이번에는 죽어도 안되는 거였다.

즉 서버 2003에서 이런식으로 작성해서 저장하면 




보안구성

요런식으로 넣으면 레지스트리의 
HKLM\SOFTWARE\Microsoft\Rpc\internet 부분에 저장이 된다. (아래화면 참조)

즉, 이 부분을 레지스트리에 넣으나, 프로그램으로 넣으나 같은 결과물을 가진다는 말이다.



그런데, Fucking 2013 서버는 
전혀 다른 메카니즘을 보이고 있었다는 것을 간과했다.


나는 예전에만 분산트렌젝션을 일으켰기 때문에 기억은 2003 서버 기준으로만 생각하고 있었다.

그런데,

일단 화면 부터 퍼킹 헬이다.


포트번호 넣고 MSDTC설정 해볼까? 했는데 

MSDTC가 여기에 없다.
즉, 여기에 있다.




보다 시피 옵션도 존나리 많다.

하지만, 이건 넘어가리라고 생각했다.

근데 Dtcping을 통하여 통신이 일어나지 않았다.

어떤 문서 보니깐 HKLM\SOFTWARE\Microsoft\Rpc 여기에다가 포트 넣으라고 되어있어서
씨붕 넣었다.

안되었다.

다시 internet 폴더에 



넣었다.


이게 무슨 개 삽질일까?


아마 인터넷폴더를 번역하면서 어떤 ㅂㅅ이 internet을 폴더라고 생각하지 않고 RPC폴더에 바로 넣는 것으로 착각했나 보다.



암튼 개삽질이었음.



정리해보자

1. RPC통신을 하려면 두 MSSQL 컴퓨터간에 컴퓨터이름으로 ping을 할수가 있어야 한다.

즉 CMD창(명령프롬프트)에서 ping xxx.xxx.xxx.xxx 가 아닌 A서버의 명칭 
 - 서버명칭은 제어판->시스템 에서 보면 나옵니다. - 

  즉, ping rpc서버1[가칭] 과 ping rpc서버2[가칭] 가 다 먹혀야 한다.
     이게 안먹힌다면, windows\system32\drivers\etc\hosts파일을 열어서 
     xxx.xxx.xxx.xxx  rpc서버1 을 추가하면 됩니다.
서버 재부팅 해주세요.
  
   이렇게 했는데도 안먹히면 분명히 firewall이 막혀있습니다.  
   네트웍 관리자에게 Ping 막혀있는지 물어보세요

2. RPC포트 열려있어야 합니다. 
   135번 포트입니다.
  이게 내부방화벽으로 막혀있을 수도 있고, 


아니면 네트웍 방화벽에도 막혀있을 수 있으니 네트웍 관리자에게 물어보세요


3. RCP가 내부적으로 사용하는 동적포트를 뚫어주어야 합니다.


그건 위에 다 설명되어 있습니다.




이후 DTCping.exe파일을 두 서버에서 동시에 실행한 후에


두 포트가 정상적으로 오는지 보고 통신이 왔다갔다 하는지를 보세요

한군데서 실행한 후에 다른데서 실행하면 두놈다 반응을 일으킵니다.
A서버에서 실행한 후 컴터이름을 치고 Ping을 했는데, B서버에서 반응이 없으면 RPC가 안되는 겁니다.


이게 되면 분산트렌잭션은 거의 100% 실행됩니다.

참조

http://nagid.egloos.com/2986380


오라클에서 MSSQL처럼 TOP 1 을 통하여 UPDATE하기

우선 http://nagid.egloos.com/3017597 이 게시물 참조.


SET SERVEROUTPUT ON;
BEGIN
                MERGE INTO hrap002 m 
                USING (SELECT rid, ROWNUM rn FROM (SELECT ROWID rid FROM hrap002
                                                    where emp_id = '10001'
                                                   ORDER BY appnt_dt desc, SEQ_NO desc)
                                            where ROWNUM = 1       
                                                   ) s 
                ON (m.ROWID = s.rid) 
                WHEN MATCHED THEN 
                UPDATE SET job_title_cd =  '11'
                
                ;   
          
    DBMS_OUTPUT.PUT_LINE( TO_CHAR(SQL%ROWCOUNT) || ' 줄 반영되었어..');
    DBMS_OUTPUT.PUT_LINE( TO_CHAR(etl.get_merge_insert_count) || ' 줄 추가되었어.');
    DBMS_OUTPUT.PUT_LINE( TO_CHAR(etl.get_merge_update_count( SQL%ROWCOUNT ))|| ' 줄 수정되셨어.');
                
END;

요런 방식으로 쓰면 자기가 원하는 방식으로 업데이트를 칠수 있음.
물론 이 방식으로 Insert도 할 수 있음.

최근에 고전 Xcom1 로 OpenXcom 을 해보았다. 게임?

1. OpenXcom은 두가지 버전 모두를 지원하니 이쪽도 해보고 저쪽도 해봐서 좋다.

일단 화면 투척



그지같은 외계인 비행접시 2대나 걸린 스크린샷이다.

이놈들 초반에 너무 강해서 보이자마자 몇방맞고 탱크 빈사상태다.

물론 이판은 내가 보이자마자 후퇴한 미션이다.


암튼 xcom1도 무지막지한 보정을 통해서 예전에 쉽게 끝내신분들도 한번 해보면 힘들거라고 생각한다.


물론 본인은 SuperHuman 에다가 철인모드(ironman - 저장은 자동으로되고 이전버전으로 로딩안됨. )


끝내기는 했다.


오랫만에 한거라서 초반에 태크트리 좀 꼬여서 실패(2번이나... 헉) 를 하기도 했지만 무난하게 끝낸거 같다.

간만에 불태웠음.


어라? 이미지들이 왜 올리면 다 짤리지?


최근에 고전 Xcom2 인 TFTD의 OpenXcom 을 해보았다. 게임?




일단 화면을 한번 보자

1. 일단 전체 해상도가 그래픽카드 지원되는 것 까지 다 지원한다.
   이 말은 즉, 예전에 갑갑한 정도의 화면이 보여줘서 스크롤이 존나 많이 발생했는데 이번 버전은 그래픽카드 지원까지로 크게 볼수있다. 덕분에 그래픽이 깔끔한 것 같은 느낌이 든다.
2. 난이도가 일부 변경되었다.
   본인은 원래 Xcom1은 시시하고 Xcom2는 초인난이도 즉, "Super Human" 이 아니면 안한다.
   그런데, 이번에 그냥 간단하게 생각하고 Super Human으로 했는데 개박살 났다. 그것도 2번이나.

   왜 그런고 하니, 처음에 원래 기술개발이 진행되지 않으면 3개월이내에는 기지로 처들어오지 않았다.
   그런데 이놈들 2개월 되니깐 처들어 온다.
   어떤때는 1개월에 처들어 올때도 있었다.
   아쿠아로이드인 초록색 최고 쉬운 놈 (섹토노이드 같은 놈)들만 처들어 올때는 상관 없는데,
   문제는 잠수복입은 놈과 같이 처들어오면 초반 공격형 무기가 낮을때는 죽일 수가 없다는 점이다.

   3방 4방 맞추면 죽일수 있을 것 같았지만, 데이지가 들쑥날쑥이라서 공격력이 너무 낮으면 공격차체가 무위로 돌아가는 시스템인거 같은데, 문제는 고폭수류탄을 3~4방 터뜨려도 멀쩡하다는 점이다. 물론 다른 것들의 무기도 마찬가지임.

최소한 "Sonic purser" 정도의 외계인 수류탄이나, 외계인무기 정도는 되야 안정적으로 딜을 할수 있는 관계로 기지침공미션에서 전멸했다. 탱크를 많이 모았어도 탱크로 부술수 없었다.
그리고 또한 데미지 보정이 많이 일어나서 예전에는 탱크만 있어도 초반미션은 어느정도 해결되었는데, 이번 버전은 탱크신공으로는 깨기가 불가능한 상황이 벌어졌다. 

암튼 그래서 초반에는 존나 힘들었다.

아~ 그리고 이놈들 이제 문도 잘따고 들어온다. 물론 엄청난 인공지능은 아니지만
적이 우리편 우주선 문을 따고 유도미사일로 우리편 전멸시키는 장면을 내가 목격했다. 와우~

암튼 이래저래 세이브를 통해서 마지막 미션만 남겨놓은 상태.


3. 여러가지 옵션을 통해 인터페이스가 엄청난 개선이 일어났다.
   이건 해보면 알겠네요.



암튼 옛날에 해보신 분들이라면 꼭 해보시고,
최근 버전의 Xcom 시리즈를 즐겨하셨던 분들이시라면 마음 단단히 먹고 해보세요.


SP_HELPTEXT 의 대안 -> 이건 정말 미칠 일이다. MSSQL과 ORACLE

MSSQL의 sp_helptext 는 정말 유용한 기능을 제공하는 SP 이다.

하지만 문제는 이놈이 문자열의 크기가 256자리가 넘어가면 강제로 개행문자를 넣어주는 어처구니가 없는 버그가 있다.


예)

DECLARE @SQL NVARCHAR(MAX);
SET @SQL = 'CREATE PROCEDURE sp_Test_sp_helptext AS SELECT '''
+ REPLICATE('X',280)
+ ''' as [Stored Procedure Select Result];';
EXEC (@SQL);
GO

이렇게 SP 명칭 sp_Test_sp_helptext에 280자리 정도의 X가 들어간 SP를 실행해본다.


보이는 가?

칸이 지멋대로 2줄로 나왔다.

정상적으로 나왔다면



이건 아래와 같이 붙어서 
나와야 한다.


그래서 내가 화딱지 나서 구글신에게 물어서 알아봤다.
그 결과물이 nagid.egloos.com/3024869 이다



그런데, Fucking Asshole !!!!!





왜 모든 개행문자가 다 붙어버리냐?

아우 빡쳐!



테스트한 위한 예시


ALTER PROCEDURE sp_Test_sp_helptext AS SELECT 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' as [Stored Procedure Select Result]

print 'fadsfasd'



print N'요로코롬 개행이 좀 보여야지'
;


이걸 
EXEC sp_helptext2 sp_Test_sp_helptext  
로 실행하면


CREATE PROCEDURE sp_Test_sp_helptext AS SELECT 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' as [Stored Procedure Select Result]
print 'fadsfasd'
print N'요로코롬 개행이 좀 보여야지'
;

이래 나온다.


이걸 어따써?


해서 또 다시 구글링 했다.

그래서 완전판은 아니지만
1행에 4000자 정도까지는 버텨줄 수 있는 SP를 구했다.

4000를 넘기면 어떻게 하냐구요?  쓰지마세요. 그렇게 만들지 마세요.
가독성 제로입니다. 그렇게 만든 놈은 때리세요.

그러면 새로운 버전을 내놓겠습니다.

CREATE PROCEDURE [dbo].[sp_helptext2] (@ProcName NVARCHAR(256))
AS
BEGIN
  DECLARE @PROC_TABLE TABLE (X1  NVARCHAR(MAX))

  DECLARE @Proc NVARCHAR(MAX)
  DECLARE @Procedure NVARCHAR(MAX)
  DECLARE @ProcLines TABLE (PLID INT IDENTITY(1,1), Line NVARCHAR(MAX))

  SELECT @Procedure = 'SELECT DEFINITION FROM '+db_name()+'.SYS.SQL_MODULES WHERE OBJECT_ID = OBJECT_ID('''+@ProcName+''')'

  insert into @PROC_TABLE (X1)
        exec  (@Procedure)

  SELECT @Proc=X1 from @PROC_TABLE

  WHILE CHARINDEX(CHAR(13)+CHAR(10),@Proc) > 0
  BEGIN
        INSERT @ProcLines
        SELECT LEFT(@Proc,CHARINDEX(CHAR(13)+CHAR(10),@Proc)-1)
        SELECT @Proc = SUBSTRING(@Proc,CHARINDEX(CHAR(13)+CHAR(10),@Proc)+2,LEN(@Proc))
  END
  --* inserts last line
  insert @ProcLines 
  select @Proc ;

  SELECT Line FROM @ProcLines ORDER BY PLID
END
GO


이 버전은 라인이 지원하는 한계까지 나옵니다.
개행 함부로 나오지 않아요.


쓸만합니다.







오늘날 개신교도의 문제점. 후덜덜

제목은 거창하지만 내용은 간단하다.

예수님의 말씀은 따르지 않아서 이다.

예수님이 뭐라고 하셨나?

진리를 알지니 진리가 너희를 자유케 하리라(요 8:32)


진리를 알라고 설파하신다.
혹자는 진리가 예수니 예수님을 알면 자유로워진다는 얼토당토 않는 말을 하는데, 아휴 말귀를 좀 알아 처먹어라.

예수님 = 진리
라면 진리를 알라고(능동) 하지 않고 믿어라(수동) 라고 말해야 한다.

예수님이 말씀하신 것은 믿지말고 알아라는 거다.


끊임없이 물음을 가지고 알려고 노력하라는 점에서 
예수님은 진리를 추구하여 자유로워지라는 말을 한거다.


근데 오늘날 신앙인이라고 하는 개신교도들은 어떤가?

무조건 "아멘"만을 외친다.

신이 머리를 줬는데 머리는 어디다 팔아먹고 뇌없는 믿음만 보여주고 있다.

머리를 안쓰고 진리를 추구하지 못하면 자유롭지 못하다. 
즉, 노예가 된다는 말이다.
노예로 살지마라고 예수님 께서 말씀하셨는데, 굳이 노예로 살겠다는 사람들이 너무 많다.


종교개혁 이후에 성경은 누구나 글만 깨우치면 읽을 수 있다.

존나 어려운 라틴어가 아니고, 영어, 한국어로 글을 읽을 수 있다는 말은
누구나 성경을 읽고 능동적으로 해석하고 그 가르침을 따르면
예배당에 안다녀도 영생을 얻는다는 말이다.


목사는 그저 성경을 읽어주고 해석해주는 직업이다. 목사를 신으로 착각하는 사람들이 너무 많다.
그래서 사이비가 나오는 거다.
목사는 목자가 아니다.

목자는 오직 예수님 밖에 없다.
즉, 성경의 해석을 다르게 설교하는 목사놈이 있다면 "아멘"이라고 하면 안되고 "미친놈" 하고 외쳐야 하는 것이다.

신을 핑계로 나쁜 짓을 한다면 진리추구자의 입장에서 능동적으로 반박하고 반대해야 한다.



그런데 오늘날의 개신교도들은 어떤가?

나는 가만히 보니, 이들은 뇌를 어디다 누고 온 것 같다.
믿음만 강요하는 사람을 원한다면 신은 뇌를 주지 않았을 것이다.
근데 그 뇌를 하찮게 이용하는 개신교인을 보면 
이것들이 신을 능욕하는 게 아닌가 싶다.





추억의 게임 xcom-2 을 superhuman 모드로 최단시간 돌파 ( 실전 1) 게임?

드디어 xcom2를 구했다.

윈도7에서 도스박스로 되어있는 버전인데 스크릿 샷을 찍을 수 있음을 확인했다.

고로 이제 포스팅만 하면 된다.


주말에 달려서 초인모드로 5일정도만에 끝내는 것을 목표로 진행해 보겠다.

요즘 체력이 안되서 게임이라는 것도 오래 못하는 단점이 있는 고로 5일만에 외계인들 섬멸하는 것이 목표이다.

하지만 너무 쉬우므로 제한을 두기로 했다.


제한요건
1. SAVE & LOAD 신공은 없는 것으로 한다.
   저장질로 하면 너무 쉬우므로 저장질은 없는 편이 더 낫다는 것이 나의 결론이다. 이걸 쓰면 아무나 깬다.

2. 쉴 때 저장하는 것은 필요하므로 저장질은 단 1번만 하기로 한다. 이것도 쉬기 위한 저장이다.
   전투에 패배해서 뒤로 가는 것은 없다.
   물론 여러번 저장해놓고 꼼수로 1번 저장한 것처럼 보여준다면 거짓으로 돌파할 수 있겠지만,
   그런 시시껄렁한 것으로 포스팅을 더럽히고 싶지 않다.

3. 게임의 버그로 인하여 진행이 안 될때는 중단시점의 로드를 허용한다.
   간혹 있다. 뻣어버려서 도저히 진행이 안될때 그럴때는 별수 있나? 로드해야지.

4. 하루에 한번 이상은 저장 않기로 한다.
   즉, 버그로 맛탱이가면 어제 꺼로 다시 작업해야 한다는 말임.


그럼 포스팅 시작하겠다.

근데 오늘은 맛뵈기만...



추억의 게임 xcom-2 을 superhuman 모드로 최단시간 돌파하기(part 5) 게임?

귀찮음을 무릅쓰고 시리즈의 마지막 끝을 내기 위해 계속 씁니다.


외계인 기지를 찾으러 내렸을 때, 화면의 기준으로 우측에 기지입구가 있는 지역에 내릴수가 있다. 4분의 1 가능성
여기가 명당 자리다.

여기에 내리기 위해 몇번 왔다 갔다 하는 게 좋다.
왜냐면 외계인 


tasoth leader ( tasoth 의 지도자)가 정신조종 스킬을 가지고 있기 때문이다.
이놈을 생포해야 하는데, 생포방법은 별거 없음.
초반에는 쇼크를 때릴수 있는 무기가 없기 때문에 stun load 로 지지는 수 밖에 없는데
문제는 초반에는 이놈을이 병사들을 정신조종을 하기 때문에 여간 힘든게 아니다.

그래서 초반에 탱크 2대로 입구를 막고 병사 2명을 무장해제 시킨다면 적본거지에 투입하면
정신력의 수치를 알수 있습니다.

정신력 저항이 약한 놈은 보이지도 않는 거리에서 걸려서 적의 편이 됨.
한마디로 미침.
이런 놈은 필히 버려야 함.
근데 대부분 이런 놈은 대위급 쯤 되는 놈이 대부분임.

어쨌거나 루키라도 좋으니 정신력이 좀 견디는 2명을 데리고 가서
반드시, 탱크로만 주변을 조금 정리해주어야 함.

딱 2~3번만 왔다갔다 하면 표가 남.
초반에 웅웅 거리면서 멀리서 정신조정 하려고 하는데 하자마자 패닉이나, 정신조정 당한다면 담에는 이놈은 안싣고 와야함.

3~4번 견디는 놈이 있다면 이놈은 주위만 좀 정리해주면 납치를 할 수 있는 종자가 됨.


그럼 천천히 주위를 하나씩 정리하고 문을 열고 들어간 후, 2층을 진압해야 함.

물론 이건 탱크가 먼저 몇대 때려야 함.
탱크가 몸빵이 안되서 부숴지기도 하지만, 다 부서지고 1놈만 납치해도 이 최단깨기 미션은 성공하는 거임.

일단 스턴이 성공하면, 저같은 경우 조건에 맞는 병사만 있다면 90%의 확률로 납치함.

과학자를 투입해서 MC Disruptor! 만 만들면 게임 끝.

계속 마인드 컨트롤로 연습하면 안걸려드는 게 없음.


자~ 정리하자

초반에 에어리언 납치를 잘해야 함. Deep one 은 납치와 시체 둘다 유용하므로 첫번째 항구 미션에서 반드시 생포
모든 쓸모없는 장비는 다 팔아버리고 그 돈으로 과학자를 타이트하게 산다.

연구를 한다. 

배 납치미션은 지루하므로 1마리만 죽이고 도망오면 된다.
끝내기는 어렵지만 그보다 맵이 너무 커 수색이 짜증나므로 나는 절~대 이 미션은 안한다.

wide 레이더로 탐사하거나 전투기를 일부러 북극쪽에 패트롤 시켜보면 외계인 기지 하나 쯤 나온다.
거기서 출격/후퇴를 반복하면서 정신력 강한 병사2명과 탱2두대로 반드시 tasoth leader 를 생포하자.

이쯤이 약 겜 시간으로 3개월 쯤 된다.

실제 미션은 1개월당 3개정도 미션이 있다고 가정하면 끽해야 9개 미션으로 tasoth leader 를 생포하는 셈이다.
물론 기지 출격/후퇴를 반복은 제외. 어차피 후퇴했다가 기름만 채우고 바로 다시 투입되는 것이다.

여기까지 하면 마인드컨트롤 훈련시설 까지 만들게 될거고
그 이후 마인드 컨트롤 훈련을 1달 시키면 6개월이나 그 이전에 마인드컨트롤을 하는 병사가 완성된다.


물론 초반에는 마인드컨트롤 디게 못한다.
이게 무슨 소용이 있을지 모르겠다고 생각들어도 계속하면 숙련도가 높아진다.
어느정도 높아지면 그때부터는 돈이 남아돌게 된다.
여전히 숙소와 연구소와 과학자를 충분히 확보하고 살아있는 에어리언이 있다면 팔아치우고 기지로 간다.

기지에서 힘들어도 반드시 lobsterman navigator ( 가재 운항사)와 obsterman commander 를 꼭 기절시켜서 운반 혹은 미션을 끝내자

그럼 이제 끝난거다.

기술개발 빡시게 하면 그 다음은 최종비행 유닛을 만들수 있고 외계인 본거지에 출동할 수있게 된다.

이 일련의 작업이 컴퓨터의 시간으로 12개월안에 끝낼수 있다.
미션수로 따지면 30개 미션 정도만 하면 돌파할 수 있고,
정말 귀찮다면 필요 없는 미션 넘기고 술렁술렁하면 30개 이내의 미션으로도 돌파할 수 있다.



문제는 그거다.

1. 최단 컴퓨터 개월수 를 목표로 할 것인가? -> 이걸 원하면 미션은 거의 다 수행해야 한다. 그것도 good 이상으로.
그래야 돈이 많이 들어와서 과학자 많이 뽑고 빨리 끝낸다.


2. 최단 플레이 시간을 목표로 할 것인가? -> 이걸 원하면 배납치 미션은 넘어가고 왠만큼 짜증나는 외계인 기지 침략 미션은 다 안하고 외계인 최종시설만 폭파하고  lobsterman navigator ( 가재 운항사)와 obsterman commander 를 꼭 기절시켜서 데려오면 된다.



나는 플레이가 귀찮으므로 2번 루트를 꼭 탄다.

왜냐면 싸우는 것보다 찾는게 더 지겹다.
꼭꼭 숨어있는 놈들은 제일 짱나기 때문이다. 안나온다. 쩝


이거 동영상을 찍을 수 있으면 내가 찍어서 올려보고 싶은데... 








추억의 게임 xcom-2 을 superhuman 모드로 최단시간 돌파하기(part 4) 게임?

존나 오랜 시간 이후에 글을 올려본다.

나의 게으름은 상상을 초월하므로 이거 만들어 올리기 정말 힘들어서 안했다. 내가 백수도 아니고..

초인모드 즉, SUPERHUMAN 은 초반에만 잘 하면 최단기간에 끝낼 수 있다.

즉, 외계인 수용시설을 제일 처음 짓고 항구미션까지 무손실로 버텨야 한다.
무손실로 버티는 요령은 당연히 탱크이고 탱크는 내구력이 짱이지만 초반에 명중률은 쥐약이다.

첨에는 탱크로 정찰하고 수류탄으로 죽이고, 섹토이드 조차도 1방에 안죽는 초인모드라 2방 쏴야 한다.

초반에  Coelacanth Gas Cannon 이라는 42달러짜리 3개와 Solid Harpoon Bolts라는 총알 90개
짜리를 사야하는데 이걸 사기 위해서 창고에 있는 것은 모조리 팔자.
단, 전투기에 들어가는 하푼은 팔면 안되고 최소한의 무기는 있어야 하니 일반폭탄(general 폭탄)은 팔지말고 바행기에 둬야 함.

그리고 약간의 메디컬 플레어 (야간 반짝이) 몇개는 사야한다. 
밤에 시야가 극악으로 줄어들기 때문에 몇개 던지고 나중에 그 자리에 가서 줍고 또 던지고를 반복한다.

초반 탱크 2대로 적들을 털고 가능한한 모든 자원을 털어 과학자를 사서 연구를 하면 
항구미션이 만들어지고 항구에서 Deep One 을 생포해야 한다. 이놈 생포 못하면 연구테크 꼬임.

생포하는 방법은 역시 탱크가 앞에서 몸빵하면 됩니다. 그후 stun load 인가로 생포하면 됨.
deep one을 연구하면 아쿠아플라스틱을 연구하게 되고 이후 테크는 비행유닛 테크만 잘타면 날아다니면서 끝낼수 있습니다.
그때되면 쉬워짐.

최단시간에 돌파에 가장 중요한거, 돈이 좀 모이면 무조건 레이다 설치해서 외계인 기지를 찾으면 만사해결됨.
<- 이게 가장 중요.

외계인 기지를 찾으면 탱크2대, 무기하나도 없는 병사를 끌고 외계인 기지에 무조건 갑니다.
가서 보면 정신능력이 개판인 애들은 가자마자 적에게 마음을 빼앗깁니다.


그럼 그놈은 버려야 할 놈입니다.
몇번 왔다갔다 하면서 정신감응에 잘 안 걸리는 놈을 찾습니다.


 




[MS-SQL]에서 거래처별 품목, 단가에 대한 적용일을 시작~종료일로 정렬하기 MSSQL과 ORACLE

WITH HRAP_SEQ AS 
(
    SELECT ROW_NUMBER() OVER(ORDER BY ITEM_ID, VEND_CD, START_DT DESC) SEQ_NO
         , A.ITEM_ID
         , A.VEND_CD
         , A.PRICE
         , A.START_DT
      FROM SSC_SALE_PRICE A
     WHERE A.USE_FLAG = '1'
       AND A.PRICE_APPROVAL_FLAG = '1'
)
SELECT A.ITEM_ID
     , A.VEND_CD
     , A.PRICE
     , A.START_DT
     , CONVERT(VARCHAR(8), DATEADD(DAY, -1, CONVERT(DATETIME, ISNULL(B.START_DT, '29991231'), 112)), 112)  END_DT
  FROM HRAP_SEQ A
  LEFT
  JOIN HRAP_SEQ B
    ON A.ITEM_ID  = B.ITEM_ID
   AND A.VEND_CD  = B.VEND_CD
   AND A.SEQ_NO   = B.SEQ_NO + 1
 ORDER
    BY A.ITEM_ID
     , A.VEND_CD
     , A.START_DT DESC
     , A.SEQ_NO   DESC

[MS-SQL]에서 거래처별 품목, 단가에 대한 적용일을 시작~종료일로 정렬하기기

WITH HRAP_SEQ AS 
(
    SELECT ROW_NUMBER() OVER(ORDER BY ITEM_ID, VEND_CD, START_DT DESC) SEQ_NO
         , A.ITEM_ID
         , A.VEND_CD
         , A.PRICE
         , A.START_DT
      FROM SSC_SALE_PRICE A
     WHERE A.USE_FLAG = '1'
       AND A.PRICE_APPROVAL_FLAG = '1'
)
SELECT A.ITEM_ID
     , A.VEND_CD
     , A.PRICE
     , A.START_DT
     , CONVERT(VARCHAR(8), DATEADD(DAY, -1, CONVERT(DATETIME, ISNULL(B.START_DT, '29991231'), 112)), 112) END_DT
  FROM HRAP_SEQ A
  LEFT
  JOIN HRAP_SEQ B
    ON A.ITEM_ID  = B.ITEM_ID
   AND A.VEND_CD  = B.VEND_CD
   AND A.SEQ_NO   = B.SEQ_NO + 1
 ORDER
    BY A.ITEM_ID
     , A.VEND_CD
     , A.START_DT DESC
     , A.SEQ_NO   DESC

[ORACLE]에서 거래처별 품목, 단가에 대한 적용일을 시작~종료일로 정렬하기 MSSQL과 ORACLE

WITH HRAP_SEQ AS 
(
    SELECT ROW_NUMBER() OVER(ORDER BY ITEM_ID, VEND_CD, START_DT DESC) SEQ_NO
         , A.ITEM_ID
         , A.VEND_CD
         , A.PRICE
         , A.START_DT
      FROM SSC_SALE_PRICE A
     WHERE A.USE_FLAG = '1'
       AND A.PRICE_APPROVAL_FLAG = '1'
)
SELECT A.ITEM_ID
     , A.VEND_CD
     , A.PRICE
     , A.START_DT
     , TO_CHAR(TO_DATE(NVL(A.START_DT, '29991231'), 'YYYYMMDD') - 1, 'YYYYMMDD') END_DT
  FROM HRAP_SEQ A
  LEFT
  JOIN HRAP_SEQ B
    ON A.ITEM_ID  = B.ITEM_ID
   AND A.VEND_CD  = B.VEND_CD
   AND A.SEQ_NO   = B.SEQ_NO + 1
 ORDER
    BY A.ITEM_ID
     , A.VEND_CD
     , A.START_DT DESC
     , A.SEQ_NO   DESC

[MS-SQL]에서 거래처별 품목, 단가에 대한 적용일을 시작~종료일로 정렬하기기

WITH HRAP_SEQ AS 
(
    SELECT ROW_NUMBER() OVER(ORDER BY ITEM_ID, VEND_CD, START_DT DESC) SEQ_NO
         , A.ITEM_ID
         , A.VEND_CD
         , A.PRICE
         , A.START_DT
      FROM SSC_SALE_PRICE A
     WHERE A.USE_FLAG = '1'
       AND A.PRICE_APPROVAL_FLAG = '1'
)
SELECT A.ITEM_ID
     , A.VEND_CD
     , A.PRICE
     , A.START_DT
     , CONVERT(VARCHAR(8), DATEADD(DAY, -1, CONVERT(DATETIME, ISNULL(B.START_DT, '29991231'), 112)), 112) END_DT
  FROM HRAP_SEQ A
  LEFT
  JOIN HRAP_SEQ B
    ON A.ITEM_ID  = B.ITEM_ID
   AND A.VEND_CD  = B.VEND_CD
   AND A.SEQ_NO   = B.SEQ_NO + 1
 ORDER
    BY A.ITEM_ID
     , A.VEND_CD
     , A.START_DT DESC
     , A.SEQ_NO   DESC


[MS-SQL] 속도 향상을 위한 테이블 분할에 대한 고찰(조회에 대한 고찰)(3) MSSQL과 ORACLE

그냥 데이터를 가지고 하니 체감이 좀 덜해서 예전에 쓰던 사이트의 DB로 테이블 분할에 대한 속도 향상을 체감해 보겠다.


OMM_OUT_HIST -> 출고를 담당하는 테이블인데 갯수가 3606539개이다.

대략 3백6십만건 쯤 된다고 생각하시면 되겠다.

들어가 있는 칼럼은 61개 짜리이다.
물론 REMARK부터 시작해서 온갖 잡 칼럼들이 다 모여 있다.


이 테이블로 아주 간단한 칼럼 몇개를 추출해서 테이블 OMM_OUT_HIST_MAIN 을 만들어 보겠다.

========================================================================
SELECT COM_CD, DIV_CD, BILL_SHEET_NO, ITEM_ID, STORE_CD, OUT_DT, CREATE_CLASS, SLIP_CLASS_CD
  INTO OMM_OUT_HIST_MAIN  
  FROM OMM_OUT_HIST
========================================================================


그리고 조건을 주어 검색해 보겠다.

========================================================================
SET STATISTICS IO ON
SET STATISTICS TIME ON

SELECT COM_CD, DIV_CD, BILL_SHEET_NO, ITEM_ID, STORE_CD, OUT_DT, CREATE_CLASS, SLIP_CLASS_CD 
  FROM OMM_OUT_HIST 
 WHERE OUT_DT >= '20140101' AND OUT_DT <= '20140131'

SET STATISTICS IO OFF
SET STATISTICS TIME OFF
========================================================================
테이블 'Workfile'. 검색 수 0, 논리적 읽기 수 0, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0.
테이블 'Worktable'. 검색 수 0, 논리적 읽기 수 0, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0.
테이블 'omm_out_hist'. 검색 수 3, 논리적 읽기 수 76495, 물리적 읽기 수 6, 미리 읽기 수 76252, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0.

 SQL Server 실행 시간: 
 CPU 시간 = 8549ms, 경과 시간 = 14333ms
========================================================================

-> 10만건에 14초의 시간이 걸렸다.




========================================================================
SET STATISTICS IO ON
SET STATISTICS TIME ON

SELECT COM_CD, DIV_CD, BILL_SHEET_NO, ITEM_ID, STORE_CD, OUT_DT, CREATE_CLASS, SLIP_CLASS_CD 
  FROM OMM_OUT_HIST_MAIN 
 WHERE OUT_DT >= '20140101' AND OUT_DT <= '20140131'

SET STATISTICS IO OFF
SET STATISTICS TIME OFF
========================================================================
SQL Server 구문 분석 및 컴파일 시간: 
   CPU 시간 = 0ms, 경과 시간 = 0ms.

(215332개 행이 영향을 받음)
테이블 'OMM_OUT_HIST_MAIN'. 검색 수 1, 논리적 읽기 수 40189, 물리적 읽기 수 0, 미리 읽기 수 40122, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0.

 SQL Server 실행 시간: 
 CPU 시간 = 1186ms, 경과 시간 = 6977ms

 SQL Server 실행 시간: 
 CPU 시간 = 0ms, 경과 시간 = 0ms
========================================================================

-> 같은 데이터로 10만건에 7초의 시간이 걸렸다.



즉, 테이블이 커지면, 즉 칼럼의 갯수가 커지면 물리적 데이터가 적은 테이블이 속도가 더 빠르다는 결론이다.

이것은 확실히 유의미한 데이터 이다.



자 그럼 거의 풀 스캔해보자. 
총 3백6시만건 쯤 되는데, 3백만건 조회해 보자.

========================================================================
SET STATISTICS IO ON
SET STATISTICS TIME ON

SELECT COM_CD, DIV_CD, BILL_SHEET_NO, ITEM_ID, STORE_CD, OUT_DT, CREATE_CLASS, SLIP_CLASS_CD 
  FROM OMM_OUT_HIST 
 WHERE OUT_DT >= '20100101' AND OUT_DT <= '20140131'

SET STATISTICS IO OFF
SET STATISTICS TIME OFF
========================================================================
SQL Server 구문 분석 및 컴파일 시간: 
   CPU 시간 = 0ms, 경과 시간 = 0ms.

(2993323개 행이 영향을 받음)
테이블 'omm_out_hist'. 검색 수 1, 논리적 읽기 수 236928, 물리적 읽기 수 0, 미리 읽기 수 233244, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0.

 SQL Server 실행 시간: 
 CPU 시간 = 7348ms, 경과 시간 = 81681ms

 SQL Server 실행 시간: 
 CPU 시간 = 0ms, 경과 시간 = 0ms
========================================================================

-> 약 1분 21초 걸렸다.



그럼 캄럼을 줄인 테이블을 조회해보자.

========================================================================
SET STATISTICS IO ON
SET STATISTICS TIME ON

SELECT COM_CD, DIV_CD, BILL_SHEET_NO, ITEM_ID, STORE_CD, OUT_DT, CREATE_CLASS, SLIP_CLASS_CD 
  FROM OMM_OUT_HIST_MAIN 
 WHERE OUT_DT >= '20100101' AND OUT_DT <= '20140131'

SET STATISTICS IO OFF
SET STATISTICS TIME OFF
========================================================================
SQL Server 구문 분석 및 컴파일 시간: 
   CPU 시간 = 0ms, 경과 시간 = 0ms.

(2993323개 행이 영향을 받음)
테이블 'OMM_OUT_HIST_MAIN'. 검색 수 1, 논리적 읽기 수 40189, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0.

 SQL Server 실행 시간: 
 CPU 시간 = 6412ms, 경과 시간 = 79694ms

 SQL Server 실행 시간: 
 CPU 시간 = 0ms, 경과 시간 = 0ms
========================================================================

-> 약 1분 19초 걸렸다.


즉, 풀데이터를 스캔한다면 결과값이 유의미하지 않다는 결론이다.






또한, 데이터의 양이 어느 정도이냐에 따라서 속도가 빨라지기도 느려지기도 한다.


13만건 정도의 데이터를 조회한다고 가정하면,
테이블 'omm_out_hist'. 검색 수 3, 논리적 읽기 수 75550, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0.
 SQL Server 실행 시간: 
 CPU 시간 = 7192ms, 경과 시간 = 10377ms

VS

테이블 'OMM_OUT_HIST_MAIN'. 검색 수 1, 논리적 읽기 수 40189, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0.

 SQL Server 실행 시간: 
 CPU 시간 = 1092ms, 경과 시간 = 4620ms


즉, 10대 4.5초 정도로, 유의미한 속도 향상이 있지만,



행의 수를 극단적으로 낮추면, 즉, 3만건 이내를 조회한다고 가정하고 쿼리를 날리면

테이블 'omm_out_hist'. 검색 수 1, 논리적 읽기 수 115776, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0.

 SQL Server 실행 시간: 
 CPU 시간 = 530ms, 경과 시간 = 978ms

VS 

(28412개 행이 영향을 받음)
테이블 'OMM_OUT_HIST_MAIN'. 검색 수 1, 논리적 읽기 수 40189, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0.

 SQL Server 실행 시간: 
 CPU 시간 = 858ms, 경과 시간 = 1672ms

로 오히려 반대의 경우도 발생한다.



즉, 데이터 정규화를 거친 테이블이 반드시 속도를 향상시켜주는 것은 아니라는 것이다.





[MS-SQL] 속도 향상을 위한 테이블 분할에 대한 고찰(조회에 대한 고찰) MSSQL과 ORACLE

MS SQL 에서 속도 향상을 위하여 테이블을 쪼개야 할 필요가 있는가?
에 대한 여러가지 문서를 찾을 일이 있었다.

정말 어렵고도 난해한 일이었다.

여기서 필요한 내용은

같은 사이즈의 테이블을 Row 갯수와 Column 갯수에 대하여 성능 저하와 향상을 기대할 수 있는가에 대한 문제다.

1. columun 수나 Row 수가 입력에 대한 속도 영향을 미치냐 하는 것.
2. columun 수나 Row 수가 조회에 대한 속도 영향을 미치냐 하는 것.


1번의 경우는 앞에서 보았다.

2번 조회는 어떻게 될까?





일단, mssql 에서 제공하는 파티션(partition) 에 대한 설명을 좀 하자.
파티션이란? MSSQL 엔터프라이즈 버전 이상에서 지원하는 테이블을 쪼개주는 기능이다.
이것을 "수평분할"이라고 명하겠다.


그리고 "수직분할"이라고 명칭할 수 있는 단어가 있는데,

그것은, 

수직 테이블 파티셔닝
수직 분할의 예


요로코롬 테이블을 쪼개주는 것이다.
데이터베이스 정규화라고도 말할 수 있겠죠?


자. 그럼 수평분할을 하면 속도가 향상되는 가?

쿼리로 실험해 보자

EmployeeReports 라는 테이블을 만들어 보자. 100만건 짜리로


---------------------------------------------
CREATE TABLE EmployeeReports
(
ReportID int IDENTITY (1,1) NOT NULL,
ReportName varchar (100),
ReportNumber varchar (20),
ReportDescription varchar (max)
CONSTRAINT EReport_PK PRIMARY KEY CLUSTERED (ReportID)
)

DECLARE @i int
SET @i = 1

BEGIN TRAN
WHILE @i<= 1000000
BEGIN
INSERT INTO EmployeeReports
(
ReportName,
ReportNumber,
ReportDescription
)
VALUES
(
'ReportName',
CONVERT (varchar (20), @i),
REPLICATE ('Report', 1000)
)
SET @i=@i+1
END
COMMIT TRAN
GO
---------------------------------------------

SET STATISTICS IO ON
SET STATISTICS TIME ON

SELECT er.ReportID, er.ReportName, er.ReportNumber
  FROM dbo.EmployeeReports er
 WHERE er.ReportNumber LIKE '%33%'
 

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

라고 쿼리를 날려보자



---------------------------------------------
테이블 'EmployeeReports'. 검색 수 9, 논리적 읽기 수 113291, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0.

 SQL Server 실행 시간: 
 1차 : CPU 시간 = 4493ms, 경과 시간 = 28965ms
 2차 : CPU 시간 = 4773ms, 경과 시간 = 28904ms
 3차 : CPU 시간 = 4665ms, 경과 시간 = 28907ms

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

참고로 내 화면에서는 이런 결과가 떴다. 4493밀리초.


자 그럼 수직분할 해보자.

---------------------------------------------
CREATE TABLE ReportsDesc
( ReportID int not null,
  ReportDescription varchar(max)
  CONSTRAINT PK_ReportDesc PRIMARY KEY CLUSTERED (ReportID)
)
 
CREATE TABLE ReportsData
(
ReportID int NOT NULL,
ReportName varchar (100),
ReportNumber varchar (20),
CONSTRAINT DReport_PK PRIMARY KEY CLUSTERED (ReportID)
)
INSERT INTO dbo.ReportsData
(
    ReportID,
    ReportName,
    ReportNumber
)
SELECT er.ReportID,
er.ReportName,
er.ReportNumber
FROM dbo.EmployeeReports er

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


테이블 ReportsData 는  단지, 3개의 칼럼만 가지는 아주 작은 테이블로 구성되어 있다.
물론 PRIMERY KEY는 동일하다.

---------------------------------------------
SET STATISTICS IO ON
SET STATISTICS TIME ON
SELECT er.ReportID, er.ReportName, er.ReportNumber
  FROM ReportsData er
 WHERE er.ReportNumber LIKE '%33%'

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

조회를 해보았다.

결과는?

---------------------------------------------
테이블 'ReportsData'. 검색 수 1, 논리적 읽기 수 421, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0.

 SQL Server 실행 시간: 
  1차 : CPU 시간 = 452ms, 경과 시간 = 955ms
  2차 : CPU 시간 = 422ms, 경과 시간 = 955ms
  3차 : CPU 시간 = 390ms, 경과 시간 = 970ms


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

 참고로 내 화면에서는 이런 결과가 떴다. 452밀리초.

어라?
같은 칼럼을 조회했는데, 어째서 10배 차이가 나는 값으로 속도가 나왔을까?




좋다 그럼  PRIMERY KEY를 조회조건으로 주고 검색해보자. LIKE문은 속도상 문제가 있을 수 있으니,
라고 생각하고 조회해 보았다.

---------------------------------------------
SET STATISTICS IO ON
SET STATISTICS TIME ON
SELECT er.ReportID, er.ReportName, er.ReportNumber
  FROM dbo.EmployeeReports er
 WHERE er.ReportID >= 10000 and er.ReportID <= 500000
SET STATISTICS IO OFF
SET STATISTICS TIME OFF
---------------------------------------------
SQL Server 구문 분석 및 컴파일 시간: 
   CPU 시간 = 0ms, 경과 시간 = 0ms.
테이블 'EmployeeReports'. 검색 수 1, 논리적 읽기 수 99370, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0.

 SQL Server 실행 시간: 
 1차 : CPU 시간 = 2870ms, 경과 시간 = 14569ms
 2차 : CPU 시간 = 2948ms, 경과 시간 = 14561ms
 3차 : CPU 시간 = 2855ms, 경과 시간 = 14572ms


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

2870밀리초다.

---------------------------------------------
SET STATISTICS IO ON
SET STATISTICS TIME ON
SELECT er.ReportID, er.ReportName, er.ReportNumber
  FROM ReportsData er
 WHERE er.ReportID >= 10000 and er.ReportID <= 500000
SET STATISTICS IO OFF
SET STATISTICS TIME OFF 
---------------------------------------------

SQL Server 구문 분석 및 컴파일 시간: 
   CPU 시간 = 0ms, 경과 시간 = 0ms.
테이블 'ReportsData'. 검색 수 1, 논리적 읽기 수 418, 물리적 읽기 수 0, 미리 읽기 수 0, LOB 논리적 읽기 수 0, LOB 물리적 읽기 수 0, LOB 미리 읽기 수 0.

 SQL Server 실행 시간: 
  1차 : CPU 시간 = 250ms, 경과 시간 = 9400ms
  2차 : CPU 시간 = 328ms, 경과 시간 = 9514ms
  3차 : CPU 시간 = 343ms, 경과 시간 = 9528ms


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

250밀리초다.



7배 이상의 차이를 보여주고 있다.


뭐 이런 결과물이 항상 나오지는 않을 것이다.
여러가지 변수에 의해 수직분할이 안하는 것보다 나은 결과를 보여줄 것이라고 생각할 수 있는 타당한 근거가 되는 것이다.



머 수평분할은 다음에 쓰겠다.
너무 쓸게 많어.





참조 :
http://www.sqlshack.com/database-table-partitioning-sql-server/





[MS-SQL] 속도 향상을 위한 테이블 분할에 대한 고찰(입력에 대한 고찰) MSSQL과 ORACLE

MS SQL 에서 속도 향상을 위하여 테이블을 쪼개야 할 필요가 있는가?
에 대한 여러가지 문서를 찾을 일이 있었다.

정말 어렵고도 난해한 일이었다.

여기서 필요한 내용은

같은 사이즈의 테이블을 Row 갯수와 Column 갯수에 대하여 성능 저하와 향상을 기대할 수 있는가에 대한 문제다.

1. columun 수나 Row 수가 입력에 대한 속도 영향을 미치냐 하는 것.
2. columun 수나 Row 수가 조회에 대한 속도 영향을 미치냐 하는 것.


1번의 경우는 간단하다.
넣어보면 되는 것이다. 

즉,

테이블 이름

열 의 갯수

구조

T2

(2)

(C1)의 INT, C2의 VARCHAR (10)

T7

(7)

U1의 고유 식별자, U2의 고유 식별자, I1의 INT, I2의 INT, D1의 날짜, I3의 INT, I4의 INT

T10

(10)

T7 구조 플러스 3 VARCHAR (200) 열

T23

(23)

T7 구조 플러스 15 VARCHAR (40)

표 1



표1에 대한 성능 결과가 중요한 것이다.


CREATE TABLE [dbo].[t2](
[c1] [int] NULL,
[c2] [varchar](10) NULL
)

에 대한 입력방법은
Insert into t2(c1,c2) values(1,’A’),(2,’B’),(3,’C’);
이런 식으로 입력하는 것이다.

물론, 이런 식의 쿼리로 입력하는 것이 많지는 않을 것이지만, 단순히 데이터 밀어넣는 작업에는 필요할 수도 있으므로 예외로 보자.


Image 3

Image 4



이 표에 의하면 여러 Row 가 단일 Row에 많은 칼럼을 입력하는 것 보다 8배나 빠른 입력을 제공한다.

테이블의 열 수

(2)

7

(10)

(23)

속도 차이 (단일 행 결과는 / 여러 행이 결과)

6.25

2.56

1.75

1.16

단일 행은 초 결과

57

(61)

(72)

(92)

여러 행이 초 결과를 삽입

9.125

23.8

(41)

79

삽입 행의 수

(25)

(5)

(5)

(2)

표 2


표 2는 여러 행으로 입력하는 것이 단일행의 여러 칼럼의 입력보다 속도가 빠르다는 것이다.




테스트에서, 나는 완전히 전체 크기에 따라 문자 열을 채웠다.

이미지 (9)

이미지 (10)




여기 결과물인 1천만건에 대한 입력도 4배 빠른 결과물을 보여주고 있다.


즉, 입력은 여러 칼럼으로 된 입력보다 여러행으로 된 입력이 현저한 차이만큼의 속도향상을 보여준다는 말이다.


출처, https://www.simple-talk.com/sql/performance/comparing-multiple-rows-insert-vs-single-row-insert-with-three-data-load-methods/




이클립스 로딩 멈춤 해결

설정된 workspace 로 가서,

[Workspace Directory]/.metadata/.plugins/org.eclipse.core.resources/.snap

.snap를 삭제해준다.


드뎌 블자가 도탑전기에 대해서 소송을 걸었다. 게임?

저 밑에 보면 "도타의 저작권은 블리자드가 아니에용 도탑전기가 블리자드와 도타를 이용한것이죠"
어쩌구 저쩌구 하는데 결국, 블리자드가 소송을 걸었다.


사건의 발단

1. 유쿨이라는 히어로즈차지를 만든 회사가 - 이 회사는 도탑전기를 고대로 베낌 - 
도탑전기의 소스코드를 디컴파일해서 자신의 히어로즈차지에 넣다가 숨겨논 이스터에그가 발각되는 사태가 발생함
참고로, 유쿨은 블리자드에 저작권 관련 협의를 마친 상태임.


2. 릴리스 - 즉, 도탑전기 쪽에서 잘 걸렸다고 생각하고, 유쿨을 상대로 저작권(?) 소송을 걸어버림.

3. 이러한 소송이 발생하자 블리자드는 당연히 도탑쪽을 향해서 소송을 걸었음.
   즉, 도탑이라는 놈이 저작권에 대한 개념이 있는데도 불구하고 - 유쿨 소송 내용 -, 맘대로 카피했으니 이번 기회에 이놈들을 박살내주겠다고 벼르고 있음.

4. 상황이 이렇게 되자, 유쿨보다는 릴리스(도탑전기) 쪽이 더 당황함.


자~ 이 진흙탕 싸움의 승자는 누구일까?


SP_HELPTEXT 완전 수정판 MSSQL과 ORACLE


알다시피 MS-SQL의 SP_HELPTEXT는 버그가 있다.

황당버그, 라인이 어느정도 늘어나면 자동으로 엔터가 쳐져서 소스가 바뀐다.



이게 사람 죽이는 거다.

수정을 계속하면 할수록, 이게 완전히 원래 소스를 알아볼수 없게 만든다.



그래서 예전에 내가 수정버전을 올린 적이 있었는데, 그것도 약간의 버그가 존재했다.

하지만 지금 버전은 그런거 없다.


자 잘 카피해서 써라

단 sp_helptext는 시스템 프로시저이므로 sp_helptext2 로 만들고 써야 한다.


CREATE PROCEDURE [dbo].[sp_helptext2] (@ProcName NVARCHAR(256))
AS
BEGIN
  DECLARE @PROC_TABLE TABLE (X1  NVARCHAR(MAX))

  DECLARE @Proc NVARCHAR(MAX)
  DECLARE @Procedure NVARCHAR(MAX)
  DECLARE @ProcLines TABLE (PLID INT IDENTITY(1,1), Line NVARCHAR(MAX))

  SELECT @Procedure = 'SELECT DEFINITION FROM '+db_name()+'.SYS.SQL_MODULES WHERE OBJECT_ID = OBJECT_ID('''+@ProcName+''')'

  insert into @PROC_TABLE (X1)
        exec  (@Procedure)

  SELECT @Proc=X1 from @PROC_TABLE

  WHILE CHARINDEX(CHAR(13)+CHAR(10),@Proc) > 0
  BEGIN
        INSERT @ProcLines
        SELECT LEFT(@Proc,CHARINDEX(CHAR(13)+CHAR(10),@Proc)-1)
        SELECT @Proc = SUBSTRING(@Proc,CHARINDEX(CHAR(13)+CHAR(10),@Proc)+2,LEN(@Proc))
  END
 --* inserts last line
 insert @ProcLines 
 select @Proc ;

 --edited here. (where Line<>'')
 SELECT Line FROM @ProcLines where Line<>'' ORDER BY PLID
END




오늘도 외친다.

구글신이시여....




토드 그리드 복사

해더 및 전체 그리드 복사 ->   Ctrl + Ins
                                  -> Ctrl + A, Ctrl + C

입니다.

1행밖에 없을 때는 반드시 Ctrl + Ins
로 복사해야 함.

MS-SQL에서 "create table if exists" 를 오라클 구문으로 바꾸기 MSSQL과 ORACLE

잘 알다시피 mssql에서는 동적으로 테이블을 생성하기 위해서 

IF EXISTS (select table_name
             from information_schema.tables
Where table_name = 'test123')
drop table test123
 CREATE TABLE test123
  ( vend_cd             NVARCHAR(255)
  , vend_nm             NVARCHAR(255)
 )

이런 구문이 있다.

그런데 오라클은?



별로 어렵지 않아요.



DECLARE v_table_name varchar2(50) := upper('test123');

CURSOR C1 is SELECT table_name FROM all_tables where table_name = v_table_name;
BEGIN
FOR I IN c1 LOOP
EXECUTE IMMEDIATE 'DROP TABLE '||I.table_name;
END LOOP;
EXECUTE IMMEDIATE ('CREATE TABLE ' || v_table_name  || ' ( '
|| ' SIT_CODE       VARCHAR2 (8), '
|| ' PT_SEQUENCE    INTEGER, '
|| ' PT_CHANGETIME  DATE, '
|| ' PT_OPCODE      INTEGER, '
|| ' PT_STATUS      INTEGER, '
|| ' PT_ERR_MESSAGE VARCHAR2 (255), '
|| ' PT_ERR_OBJ     CLOB, '
|| ' PT_ERR_CODE    VARCHAR2 (255), '
|| ' PT_PUB_AFTER   DATE   ) '
);
END;





단, 위의 구문은 테이블이 존재하면 삭제(DROP) 하고 만드는 거다.

mssql은 물론이요, 오라클 또한 그러니 잘보시오.


테이블이 흔적도 없이 사라졌다가 생긴다는 말임.






오라클 MERGE INTO 구문에서 카운터 세기 MSSQL과 ORACLE

오라클에 Merge 가 있다.


아실랑가 모르겠지만, 오라클에서 복잡하게 대량으로 업데이트, 삭제 할려고 하면
서브쿼리 쓰던가 
조인뷰를 쓰던가
merge구문을 쓰면 된다.

근데 오라클은 Ms-sql과 달리, 쓰는 방식에 제약이 좀 따른다.
조인뷰의 /*+ BYPASS_UJVC */  힌트는 11g에서 안먹힘.

그래서 1:1의 수정이 아닌 경우에는 좌절됨.
서브쿼리는 속도가 느리다. 씨봉탱들....

그래서 merge를 쓰면 쾌적한 업데이트와 삭제를 하는데
문제는 이게 수행 결과값을 리턴하지 않는다는 것이다.
즉, 몇개 넣었다라는 메세지를 안보내준다.


위를 보다시피 안보인다.
뭐가 얼만큼 실행되었는지 보이지 않는다는 말이다.



한마디로 장님이 된다는 소리.

씨봉 100만건 업데이트 했는지, 10건 업데이트 했는지 모른다면 어찌 그걸 사용할 수 있으랴?

그래서 존나 인터넷으로 달렸다.

그래서 찾았다.
오호라 구글신이시여

먼저 package를 만들어야 해.
안 만들거면 뒤로가기 누르세요.
여기서 삽질하지 마시고.



CREATE OR REPLACE PACKAGE etl AS

   c_inserting CONSTANT PLS_INTEGER := 0;
   c_updating  CONSTANT PLS_INTEGER := 1;

   FUNCTION merge_counter (
            action_in IN PLS_INTEGER DEFAULT c_inserting
            ) RETURN PLS_INTEGER;

   FUNCTION get_merge_update_count RETURN PLS_INTEGER;

   FUNCTION get_merge_update_count (
            merge_count_in IN PLS_INTEGER
            ) RETURN PLS_INTEGER;

   FUNCTION get_merge_insert_count RETURN PLS_INTEGER;

   FUNCTION get_merge_insert_count (
            merge_count_in in PLS_INTEGER
            ) RETURN PLS_INTEGER;

   PROCEDURE reset_counters;

END etl;


CREATE OR REPLACE PACKAGE BODY etl AS

   g_update_counter PLS_INTEGER NOT NULL := 0;
   g_insert_counter PLS_INTEGER NOT NULL := 0;

   /*----------- FUNCTION merge_counter -----------*/
   FUNCTION merge_counter (
            action_in IN PLS_INTEGER DEFAULT c_inserting
            ) RETURN PLS_INTEGER IS
   BEGIN
      CASE action_in
         WHEN c_updating
            THEN g_update_counter := g_update_counter + 1;
         WHEN c_inserting
            THEN g_insert_counter := g_insert_counter + 1;
         ELSE
            RAISE PROGRAM_ERROR;
      END CASE;
      RETURN 0;
   END merge_counter;

   /*----------- FUNCTION get_merge_update_count V1 -----------*/
   FUNCTION get_merge_update_count
      RETURN PLS_INTEGER is
   BEGIN
      RETURN g_update_counter;
   END get_merge_update_count;

   /*----------- FUNCTION get_merge_update_count V2 -----------*/
   FUNCTION get_merge_update_count (
            merge_count_in IN PLS_INTEGER
            ) RETURN PLS_INTEGER IS
   BEGIN
      RETURN NVL( merge_count_in - g_insert_counter, 0 );
   END get_merge_update_count;

   /*----------- FUNCTION get_merge_insert_count V1 -----------*/
   FUNCTION get_merge_insert_count
      RETURN PLS_INTEGER IS
   BEGIN
      RETURN g_insert_counter;
   END get_merge_insert_count;

   /*----------- FUNCTION get_merge_insert_count V2 -----------*/
   FUNCTION get_merge_insert_count (
            merge_count_in IN PLS_INTEGER
            ) RETURN PLS_INTEGER IS
   BEGIN
      RETURN NVL( merge_count_in - g_update_counter, 0 );
   END get_merge_insert_count;

   /*----------- FUNCTION reset_counters -----------*/
   PROCEDURE reset_counters IS
   BEGIN
      g_update_counter := 0;
      g_insert_counter := 0;
   END reset_counters;

END etl;


여기까지만 쓰면 만사 오케이

SET SERVEROUTPUT ON;
BEGIN
    MERGE INTO  사원테이블1 X
    USING(
           SELECT a.emp_id, b.deptcd_acct  rep_comp_cd2 
               FROM 사원테이블1 a
                  , 사원테이블2 b
              WHERE a.emp_id= b.emp_id
                and b.status = '1'
          ) B
    ON (X.emp_id = B.emp_id)
    WHEN MATCHED THEN
          UPDATE 
          SET  X.in_user = '업데이트1'
            ,  X.in_time = SYSDATE
          ;
          
    DBMS_OUTPUT.PUT_LINE( TO_CHAR(SQL%ROWCOUNT) || ' 줄 반영되었어..');
    DBMS_OUTPUT.PUT_LINE( TO_CHAR(etl.get_merge_insert_count) || ' 줄 추가되었어.');
    DBMS_OUTPUT.PUT_LINE( TO_CHAR(etl.get_merge_update_count( SQL%ROWCOUNT ))|| ' 줄 수정되셨어.');

END;



보이지? 잘 나와





테이블 select 로 복사하기 MSSQL과 ORACLE

자주 까먹어서 여기 적어둠.


MS-sql

select *
   into 복사할테이블명
  from 원본테이블명
 where 절


Oracle
create table 복사할테이블명
As
select *
  from 원본테이블명
 where 절



이다.


자꾸 문법 까먹는거 보니 늙었나봐.... 힝...


도탑전기에 관하여. (게임 개발 성공요인 분석 Part 5.) 게임?

짱께 게임 분석 파뜨 5다.

이 게임 표절이다.

표절이 그냥 표절이 아니라 존나 표절이다.

이름 조차도 도타(?)전기다.

도타 캐릭을 그냥 카피한 수준입니다.

그러나 그 카피라는 것이 익숙함에 대한 미학(?)을 가지고 만든 거라서 뭐라 딱히 말 해주고는 싶은 데 말하기가 뭐하다.




정말 잘못되었다고 따끔하게 말하고는 싶은데, 뭔가 귀찮다니깐 정말.


암튼 이런 캐릭은 이걸로 친숙함을 유도해서 사람들이 좋아하게 만들어 준다.
수집욕을 부추긴다.
뭔가 내가 가지고 싶다는 말이 정말로 생기게 만드는 힘을 가진다는 말이다.


그리고 그런 수집욕이 우리에게 돈을 쓰게 만드는 요인이기도 하다.


오히려 카피가 돈을 쓰게 만드려고 하는 욕구가 생기게 하는 희안한 사건이 발생하는 것이다.


그 외에도 돈을 쓰게 만드는 요인들이 너무나~~ 많아서 쓰기 귀찮을 정도다.

1. 돈쓰기 쉽게 만드는 인터페이스
2. 감질나게 만들어서 돈쓰게 만드는 케이스
3. 한번쓰면 더 쓰게 만드는 케이스
4. 심리경제학 까지 동원해서 돈을 쓰게 만드는 케이스

암튼 돈을 크게 구애받지 않고 게임을 충분히~ 즐길 수 있지만, 대신에 지르면 지를 수록 


더 편하게 지르고 싶다는 충동을 느끼게 만든다는 점에서 왠지 연쇄할인마가 생각난다.


이런 여러가지 요인들이 이 게임이 실패가 힘든 게임으로 만든 것이다.

만일, 이게 실패를 만들 수 있는 거라면, 아마도


저작권

문제이다.



만일 블리자드가 작정하고 소송걸면 이 회사가 좀 힘들어지기는 할거다.
하지만 버는 돈이 많아서 그냥 돈으로 무마될 거 같기도 하다.


English »
 
Text-to-speech function is limited to 100 characters

도탑전기에 관하여. (게임 개발 성공요인 분석 Part 4.) 게임?

짱개 께임 성공요인 파뜨 4 되시겠다.

사진 넣으면서 편집하기 정말 싫어. 귀찮아..~~~~~



너무 너무 귀찮아.


암튼 그 요인 네번째


마를 수 없는 컨텐츠와 잦은 관심이 필요한 컨텐츠

되시겠다.


도탑전기라는고 하는게 멀티로 키우기 정말 어렵다.


물론 계정을 여러 개 만들 수는 있다.

하지만 레벨이 올라가면 갈 수록, 스테미너가 많아진다.
즉, 해야할 일들의 한계가 점점 줄어드는데, 하면 할 수록 시간이 많이 필요하다는 거다.

1스테이지 길이에 소모되는 속도가 대충 2~3분이라고 가정했을때, 

노말 던전의 경우 스테미너가 6, 엘리드 던전의 경우 12를 소모한다고 가정하고
이 던전을 평균 9정도 스테미너가 소모된다.

그런데 하루 100의 스테미너와 짬짬이 보충해주는 스테미너를 합치면 200이 넘는 스테미너가 40렙 기준으로 있다는 거다.

그럼 단순 계산으로 200의 스테미너를 소모하려면 얼마정도의 시간이 필요한까?
9로 200의 스테미너는 22던전을 돌 수 있는 스테미너다

22던전을 2분동안 돈다고 생각하면 44분이고 3분동안 돈다고 생각하면 66분이다.

단순 계산만 해도 1시간이 넘는 시간동안 돌아야 1캐릭의 스테미너를 다 소모한다.
하지만 세상일이 어찌 단순하게만 계산되나?

1시간 넘게 걸리고, 그리고 이게 레벨이 올라가면 갈수록 더 많이 필요하게 된다.

하루 2시간 걸린다고 치고, 5캐릭을 돌리려면 하루가 빡빡하다.

그만큼 컨텐츠를 소모해도 아직 할게 만다는 점이 이 게임이 롱런 할 수 있는 비결이다.

현질 없어도 소모하는 시간이 엄청나고, 그 시간만큼 성장하는 게임이다.


반드시 성장하고, 성장의 차이는 있지만, 결국 최고 레벨까지 

같이 갈수 있다는 막연한 희망(?)

을 갖게하는 게임이 되시겠다.



그런 희망이 없다면 유저들이 떠난다.



도탑전기에 관하여. (게임 개발 성공요인 분석 Part 3.) 게임?

짱개 맛폰 께임 성공요인 분석 시리즈 파뜨 3 되십니다.
English »
 
Text-to-speech function is limited to 100 characters

결재 시스템의 친밀화?

결재 유도 시스템이 아주 유저 프랜드리하게 되어있슴요.

쪼매만 지를 수 있고, 자주 지를 수 있고, 간질간질하게 지를 수도 있고, 
과자값으로 질러도 효과가 있고, 
헤비하게 질러도 효과가 있도록 되어있슴.


지르는 놈만 장땡이게 만들었느냐?


그런데 말입니다.
그렇게 질러도 제한이란게 있습니다.
스태미너 즉, 시간,

이 게임은 시간을 볼모로 잡고 있습니다.
시간은 무조건 투자해야 결과적으로 레벨이 업그레이드 되는데,
스테미너를 올리는 방법은 자동으로 6시간마다 차기를 기다리는 방법과, 약간의 보석으로 충전하는 방법 밖에 없습니다.
더 많이 충전하려면 기하급수적으로 보석이 들어간다고 하는데 처음은 50개, 2번째는 100개, 3번째는 200개냐?



즉, 암만 돈을 투자해도 극히 미묘할 정도의 발랜스는 가지고 있다는 말입니다.




국민 께임이라는 롤을 볼까요?

롤은 돈주고 장비를 사지도 않고, 기껏 산다고 해봐야
스킨이랑, 캐릭선택 제한을 풀어버리는 것 뿐입니다.
나머지는 실력이라는 소리죠.



즉, 캐쉬질이 게임성을 해치지 않습니다.






정말 진정한 평등은 실력만 보는 것.

하지만 맛폰 께임에 실력만 보게 한다면 그것 또한 웃기는 것.
운빨이 좀 작동 합니다.

그래서 운빨과 시간 




그리고 실력으로 3가지 단계로 구분했음.


즉, 운은 시간으로 커버 가능한가?

시간 > 운

의 공식이 성립하고


실력이 좋으면 시간을 많이 투자한 사람보다 잘할 수 있는가?

초반
시간 >= 실력


중반이후 
실력 >= 시간

의 공식을 따르고 있다.

즉, 시간만 지나면 어느정도의 강함은 보장하지만, 그 이후의 시간 소모는 진정한 실력이 뒷받침 되어야 한다는 결과로 보여준다.




말이 딴데로 샛는데.  




결재를 아주 쉽게 만들어져 있고, 이를 적절하게 유혹하고 있습니다. 

그 결재가 사용자들을 차별화하고는 있지만 적절한 수준으로 차단하고 있습니다.


그 차단이 합리적이지 못하게 되면 이런 현상이 벌어집니다.


1. 돈으로 질렀어 -> 보상을 받아서 내가 좀 쎄졌어 -> 근데 나보다 더 지른 놈이 더 쎄 -> 더 질러야해 -> 1번으로 돌아감.

어느날 이건 미친짓이라는 것을 알게돼.

적당한 선이 필요해.
그걸 잘 지키고 있는 게 도탑의 게임모델이야.


짱께들의 게임이기는 하지만, 이러한 성공도식은 반드시 연구해야 할 모델이야.


1 2 3 4 5