UPDATE TEST_TB A INNER JOIN (
WITH RECURSIVE T_SAMPLE AS (
SELECT MAIN_CODE AS TOP_NUM, MAIN_CODE, 1 AS MAIN_LV,
RIGHT(CONCAT('000', ROW_NUMBER() OVER (ORDER BY MAIN_CODE)), 2) AS NNO
FROM TEST_TB
WHERE KEY_NO = '1' AND UP_MAIN_CODE IS NULL
UNION ALL
SELECT
B.TOP_NUM, A.MAIN_CODE, B.MAIN_LV + 1 AS MAIN_LV,
CONCAT(C.NNO, RIGHT(CONCAT('000', ROW_NUMBER() OVER (PARTITION BY A.UP_MAIN_CODE ORDER BY A.MAIN_CODE)), 3)) AS NNO
FROM TEST_TB AS A
INNER JOIN T_SAMPLE B
ON A.UP_MAIN_CODE = B.MAIN_CODE
WHERE
A.KEY_NO = '1'
)
SELECT * FROM T_SAMPLE
) B ON A.MAIN_CODE = B.MAIN_CODE
SET
A.TOP_MAIN_CODE = B.TOP_NUM,
A.MAIN_LV = B.MAIN_LV,
A.NNO = B.NNO
WHERE
A.KEY_NO = '1';
-> 실무에서 쓰고있어서 테이블, 컬럼명은 보안상 제가 임의로 바꾸었습니다.
위 쿼리를 돌렸을때
Restrictions imposed on recursive definitions are violated for table 'T_SAMPLE' 에러가 발생하였습니다.
T_SAMPLE가 WITH절 명칭입니다.
MySQL Server Blog에 보면 RECURSIVE 문장 안에서 집계함수 사용이 안된다는군요.
a recursive SELECT mustn’t contain GROUP BY, aggregate functions
(like SUM), ORDER BY, LIMIT, DISTINCT (this rule doesn’t apply to the non-recursive/anchor/seed SELECT)
Mariadb knowledge base에도 댓글이 있네요. 문서상에는 명시된 곳을 못찾았습니다.
It took a long time to find out that aggregate functions cannot be used in the recursive portion of a CTE.
https://mariadb.com/kb/en/recursive-common-table-expressions-overview/
아래처럼 SUM 함수만 넣어도 안되네요..
MariaDB [test]> WITH RECURSIVE my_cte AS -> ( -> SELECT 1 AS n, 1 s1 -> UNION ALL -> SELECT 1+n,sum(n) FROM my_cte WHERE n<10 # <- recursive SELECT -> ) -> SELECT * FROM my_cte; ERROR 4008 (HY000): Restrictions imposed on recursive definitions are violated for table 'my_cte'
-- row_number 을 recursive 밖으로 빼봤습니니다. (emp 테이블로 테스트) WITH t1 AS ( SELECT deptno , empno , mgr , CAST(LPAD(ROW_NUMBER() OVER(PARTITION BY mgr ORDER BY empno), 3, '0') AS VARCHAR(99)) rn FROM emp ) , t3 AS ( WITH RECURSIVE t2 AS ( SELECT deptno , empno , mgr , empno top_num , 1 lv , rn nno FROM t1 WHERE mgr IS NULL AND deptno = 10 UNION ALL SELECT a.deptno , a.empno , a.mgr , b.top_num , b.lv + 1 lv , CONCAT(b.nno, a.rn) nno FROM t1 a INNER JOIN t2 b WHERE a.mgr = b.empno AND a.deptno = b.deptno ) SELECT * FROM t2 ) SELECT * FROM t3 ;
https://rampart81.github.io/post/mysql_get_row_position/
위 블로그 참고해서 다른방법으로 ROW_NUMBER()안쓰고 순위매기기 처리하니깐 잘되네요 감사합니다.
아 따로 집계함수쓴것을 WITH절로 빼라고 하셨는데
아래 쿼리 같은경우
WITH절로 따로 빼게되면
안에 INNER JOIN으로 RECURSIVE처리된 T_SAMPLE테이블이 존재하지 않는다고 나오는데
올려주신 예시처럼
t1 명칭으로 따로 집계함수 포함된것을 뺄때
저같은 경우 재귀호출 with절 명칭인 T_SAMPLE 까지 가져가게되어서
그런경우 T_SAMPLE테이블이 없다고 나와
궁금하여 질문드립니다.
->
SELECT
B.TOP_NUM, A.MAIN_CODE, B.MAIN_LV + 1 AS MAIN_LV,
CONCAT(C.NNO, RIGHT(CONCAT('000', ROW_NUMBER() OVER (PARTITION BY A.UP_MAIN_CODE ORDER BY A.MAIN_CODE)), 3)) AS NNO
FROM TEST_TB AS A
INNER JOIN T_SAMPLE B
ON A.UP_MAIN_CODE = B.MAIN_CODE
WHERE
A.KEY_NO = '1'
가지고 있던 유사한 테이블로 테스트 했습니다.
테이블명 컬럼명만 해당 이름으로 바꾸시면 됩니다.
WITH t1 AS ( SELECT key_no , main_code , up_main_code , CAST(LPAD( ROW_NUMBER() OVER(PARTITION BY key_no, up_main_code ORDER BY main_code) , 3, '0') AS VARCHAR(99)) rn FROM test_tb ) , t3 AS ( WITH RECURSIVE t2 AS ( SELECT key_no , main_code , up_main_code , main_code top_num , 1 main_lv , rn nno FROM t1 WHERE up_main_code IS NULL AND key_no = '1' UNION ALL SELECT a.key_no , a.main_code , a.up_main_code , b.top_num , b.main_lv + 1 main_lv , CONCAT(b.nno, a.rn) nno FROM t1 a INNER JOIN t2 b WHERE a.up_main_code = b.main_code AND a.key_no = b.key_no ) SELECT * FROM t2 ) SELECT * FROM t3 ;
안녕하세요.
어제 참고하라신 쿼리를 적용해서 테스트를 해보았는데
RECURSIVE t2 WITH절안에 union all아래있는 SELECT문에서
b.main_lv + 1 main_lv에 의해서 재귀를 돌면서
main_lv가 증가되면 CONCAT(b.nno, a.rn) nno에 의해서
예를들어 main_lv가 1인경우 001인것이
2가 되는 경우 재귀를 돌게 되므로 CONCAT(b.nno, a.rn)에 의해서
001001 이렇게 되어야 하는데 001로 그대로 조회되서
t3 로 with recursive를 감싸서 select문을 조회해서 그런지
재귀가 적용이 안되는것을 보면
t3를 다시 recursive처리를 해야되는건지 궁금하여 질문드립니다.