본문 바로가기
프로그래밍/TSQL

[MSSQL] 문자열 비교하기 ( 동일한 부분만 리턴하기 )

by 정리 습관(★arranging★) 2020. 4. 14.
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

댓글