프로그래밍/TSQL
[MSSQL] 문자열 비교하기 ( 동일한 부분만 리턴하기 )
정리 습관(★arranging★)
2020. 4. 14. 16:41
728x90
두개의 문자열을 비교해서 동일한 부분만 출력하고 싶을 때 아래 방식으로 함수를 구현할 수 있습니다.
아래 예시는 동일한 문자열에 경우에 한해서 작성되었고,
목적은 아래와 같이 문자열이 입력되었을 때 '/' 문자열 기준으로 동일하게 분리된 문자열만 살리는 일종의
XOR 연산과 같은 동작을 구현하는데 있습니다.
문자열 1 : 가/나다/라/마/바사
문자열 2 : 가나다/라마/바사
결과 : 가나다/라마/바사
사용 기술 : CTE, 재귀, STUFF
MSSQL, TSQL 로 문자열 만지기, 재귀(RECURSIVE) 구현하기 재밋네요
code 공유 드립니다.
DECLARE @RESULT VARCHAR(100), @STR1 VARCHAR(100),@STR2 VARCHAR(100)
DECLARE @SEP VARCHAR(2)
DECLARE @MAX INT
SET @STR1 = '가나다/라바사'
SET @STR2 = '가나다/라/바사'
--SET @STR1 = '트와이스/짱'
--SET @STR2 = '트/와/이스짱'
SET @SEP= '/'
SET @MAX = CASE WHEN LEN(@STR1) > LEN(@STR2) THEN LEN(@STR1) ELSE LEN(@STR2) END
--1차 가공한자씩 봐서 '/'앞뒤 지우기
IF @SEP = LEFT(@STR1,1)
SET @STR1 = RIGHT(@STR1,LEN(@STR1)-1)
IF @SEP = RIGHT(@STR1,1)
SET @STR1 = LEFT(@STR1,LEN(@STR1)-1)
IF @SEP = LEFT(@STR2,1)
SET @STR2 = RIGHT(@STR2,LEN(@STR2)-1)
IF @SEP = RIGHT(@STR2,1)
SET @STR2 = LEFT(@STR2,LEN(@STR2)-1)
IF REPLACE(@STR1,@SEP,'') = REPLACE(@STR1,@SEP,'')
BEGIN
;WITH EACH(A_NAME,B_NAME,OFFSET_A,A_KEY,OFFSET_B,B_KEY)
AS (
--ANCHOR
SELECT @STR1,@STR2
,1 AS OFFSET_A,SUBSTRING(@STR1,1,1) AS KEY_A
,1 AS OFFSET_B,SUBSTRING(@STR2,1,1) AS KEY_B
UNION ALL
--RECURSIVE
SELECT @STR1,@STR2
, CASE WHEN SUBSTRING(@STR1,OFFSET_A+1,1) = SUBSTRING(@STR2,OFFSET_B+1,1)
THEN OFFSET_A+1
ELSE
CASE WHEN SUBSTRING(@STR2,OFFSET_B+1,1) = @SEP
THEN OFFSET_A
ELSE OFFSET_A+1
END
END AS OFFSET_A
, SUBSTRING(@STR1, CASE WHEN SUBSTRING(@STR1,OFFSET_A+1,1) = SUBSTRING(@STR2,OFFSET_B+1,1)
THEN OFFSET_A+1
ELSE
CASE WHEN SUBSTRING(@STR2,OFFSET_B+1,1) = @SEP
THEN OFFSET_A
ELSE OFFSET_A+1
END
END,1)
, CASE WHEN SUBSTRING(@STR1,OFFSET_A+1,1) = SUBSTRING(@STR2,OFFSET_B+1,1)
THEN OFFSET_B+1
ELSE
CASE WHEN SUBSTRING(@STR1,OFFSET_A+1,1) = @SEP
THEN OFFSET_B
ELSE OFFSET_B+1
END
END AS OFFSET_B
, SUBSTRING(@STR2, CASE WHEN SUBSTRING(@STR1,OFFSET_A+1,1) = SUBSTRING(@STR2,OFFSET_B+1,1)
THEN OFFSET_B+1
ELSE
CASE WHEN SUBSTRING(@STR1,OFFSET_A+1,1) = @SEP
THEN OFFSET_B
ELSE OFFSET_B+1
END
END,1)
FROM EACH AA
WHERE OFFSET_A < @MAX AND OFFSET_B < @MAX
--EXIT CONDITION
)
-- SAME CHARCTER ONLY
,RESULT
AS
(
SELECT *
FROM EACH
WHERE A_KEY = B_KEY
)
-- RESULT SET
SELECT @RESULT = KEYS
FROM ( SELECT DISTINCT REPLACE(STUFF((SELECT '$'+A_KEY
FROM RESULT FOR XML PATH('')),1,1,''),'$','') AS KEYS
FROM RESULT ) A
END
ELSE
SET @RESULT = NULL
SELECT @RESULT
구현 방식
- 1 입력 문자열에 대한 유효성 체크 : 입력 문자열의 오류 상황을 제어한다.
--1차 가공한자씩 봐서 '/'앞뒤 지우기
IF @SEP = LEFT(@STR1,1)
SET @STR1 = RIGHT(@STR1,LEN(@STR1)-1)
IF @SEP = RIGHT(@STR1,1)
SET @STR1 = LEFT(@STR1,LEN(@STR1)-1)
IF @SEP = LEFT(@STR2,1)
SET @STR2 = RIGHT(@STR2,LEN(@STR2)-1)
IF @SEP = RIGHT(@STR2,1)
SET @STR2 = LEFT(@STR2,LEN(@STR2)-1)
- 2 문자열을 각각 한단어씩 분류하기 : CTE 재귀를 이용하여 각 단어를 순회하여 성능을 확보한다.
;WITH EACH(A_NAME,B_NAME,OFFSET_A,A_KEY,OFFSET_B,B_KEY)
AS (
--ANCHOR
SELECT ...
UNION ALL
--RECURSIVE
SELECT ...
- 3 문자열 중 긴문자 기준으로 재귀 종료조건 설정 : 재귀의 종료는 둘중 긴문자열의 길이
SET @MAX = CASE WHEN LEN(@STR1) > LEN(@STR2) THEN LEN(@STR1) ELSE LEN(@STR2) END
.
.
.
WHERE OFFSET_A < @MAX AND OFFSET_B < @MAX
- 4 재귀 수행하면서 각 문자열을 비교하고 분리조건 '@SEP'에 해당하는 문자열에 dummy 삽입
--문자열 1 기준으로 설명
SUBSTRING(@STR1, CASE WHEN SUBSTRING(@STR1,OFFSET_A+1,1) = SUBSTRING(@STR2,OFFSET_B+1,1)
THEN OFFSET_A+1
ELSE
CASE WHEN SUBSTRING(@STR2,OFFSET_B+1,1) = @SEP
THEN OFFSET_A
ELSE OFFSET_A+1
END
END,1)
--해당 문자열이 잘릴위치에서 반대편 문자열이 @SEP에 해당한다면
--현재 문자는 동일한 오프셋을 유지하여 dummy값(이전문자)를 유지한다.
--각 문자에 @SEP의 위치와 개수가 다름으로 인한 처리이며
--최종 결과물 출력 시 동일한 위치의 문자만 출력되도록 처리하므로 해당 문자는 제거된다.
- 5 양쪽이 모두 일치하는 결과 조회 : 생성된 결과(DUMMY포함)를 정리하여 출력한다.
,RESULT
AS
(
SELECT *
FROM EACH
WHERE A_KEY = B_KEY
)
- 6 레코드 단위 문자열을 행으로 변경 : FOR XML과 STUFF 를 활용하여 문자열을 이어 붙이고 연결문자는 제거한다.
SELECT @RESULT = KEYS
FROM ( SELECT DISTINCT REPLACE(STUFF((SELECT '$'+A_KEY
FROM RESULT FOR XML PATH('')),1,1,''),'$','') AS KEYS
FROM RESULT ) A