SQL/RDBMS

[MariaDB] 중복 파일명 처리(이름 뒤에 seq 붙이기)

dddzr 2023. 7. 3. 17:11

가장 간단하게는 파일명이 중복되지 않을 때 까지 반복문을 사용하여 확인 할 수 있다.

예시 코드입니다.

//java
String orgName = fileObj.getFileName();
String newName = NewNameService.getNewName(fileObj);
int idx = 1;
while(newName != null){
    fileObj.setFileName(orgName + "(" + (idx+1).toString() + ")");
    newName = NewNameService.getNewName(fileObj);
}

 

하지만 저는 아래와 같은 생각 때문에 쿼리한번으로 처리 하고자 했습니다.

1. 성능:

  • 반복문을 사용하는 경우: 각각의 fileName를 가져와서 (idx)를 붙여서 중복을 확인하고, 존재하지 않는 idx를 찾을 때까지 반복문을 실행해야 합니다. 이는 데이터베이스와 여러 번의 통신을 수행하므로 성능이 저하될 수 있습니다.
  • 쿼리를 사용하는 경우: 하나의 쿼리로 중복된 fileName를 확인하고, 최대 idx를 계산하여 바로 새로운 fileName를 생성할 수 있습니다. 이는 데이터베이스와 한 번의 통신만 수행하므로 성능이 개선될 수 있습니다.

2. 안전성:

  • 반복문을 사용하는 경우: 여러 번의 통신과 반복문을 통해 중복된 fileName와 최대 idx를 확인하므로, 동시에 동일한 fileName를 처리하는 다른 요청과의 충돌이 발생할 수 있습니다.
  • 쿼리를 사용하는 경우: 하나의 쿼리로 중복된 fileName와 최대 idx를 확인하므로, 데이터베이스에서 동시에 수행되는 다른 요청과의 충돌을 최소화할 수 있습니다.

 

JAVA에서

//java
String newName = NewNameService.getNewName(fileObj);
if(newName != null){
	fileObj.setFileName(newName);
}

 

중복되는 파일 이름뒤에 seq

괄호 없이 숫자 먼저 붙여 보았다.

 

ex) New 파일이 존재할 때

fileName이 New면 New1반환

 

ex) New 파일과 New1 파일이 존재할 때

fileName이 New면 New2반환

 

ex) New1 파일이 존재할 때

fileName이 New1면 New11반환

SELECT
CONCAT (#{fileName}, (
SELECT MAX(CAST(substring(FILENAME, LENGTH(#{fileName})+1) AS INTEGER)) FROM FILELIST
WHERE FOLDER = #{folder} AND FILENAME LIKE CONCAT(#{fileName}, '%'))+ 1) AS seq
FROM FILELIST
WHERE FOLDER = #{folder} AND FILENAME LIKE CONCAT(#{fileName}, '%')
ORDER BY FILENAME DESC limit 1

error) new1234를 했을 때 new12345가 아닌 new1235가 되는 에러가 있었음

 

 

중복되는 파일 이름 뒤에 (seq) 붙이기

    SELECT IF((SELECT FILENAME FROM FILELIST
    WHERE FOLDERID = #{folderId} AND FILENAME = #{fileName}) IS NOT NULL, 
    (SELECT IFNULL(
    (SELECT
    CONCAT (#{fileName}, '(', (    
    SELECT MAX(CAST(REPLACE(REPLACE(REPLACE(FILENAME, #{fileName}, ''), '(', ''), ')', '') AS INTEGER)) FROM UX_FUNCTION
    WHERE FOLDERID = #{folderId}
    AND FILENAME LIKE CONCAT(#{fileName}, '(', '%', ')') 
    AND FILENAME REGEXP CONCAT(REPLACE(REPLACE(#{fileName}, '(', '\\('), ')', '\\)' ), '\\([0-9]+\\)$' ))
    + 1, ')') AS seq
    FROM FILELIST
    WHERE FOLDERID = #{folderId}
    AND FILENAME LIKE CONCAT(#{fileName}, '(', '%', ')') 
    AND FILENAME REGEXP CONCAT(REPLACE(REPLACE(#{fileName}, '(', '\\(' ), ')', '\\)' ), '\\([0-9]+\\)$' )
    ORDER BY FILENAME DESC limit 1),
    CONCAT(#{fileName}, '(1)'))),
    NULL)

예시 1) "New"라는 파일 생성시 database에

"New" 라는 데이터가 있을 때 : "New(1)" 반환

"New", "New(1)" 라는 데이터가 있을 때 : "New(2)" 반환

 

예시 2) "New(1)"라는 파일 생성시 database에

"New(1)" 라는 데이터가 있을 때 : "New(1)(1)" 반환

"New(1)", "New(1)(1)" 라는 데이터가 있을 때 : "New(1)(2)" 반환

 

1. IF문

IF(A, B, C)

A가 참이면 B, 아니면 C를 반환합니다.

 

위의 쿼리에서

A: 입력한 fileName이 존재하는지(not null) 확인

B: fileName(idx)형태의 새 이름

C: NULL

 

2. 1-B 구문에서 IFNULL

IFNULL (A, B)

A가 NULL이 아니면 A를, NULL이면 B를 반환

 

위의 쿼리에서

A: fileName(maxidx + 1) 형태의 새 파일이름 반환

B: maxidx를 구할 수 없을 때 ( fileName(idx)형태 데이터 없고, fileName만 있을 때) fileName(1)을 반환

 

3. 2-A 구문에서 

fileName(idx)형태의 데이터 idx가 숫자가 아닌경우를 방지하기 위해 아래 쿼리 대신 REGEXP를 사용

#숫자가 아닌것도 들고 옴
FILENAME LIKE CONCAT(#{fileName}, '(', '%', ')')
#숫자만 들고옴
REGEXP CONCAT(#{fileName}, '\\([0-9]+\\)' )

REGEXP는 정규표현식을 이용할 때 사용한다.

 

 

4. 아래 에러에 따른 수정

*위의 코드는 수정한 상태

 

error) 파일이름에 괄호가 포함되었을 때 REGEXP에서 이렇게 고쳐줘야함.

REGEXP CONCAT('New\\(3\\)', '\\([0-9]+\\)' ))

이걸 스프링매퍼에서 하려면 아래와 같음.

REGEXP CONCAT(REPLACE(REPLACE(#{fileName}, '(', '\\('), ')', '\\)' ), '\\([0-9]+\\)' ))

 

error) New(1)(3)가 있을 때 New를 입력했을 때 14으로 나옴

#수정전
FILENAME REGEXP CONCAT(#{fileName}, '\\([0-9]+\\)' ))
#수정후
FILENAME REGEXP CONCAT(#{fileName}, '\\([0-9]+\\)$' ))

 '$'는 문자열의 끝을 나타내며, 이를 추가하여 숫자 뒤에 다른 문자가 오는 경우를 제외할 수 있습니다.