쿼리 질문 드립니다. 0 12 1,065

by 햇살좋은날 [2017.09.18 18:33:36]


안녕하세요.

날씨가 많이 선선해졌네요~ 다들 건강 잘 챙기시길 바랍니다.

다름이 아니고 논리게이트 관련 계산 질문 드립니다.

구조는 위와 같고, 데이터는 다음과 같습니다.

WITH T1 AS (
SELECT 0 AS ID, 'ND' AS UNIT, NULL AS VAL FROM DUAL
UNION ALL SELECT 1, 'AG', NULL FROM DUAL
UNION ALL SELECT 2, 'ND', NULL FROM DUAL
UNION ALL SELECT 3, 'OG', NULL FROM DUAL
UNION ALL SELECT 4, 'ND', 0.003 FROM DUAL
UNION ALL SELECT 5, 'ND', 0.004 FROM DUAL
UNION ALL SELECT 6, 'ND', 0.005 FROM DUAL
)
SELECT * FROM T1;


WITH T2 AS (
SELECT 0 AS START_NODE_ID, 1 AS  END_NODE_ID FROM DUAL
UNION ALL SELECT 1 AS START_NODE_ID, 2 AS  END_NODE_ID FROM DUAL
UNION ALL SELECT 2 AS START_NODE_ID, 3 AS  END_NODE_ID FROM DUAL
UNION ALL SELECT 3 AS START_NODE_ID, 4 AS  END_NODE_ID FROM DUAL
UNION ALL SELECT 3 AS START_NODE_ID, 5 AS  END_NODE_ID FROM DUAL
UNION ALL SELECT 1 AS START_NODE_ID, 6 AS  END_NODE_ID FROM DUAL
)
SELECT * FROM T2;

 

ID 0, 2의 값을 생성하고 싶습니다.

AND는 곱 OR는 합이고,

ID 2의 값은 0.007

ID 0의 값은 0.000035 입니다.

by jkson [2017.09.18 19:31:30]

AG -> AND GATE, OG -> OR GATE 약자인 건가요?

ID 2아래 3이 있고 3 아래 4, 5가 있으며 3은 OG이기 때문에 4와 5의 합이다.. 이런 원리예요?

1은 AG 이기 때문에 0.007과 6의 0.005를 곱한 거구요?

ND는 그냥 계산을 안 한다는 건가요? 그러면 위의 데이터에서 UNIT이 전부 ND이면 어떤 결과가 나와야하는 건지..


by 햇살좋은날 [2017.09.18 20:57:42]

네 맞습니다.

AG > AND GATE, OG > OR GATE, ND > NODE 약어입니다.

그림에서 보시다시피 ISLEAF가 1인 것들(최하위 노드)만 자동 입력되는 노드이고, 계산하는 방식은 jkson님께서 생각하시는 방법이 맞습니다.

노드와 노드 즉 ND와 ND가 바로 연결되는 부분은 스크립트로 막았습니다. 따라서 ND끼리 링크되는 일은 배제하셔도 됩니다.


by jkson [2017.09.19 08:02:11]

으헝~ 어렵네요ㅎㅎ 한 1~2시간 머리 싸매고 있으면 쿼리가 나오려나 모르겠네요. 30분 정도 해보고 안 되면 포기-0-


by jkson [2017.09.19 08:38:56]

그냥 쿼리로는 어렵네요. 재귀함수 형태로 만들면 뭔가 될 것 같긴한데..


by 김용한 [2017.09.19 08:44:35]

논리게이트도 올만에 보니 헷갈리는데 거기다가 쿼리까지 어렵군요 ㅋㅋ


by 햇살좋은날 [2017.09.19 09:22:38]

설명을 빼먹은 부분이 있네요 ^^;;

각 노드는 1개씩의 연결, 즉 상위 게이트와 하위 게이트 각 1개씩의 연결만을 가집니다.

자바로 구현은 마무리 했는데, 혹시 쿼리로 가능할까 싶어 남겨봅니다.


by 마농 [2017.09.19 11:26:53]
WITH t1 AS
(
SELECT 0 id, 'ND' unit, NULL val FROM dual
UNION ALL SELECT 1, 'AG',  NULL FROM dual
UNION ALL SELECT 2, 'ND',  NULL FROM dual
UNION ALL SELECT 3, 'OG',  NULL FROM dual
UNION ALL SELECT 4, 'ND', 0.003 FROM dual
UNION ALL SELECT 5, 'ND', 0.004 FROM dual
UNION ALL SELECT 6, 'ND', 0.005 FROM dual
)
, t2 AS
(
SELECT 0 start_node_id, 1 end_node_id FROM dual
UNION ALL SELECT 1, 2 FROM dual
UNION ALL SELECT 2, 3 FROM dual
UNION ALL SELECT 3, 4 FROM dual
UNION ALL SELECT 3, 5 FROM dual
UNION ALL SELECT 1, 6 FROM dual
)
, t3 AS
(
SELECT id
     , val
     , val || '' v
     , '' unit
  FROM t1
 WHERE val IS NOT NULL
 UNION ALL
SELECT a.start_node_id id
     , a.end_node_id   val
     , 'a' || a.end_node_id || 'a' v
     , DECODE(unit, 'AG', '*', 'OG', '+') unit
  FROM t2 a
     , t1 b
 WHERE b.id = a.start_node_id
)
SELECT TO_NUMBER(
       dbms_xmlgen.getxmltype(
       'SELECT ' || v || ' FROM dual').Extract('//text()'
       ) ) x
  FROM (SELECT ROW_NUMBER() OVER(ORDER BY MIN(ROWNUM)) rn
             , id
             , '('||LISTAGG(v, unit) WITHIN GROUP(ORDER BY ROWNUM)||')' v
          FROM t3
         GROUP BY id, unit
         START WITH id = 2    -- 조건
         CONNECT BY PRIOR val = id
        )
 MODEL
 RETURN UPDATED ROWS
 DIMENSION BY (rn)
 MEASURES ( id, v )
 RULES ITERATE (20)
 ( v[1] = REPLACE( v[1]
                 , 'a'||id[ITERATION_NUMBER+2]||'a'
                 , v[ITERATION_NUMBER+2]
                 )
 )
;
-- http://database.sarang.net/?inc=read&aid=40811&criteria=oracle

 


by jkson [2017.09.19 12:10:40]

와~ 판타스틱하네요.

dbms_xmlgen.getxmltyp 사용해서 하면 되겠다 생각은 해봤지만 중간 과정 만드는 게 너무 어려워서 포기했는데

역시 뇌섹남ㅋㅋ


by 햇살좋은날 [2017.09.19 12:31:35]

와.. 신기할 따름이네요;; 공부해야겠다는 다짐을 하게 되는 쿼리입니다.

감사합니다. 마농님~!!


by 우리집아찌 [2017.09.19 13:49:58]

계층 쿼리와 dbms_xmlgen.getxmltype 쓰면 될것도 같은데... 자신이 없네요..


by jkson [2017.09.19 13:54:05]

원 쿼리로 해결 못 하는 저는.. 재귀함수를 사용해봤습니다.

--재귀함수 생성
CREATE OR REPLACE FUNCTION FC_TMP_CALC(IN_NODE NUMBER, IN_GB VARCHAR2)
 RETURN NUMBER
IS
 V_FORMULA NUMBER;
 BEGIN
 FOR CUR IN
 (SELECT A.ID, A.UNIT, TO_CHAR(A.VAL) VAL
   FROM TMP_20170919_B B
      , TMP_20170919_A A
  WHERE B.START_NODE_ID = IN_NODE
    AND B.END_NODE_ID = A.ID)
 LOOP
      IF CUR.VAL IS NULL THEN
         IF V_FORMULA IS NULL THEN
            V_FORMULA := FC_TMP_CALC(CUR.ID, CUR.UNIT);
         ELSE
            IF IN_GB = 'AG' THEN
                V_FORMULA := V_FORMULA * (FC_TMP_CALC(CUR.ID, CUR.UNIT));
            ELSIF IN_GB = 'OG' THEN
                V_FORMULA := V_FORMULA + (FC_TMP_CALC(CUR.ID, CUR.UNIT));
            END IF;
         END IF;
      ELSE
         IF V_FORMULA IS NULL THEN
            V_FORMULA   := CUR.VAL;
         ELSE
            IF IN_GB = 'AG' THEN
                V_FORMULA := V_FORMULA * CUR.VAL;
            ELSIF IN_GB = 'OG' THEN
                V_FORMULA := V_FORMULA + CUR.VAL;
            END IF;
          END IF;
      END IF;
 END LOOP;
 
 RETURN V_FORMULA;
END;

--테이블 생성
CREATE TABLE TMP_20170919_A
(
  ID    NUMBER,
  UNIT  VARCHAR2(2 BYTE),
  VAL   NUMBER
)

CREATE TABLE TMP_20170919_B
(
  START_NODE_ID  NUMBER,
  END_NODE_ID    NUMBER
)

--데이터 삽입
INSERT INTO TMP_20170919_A
SELECT 0 id, 'ND' unit, NULL val FROM dual
UNION ALL SELECT 1, 'AG',  NULL FROM dual
UNION ALL SELECT 2, 'ND',  NULL FROM dual
UNION ALL SELECT 3, 'OG',  NULL FROM dual
UNION ALL SELECT 4, 'ND', 0.003 FROM dual
UNION ALL SELECT 5, 'ND', 0.004 FROM dual
UNION ALL SELECT 6, 'ND', 0.005 FROM dual

INSERT INTO TMP_20170919_B
SELECT 0 start_node_id, 1 end_node_id FROM dual
UNION ALL SELECT 1, 2 FROM dual
UNION ALL SELECT 2, 3 FROM dual
UNION ALL SELECT 3, 4 FROM dual
UNION ALL SELECT 3, 5 FROM dual
UNION ALL SELECT 1, 6 FROM dual

--사용
SELECT FC_TMP_CALC(2 --> 시작 node
                 ,NULL) VAL FROM DUAL

 


by 햇살좋은날 [2017.09.19 14:32:10]

jkson님 감사합니다. 많은 도움이 되었습니다 ^^

댓글등록
SQL문을 포맷에 맞게(깔끔하게) 등록하려면 code() 버튼을 클릭하여 작성 하시면 됩니다.
로그인 사용자만 댓글을 작성 할 수 있습니다. 로그인, 회원가입