Translate

2022년 4월 1일 금요일

[넥사크로, Nexacro 14] 그리드 값 수정 여부 표시


Laptop
운영체제 Windows 10 Pro 64bit
개발환경 nexacro studio 14, 0, 1, 3900




유효성이 중요한 데이터를 다루던 중 데이터 수정 및 신규 여부를 화면에 나타내야 해서 만든 기능이다. 

넥사크로의 Grid에는 ROWTYPE이라는 값 수정 여부를 인식하는 속성이 있지만 값이 변경되는 시점이 edit 컴포넌트마다 달라서 이벤트를 다르게 써야 한다.

TEXT에서 입력 취소 기능(타이핑 도중 포커스 아웃하면 값 복원)은 편의상 넣었는데 이것도 역시 key 이벤트 타이밍을 잘 고려해야 한다.

Source
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
<?xml version="1.0" encoding="utf-8"?>
<FDL version="1.5">
  <TypeDefinition url="..\default_typedef.xml"/>
  <Form id="test" left="0" top="0" width="500" height="300" titletext="New Form" onload="test_onload">
    <Layouts>
      <Layout>
        <Grid id="Grid00" taborder="0" binddataset="dsGrid" autofittype="col" onkeydown="Grid00_onkeydown" onkeyup="Grid00_onkeyup" onselectchanged="Grid00_onselectchanged" oncloseup="Grid00_oncloseup" ontextchanged="Grid00_ontextchanged" left="75" top="68" width="350" height="100">
          <Formats>
            <Format id="default">
              <Columns>
                <Column size="40"/>
                <Column size="100"/>
                <Column size="80"/>
                <Column size="60"/>
              </Columns>
              <Rows>
                <Row size="24" band="head"/>
                <Row size="24"/>
              </Rows>
              <Band id="head">
                <Cell text="GBN"/>
                <Cell col="1" text="TEXT"/>
                <Cell col="2" text="DATE"/>
                <Cell col="3" text="COMBO"/>
              </Band>
              <Band id="body">
                <Cell style="align:center;color:red;color2:red;selectcolor:red;" text="bind:GBN"/>
                <Cell col="1" edittype="text" style="align:center;" text="bind:TEXT"/>
                <Cell col="2" displaytype="date" edittype="date" style="align:center;" text="bind:DATE"/>
                <Cell col="3" displaytype="combo" edittype="combo" style="align:center;" text="bind:COMBO" combodataset="dsCombo" combocodecol="CODE" combodatacol="VALUE"/>
              </Band>
            </Format>
          </Formats>
        </Grid>
        <Button id="btnAdd" taborder="1" text="ADD" onclick="btnAdd_onclick" left="140" top="183" width="60" height="30"/>
        <Button id="btnReset" taborder="2" text="RESET" onclick="btnReset_onclick" left="220" top="183" width="60" height="30"/>
        <Button id="btnSave" taborder="3" text="SAVE" onclick="btnSave_onclick" left="300" top="183" width="60" height="30"/>
      </Layout>
    </Layouts>
    <Objects>
      <Dataset id="dsGridCopy"/>
      <Dataset id="dsGrid">
        <ColumnInfo>
          <Column id="GBN" size="1" prop="default" type="STRING"/>
          <Column id="TEXT" size="10" prop="default" type="STRING"/>
          <Column id="DATE" prop="default" type="STRING"/>
          <Column id="COMBO" prop="default" type="INT"/>
        </ColumnInfo>
        <Rows>
          <Row>
            <Col id="GBN"/>
            <Col id="TEXT">TEST</Col>
            <Col id="DATE">20220101</Col>
            <Col id="COMBO">1</Col>
          </Row>
        </Rows>
      </Dataset>
      <Dataset id="dsCombo">
        <ColumnInfo>
          <Column id="CODE" prop="default" type="INT"/>
          <Column id="VALUE" size="10" prop="default" type="STRING"/>
        </ColumnInfo>
        <Rows>
          <Row>
            <Col id="CODE">1</Col>
            <Col id="VALUE">1</Col>
          </Row>
          <Row>
            <Col id="CODE">2</Col>
            <Col id="VALUE">2</Col>
          </Row>
          <Row>
            <Col id="CODE">3</Col>
            <Col id="VALUE">3</Col>
          </Row>
        </Rows>
      </Dataset>
    </Objects>
  </Form>
</FDL>

Script
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
this.isInput = false;  // 입력 중 상태 여부
this.inputBackup = '';  // 입력하기 전 값 백업

this.test_onload = function(obj:Form, e:nexacro.LoadEventInfo)
{
  this.dsGridCopy.copyData(this.dsGrid);  // 초기 데이터 백업
}

this.Grid00_ontextchanged = function(obj:Grid, e:nexacro.GridEditTextChangedEventInfo)
{
  // 수정 전 값 백업
  if (!this.isInput) {
    this.inputBackup = e.pretext;
  }
  if (this.dsGrid.getColumn(e.row, "GBN") != 'C') {
    this.isInput = true;
    this.dsGrid.setColumn(e.row, "GBN", 'U');
  }
}

this.Grid00_onkeyup = function(obj:Grid, e:nexacro.KeyEventInfo)
{
  var col = obj.getCellProperty("Body", obj.currentcol, "text").split(':')[1]; // 컬럼명
  // Enter 눌러서 입력 완료 후 ROWTYPE 상태에 따라 GBN 표시 (U: 수정, C: 신규)
  if (this.isInput && e.keycode == nexacro.Event.KEY_ENTER) {
    this.isInput = false;
    this.dsGrid.setColumn(obj.currentrow, "GBN", '');
    if (this.dsGrid.getRowType(obj.currentrow) == Dataset.ROWTYPE_UPDATE) {
      this.dsGrid.setColumn(obj.currentrow, "GBN", 'U');
    }
  }
}

this.Grid00_onkeydown = function(obj:Grid, e:nexacro.KeyEventInfo)
{
  if(e.keycode == nexacro.Event.KEY_ESC) { // 입력 도중 ESC 누를 경우
    // 포커스 해제
    obj.set_enable(false);
    obj.set_enable(true);
    
    var col = obj.getCellProperty("Body", obj.currentcol, "text").split(':')[1];
    if (this.isInput) {
      // 수정 전 값으로 복구
      this.dsGrid.setColumn(obj.currentrow, col, this.inputBackup);
      this.dsGrid.setColumn(obj.currentrow, "GBN", '');
      if (this.dsGrid.getRowType(obj.currentrow) == Dataset.ROWTYPE_UPDATE) {
        this.dsGrid.setColumn(obj.currentrow, "GBN", 'U');
      }
    }
      
    this.isInput = false;
  }
}

this.Grid00_onselectchanged = function(obj:Grid, e:nexacro.GridSelectEventInfo)
{ // 다른 row를 선택했을 경우 (입력 종료로 간주)
  var col = obj.getCellProperty("Body", e.cell, "text").split(':')[1];

  if (this.isInput && obj.getCellValue(e.oldrow, e.oldcol) == this.dsGridCopy.getColumn(e.oldrow, e.oldcol))
    this.dsGrid.setColumn(e.oldrow, "GBN", 0);
  this.isInput = false;
}

this.Grid00_oncloseup = function(obj:Grid, e:nexacro.GridEditEventInfo)
{ // 날짜, 콤보박스 선택 후
  var col = obj.getCellProperty("Body", e.cell, "text").split(':')[1];
    
  this.dsGrid.setColumn(e.row, "GBN", '');
  if (this.dsGrid.getRowType(e.row) == Dataset.ROWTYPE_UPDATE ||
    (e.value != this.dsGridCopy.getColumn(e.row, col) && this.dsGrid.getColumn(e.row, "GBN") != 'C')
      ) {
    this.dsGrid.setColumn(e.row, "GBN", 'U');
  }
}

this.btnAdd_onclick = function(obj:Button, e:nexacro.ClickEventInfo)
{ // 신규 데이터 추가
  var row = this.dsGrid.addRow();
  this.dsGrid.setColumn(row, 'GBN', 'C');
}

this.btnReset_onclick = function(obj:Button, e:nexacro.ClickEventInfo)
{ // 초기화
  this.dsGrid.reset();
}

this.btnSave_onclick = function(obj:Button, e:nexacro.ClickEventInfo)
{ // 저장 (전체 GBN ''로 설정 후 Dataset 적용 및 Copy 재처리)
  for (var i=0; i < this.dsGrid.getRowCount(); i++) {
    this.dsGrid.setColumn(i, "GBN", '');
  }
  this.dsGrid.applyChange();
  this.dsGridCopy.copyData(this.dsGrid);
}

2022년 3월 22일 화요일

유용한 프로그램 모음


2022년 3월 18일 금요일

[Oracle] 오라클 시스템 객체 쿼리 모음


Laptop
운영체제Windows 10 Pro 64bit
개발환경Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production


1. 테이블 DML 쿼리 Lock 조회

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
SELECT DISTINCT
       T1.SESSION_ID
     , T2.SERIAL#
     , T4.OBJECT_NAME
     , T2.MACHINE
     , T2.TERMINAL
     , T2.PROGRAM
     , T3.ADDRESS
     , T3.PIECE
     , T3.SQL_TEXT
     , T2.PREV_EXEC_START
     , 'ALTER SYSTEM KILL SESSION ' || '''' || T1.SESSION_ID || ', ' || T2.SERIAL# || ''';' KILL_SQL
     , T5.SPID PID
  FROM V$LOCKED_OBJECT T1
     , V$SESSION T2
     , V$SQLTEXT T3
     , DBA_OBJECTS T4
     , V$PROCESS T5
 WHERE T1.SESSION_ID = T2.SID
   AND T1.OBJECT_ID = T4.OBJECT_ID
   AND T2.SQL_ADDRESS = T3.ADDRESS
   AND T2.PADDR = T5.ADDR
--   AND MACHINE = 'PC명'
--   AND OBJECT_NAME = '테이블명'
   ORDER BY T3.ADDRESS, T3.PIECE;


2.  Package DDL Lock 조회

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
SELECT S.SID, S.SERIAL#,
       L.LOCK_TYPE,
       L.MODE_HELD,
       L.MODE_REQUESTED,
       L.LOCK_ID1,
       'ALTER SYSTEM KILL SESSION ' || '''' || S.SID || ', ' || S.SERIAL#, || ''';' KILL_SQL
    FROM   DBA_LOCK_INTERNAL L,
       V$SESSION S
    WHERE S.SID = L.SESSION_ID
    AND L.LOCK_TYPE = 'Body Definition Lock'
    AND UPPER(L.LOCK_ID1) = UPPER('패키지명');


3. JOB 목록 조회

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
SELECT
  JOB,
  WHAT,
  INTERVAL,
  TO_CHAR(NEXT_DATE, 'YYYY-MM-DD HH24:MI:SS') NEXT_DATE,
  TO_CHAR(THIS_DATE, 'YYYY-MM-DD HH24:MI:SS') THIS_DATE
FROM
  USER_JOBS
WHERE
  BROKEN = 'N'
  AND what LIKE '%' || 'JOB명' || '%'
ORDER BY
  NEXT_DATE;


4. 실행 중인 JOB 조회

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
SELECT D.JOB,
       S.SID,
       S.SERIAL#,
       STATUS,
       LOG_USER USERNAME,
       WHAT,
       DECODE (TRUNC (SYSDATE - LOGON_TIME), 0, NULL, 
            TRUNC (SYSDATE - LOGON_TIME) || ' Days' || ' + ') || TO_CHAR (TO_DATE(TRUNC(MOD(SYSDATE - LOGON_TIME, 1) * 86400), 'SSSSS'), 'HH24:MI:SS')
         RUNNING,
       D.FAILURES,
       'ALTER SYSTEM KILL SESSION ' || '''' || S.SID || ', ' || S.SERIAL# || ''';' KILL_SQL
  FROM DBA_JOBS_RUNNING D, V$SESSION S, DBA_JOBS J
 WHERE S.SID = D.SID AND D.JOB = J.JOB;


5. 객체(Package, Procedure, Function, Trigger...) 스크립트 검색

1
2
3
4
5
SELECT * FROM
  USER_SOURCE
WHERE
  UPPER('%' || '검색어' || '%')
  AND NAME LIKE '%' || '객체명' || '%';

단, 위 쿼리에서 VIEW는 USER_VIEWS 라는 별도의 테이블을 쓰기 때문에 조회되지 않는데, 스크립트가 나오는 TEXT 컬럼의 타입이 LONG으로 되어있어서 LIKE문을 쓸 수 없기 때문에 만약 함께 포함된 결과를 보고싶다면 CLOB 타입으로 변환할 임시 테이블을 쓰는 약간 번거로운 과정을 거쳐야 한다.
또한 LINE의 경우 VIEW 스크립트에서는 CREATE 부분을 없앤 상태로 주 쿼리만을 보여주기 때문에 developer 등의 기능으로 조회해서 볼 경우 맞지 않아보일 수 있다.

편하게 쓰기 위해 프로시저로 정의했다.
(임시 테이블이 남는게 싫다면 프로시저 내에서 EXECUTE로 CREATE/DROP을 할 수도 있을 것 같은데, DDL이 너무 자주 수행되는 것도 낭비같아서 일단은 남겨두기로...)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
CREATE GLOBAL TEMPORARY TABLE USER_VIEWS_CLOB
(
  NAME  VARCHAR2(30 BYTE),
  TEXT  CLOB
);

CREATE OR REPLACE PROCEDURE SP_SEARCH_OBJ(
    p_text IN VARCHAR2,
    p_type IN VARCHAR2,
    p_name IN VARCHAR2,
    v_result OUT SYS_REFCURSOR
)
IS 
BEGIN
    INSERT INTO USER_VIEWS_CLOB
    SELECT VIEW_NAME, TO_LOB(TEXT) FROM USER_VIEWS;
    
    OPEN v_result FOR
        SELECT * FROM (
            SELECT
              NAME, TYPE, LINE, TEXT
            FROM
              USER_SOURCE
            UNION
            SELECT
              NAME, 'VIEW' TYPE, LINE, TO_CHAR(TEXT)
            FROM
              (
                SELECT
                  NAME, LEVEL LINE, REGEXP_SUBSTR (TEXT, '.+', 1, LEVEL) TEXT
                FROM (
                    SELECT
                      NAME, TEXT
                    FROM
                      USER_VIEWS_CLOB
                    WHERE
                      UPPER(text) LIKE UPPER('%' || p_text || '%')
                  ) CONNECT BY LEVEL <= REGEXP_COUNT (TEXT, '.+')
              )
        )
        WHERE 
            UPPER(text) LIKE UPPER('%' || p_text || '%')
            AND TYPE = NVL(UPPER(p_type), TYPE)
            AND NAME LIKE '%' || UPPER(p_name) || '%';
    
    DELETE FROM  USER_VIEWS_CLOB;
    COMMIT;
END;

-- EXEC SP_SEARCH_OBJ('검색어', '타입', '객체명', :v_result);


6. 임의 날짜로부터 특정 일수동안의 목록 조회

ex) 2022년 1월 1일부터 1년(365일)
1
2
3
SELECT TO_DATE('20220101','YYYYMMDD') + (LEVEL-1) AS v_date
FROM DUAL 
CONNECT BY LEVEL <= (365)

~ 



- 참고 사이트