문자열 합해서 검색 0 8 4,256

by 단테 [SQL Query] 쿼리 [2015.07.13 16:26:04]


안녕하세요, DB 초보입니다.

보기 좋게 코드 누르고 올리려 하니 코드창에서 등록이 안되네요.

제 업무에 필요한 로직이 있어 Group By 에서 문자열 합치기 - 버전별 정리 를 보고

하려고 하니 잘 되지 않아 도움 청해 봅니다.

아래와 같은 테이블이 두개 있고 data를 alpha로 구분하여 보여줘야 합니다.

그런데 having 절에 있는 'data1,data3' 와 같이 사용자의 선택이 들어 갑니다.

(having 절의 split 함수는 인터넷에서 구분자로 분류해주는 함수를 찾아서 작성한 것입니다)

즉 data1의 값이나 data3의 값과 연관된 alpha의 항목과 관련 내용들을 보여 줘야 합니다.

그런데 문제는 문자열 검색으로 찾으려면 사용자의 조건을 파싱해서 exists 안에 or로 계속 연결해야 하고

이게 정해진 값이나 수가 아니기 때문에 계속 길어 질수 있고 다른 조건에 의해서 exists 가 and나 or로 여러개 들어 갈수 있어서 복잡해 질것 같아서 사용자의 조건을 테이블로 만들고 그 값을 포함하고 있는 것을 찾는데 data가 길어져 4000 바이트를 넘어 갈수도 있습니다.

문자열 합치는 함수 중에 유일하게 사용할 수 있는 것이 listagg 이든데 이건 값이 길어져서 4000 바이트 넘어갈 경우 사용할 수 없고 XMLAgg는 having 절에서 그룹함수는 사용할수 없다고 합니다.

좋은 방법이 없을까요?

with tbl1(alpha, num) as
(
 select 'keyA', 'key1' from dual
 union all
 select 'keyA', 'key2' from dual
 union all
 select 'keyB', 'key1' from dual
 union all
 select 'keyB', 'key2' from dual
 union all
 select 'keyB', 'key3' from dual
),
     tbl2(num, data) as
(
 select 'key1', 'data1' from dual
 union all
 select 'key1', 'data1' from dual
 union all
 select 'key1', 'data2' from dual
 union all
 select 'key1', 'data2' from dual
 union all
 select 'key2', 'data1' from dual
 union all
 select 'key2', 'data2' from dual
 union all
 select 'key2', 'data3' from dual
 union all
 select 'key3', 'data0' from dual
)
SELECT tbl1.alpha, tbl1.num, wm_concat(distinct tbl2.data) concat_data
 from tbl1, tbl2
 where 1 = 1
     and tbl1.num = tbl2.num
 group by tbl1.alpha, tbl1.num
 having (exists
--                (select 1 from table(split(concat_data, ',')) a, table(split('data1,data3')) b where  a.column_value = b.column_value)
--                (select 1 from table(split(wm_concat(distinct tbl2.data), ',')) a, table(split('data1,data3')) b where  a.column_value = b.column_value)
--                (select 1 from table(split(ListAgg(tbl2.data, ',') WITHIN GROUP(ORDER BY alpha), ',')) a, table(split('data1,data3')) b where  a.column_value = b.column_value)
                (select 1 from table(split(SUBSTR(XMLAgg(XMLElement(data, ',', tbl2.data) ORDER BY alpha).Extract('//text()'), 2), ',')) a, table(split('data1,data3')) b where  a.column_value = b.column_value)
           )
;

by 마농 [2015.07.13 16:43:33]

사용자 입력조건에 따른 출력결과값을 Case 별로 보여주세요. 간략한 설명과 함께.


by 마농 [2015.07.13 18:09:47]
SELECT a.alpha, a.num
  FROM tbl1 a
     , tbl2 b
 WHERE a.num = b.num
   AND INSTR(','||:v_data||',', ','||b.data||',') > 0
 GROUP BY a.alpha, a.num
-- OR 조건인 경우 Having 필요 없음. AND 조건인 경우에만 Having 사용
-- HAVING COUNT(DISTINCT b.data) = REGEXP_COUNT(:v_data, ',') + 1
;

 


by 창조의날개 [2015.07.13 18:16:12]

SELECT tbl1.alpha, tbl1.num, wm_concat(distinct tbl2.data) concat_data
 from tbl1, tbl2
 where 1 = 1
     and tbl1.num = tbl2.num
 group by tbl1.alpha, tbl1.num
having REGEXP_LIKE(wm_concat(distinct tbl2.data),'data1|data3')
;

 


by 단테 [2015.07.13 20:16:53]

To, 마농님

계속 고민 하는 동안 답변이 달렸네요, 감사드립니다.

우선 입력 데이터는 사용자가 체크할수 있는 리스트에서 선택하는 것이기 때문에 유동적으로

'data1,data2', 'data2', 'data0, data3', ... 등등으로 들어 올수 있습니다.

검색 조건은 기본적으로 'data1,data2'를 선택했으면 'data1' 이나 'data2'를 포함한 alpha를 출력해야 합니다.

그런데 이 선택값들은 유동적으로 선택할 수 있기 때문에 이 선택값들을 어떻게 조합할 것인지도 사용자가 라디오버튼으로 선택합니다. 즉 'data1,data2', 'data0,data3' 의 두개의 값('' 기호가 하나의 값)을 선택하고 or을 선택하면 data1이나 data2를 포함한 값이나 data0나 data3를 포함한 값을 출력하라는 뜻입니다.

그러나 and 조건을 선택하면 data1이나 data2를 포함한 값 중에서 data0나 data3를 포함한 값을 출력하라가 됩니다. 사실 or 조건에서는 having 절이 필요 없는데 같은 루틴으로 and, or만 바꾸면 되도록 하는 것이 더 이해가 쉬울거라 생각하고 작성했습니다.

창조의 날개님께서 달아 주신 답변으로 가능할 것으로 보입니다. regexp 를 생각 못했네요,

REGEXP_LIKE나 REGEXP_COUNT 등으로 하면 가능할 것으로 보입니다.

더 좋은 방법이 있다면 알려주시면 감사하겠습니다.

 


by 단테 [2015.07.13 20:17:58]

To. 창조의날개님

들어 오는 ','만 '|'로 바꾸면 될것 같다는 생각이 듭니다. 감사드립니다.


by 단테 [2015.07.13 20:21:46]

참 여담으로 테스트 도중 WM_CONCAT 가 여러번 사용되는 것이 안 좋을 것 같아

위의 쿼리들을 한번 더 감싸서  인라인뷰로 만드니 아래와 같은 에러가 발생하네요. WM_CONCAT에서 발생하는 듯 합니다.

ORA-22922: 존재하지 않는 LOB 값


by 창조의날개 [2015.07.14 09:04:50]

WM_CONCAT이든 다른 함수를 사용하든 문자열이 4000을 넘어서면 VARCHAR 타입으로 해결이 되지 않습니다.

아무래도 문자열로 합쳐서 비교 하는 것은 어려울듯 합니다.

아래 마농님께서 만들어주신 샘플 쿼리를 이용해서 다이나믹 쿼리로 프로그래밍 하시는것이 좋을 듯 합니다.


by 마농 [2015.07.14 08:48:58]

입력 인자는 다음 3개 라고 가정하고
:v_data1 = 첫번째 인자로 반드시 입력됨
:v_data2 = 두번째 인자로 생략 가능
:v_gubun = 두번째 인자가 없으면 Null, 있으면 'AND' 또는 'OR' 입력
 

SELECT a.alpha, a.num
  FROM tbl1 a
     , tbl2 b
 WHERE a.num = b.num
   AND (  INSTR(','||:v_data1||',', ','||b.data||',') > 0
       OR INSTR(','||:v_data2||',', ','||b.data||',') > 0 )
 GROUP BY a.alpha, a.num
 HAVING MAX(SIGN(INSTR(','||:v_data1||',', ','||b.data||',')))
      + MAX(SIGN(INSTR(','||:v_data2||',', ','||b.data||',')))
        >= DECODE(:v_gubun, 'AND', 2, 1)
;

 

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