규도자 개발 블로그

WHERE 1 = 1 TRICK 본문

Database/SQL

WHERE 1 = 1 TRICK

규도자 (gyudoza) 2019. 3. 14. 21:48

WHERE 1 = 1 TRICK

세상에 절대적인 것은 없다. 데이터베이스에서 특정 데이터를 불러와야 할 때 전용으로 만들어져있는 객체나 라이브러리를 이용하면 좋지만 불가피하게 다이나믹쿼리를 통해 구현해야 할 때가 있기 마련이다. 다이나믹쿼리로 구성돼있는 어플리케이션을 유지보수하거나, 혹은 특수한 쿼리를 이용해야할 때 등 말이다.

 그럴 때 유용하게 쓸 수 있는 게 바로 WHERE 1 = 1 트릭이다. 간단한 sql은 이곳(sqltest.net)에서 실험할 수 있다. sqltest에 들어가서 오른쪽에 Select Database를 눌러 MYSQL로 설정한 뒤에 SQL Script란에 아래 sql을 입력하자.

CREATE TABLE mysql_test (
id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY,
firstname VARCHAR(30) NOT NULL,
lastname VARCHAR(30) NOT NULL,
email VARCHAR(50),
reg_date TIMESTAMP
);

INSERT INTO `mysql_test` (`id`, `firstname`, `lastname`, `email`, `reg_date`) VALUES ('1', 'John', 'Doe', 'john.doe@sqltest.net', CURRENT_TIMESTAMP);
INSERT INTO `mysql_test` (`id`, `firstname`, `lastname`, `email`, `reg_date`) VALUES ('2', 'Alexei', 'Dragonov', 'commando@sqltest.net', CURRENT_TIMESTAMP);
INSERT INTO `mysql_test` (`id`, `firstname`, `lastname`, `email`, `reg_date`) VALUES ('3', 'Alex', 'Donald', 'commando.doenald@sqltest.net', CURRENT_TIMESTAMP);
INSERT INTO `mysql_test` (`id`, `firstname`, `lastname`, `email`, `reg_date`) VALUES ('4', 'John', 'Doremi', 'john.doremi@sqltest.net', CURRENT_TIMESTAMP);

네이밍센스가 없어서 철권 캐릭터 등을 이용했다. MYSQL을 누르면 자동생성되는 쿼리에 몇 개의 열을 추가한 게 전부이다.

 이 상태에서 오른쪽에 쿼리를 쓰면 이것저것 실험해볼 수 있다. 아래 쿼리들을 차례대로 실험해보면 오른쪽의 결과값들을 확인해볼 수 있다. 이 사이트 UI가 좀 불편하게 돼있는데 아래 파란색 Execute SQL버튼을 누르면 쿼리를 실행해볼 수 있다.

QueryResult
SELECT * FROM mysql_test
WHERE firstname LIKE 'john';



SELECT * FROM mysql_test
WHERE firstname LIKE 'john' AND lastname LIKE 'Doremi';


SELECT * FROM mysql_test
WHERE lastname LIKE 'Doremi';


SELECT * FROM mysql_test
WHERE firstname LIKE 'john' AND email LIKE '%doe%';


SELECT * FROM mysql_test
WHERE email LIKE '%commando%'


다이나믹쿼리를 짤 때 검색 조건에 따라 쿼리가 바뀌고 그 결과값을 조회하는 형태의 어플리케이션이라면 검색 조건이 없을 땐 WHERE절이 붙지 않고, 검색조건이 있을 때만 WHERE절이 들어가고 그 뒤에부터 또 AND가 들어가느냐가 바뀌게 되는데 그거때문에 조건별로 작동하는 쿼리를 분리해야할 때 조금 고민되거나 골치가 아프다.

 물론 말했다시피 이미 만들어진 라이브러리나 특정 객체를 이용해서 편하게 컬럼명과 값을 키 : value형태로 전송해 결과값을 받을 수도 있지만 이미 생쿼리로 짜여진 상태의 코드를 유지보수해야할 때나 라이브러리 등을 사용할 수 없을때 WHERE 1 = 1 트릭을 쓰기 좋다.

 WHERE 1 = 1 트릭은 사용하기 아주 간단하다. 그냥 조건문이 시작되기 전에 WHERE 1 = 1을 갈기면 된다. 그리고 추가되는 조건문들은 계속해서 앞에 AND를 달고 나오면 된다. 위 쿼리들을 WHERE 1 = 1트릭과 함께 쓰면 아래 형태가 된다. 결과는 위와 같기 때문에 따로 추가하지 않았다.

Sql
SELECT * FROM mysql_test
WHERE 1 = 1 AND firstname LIKE 'john';
SELECT * FROM mysql_test
WHERE 1 = 1 AND firstname LIKE 'john' AND lastname LIKE 'Doremi';
SELECT * FROM mysql_test
WHERE 1 = 1 AND lastname LIKE 'Doremi';
SELECT * FROM mysql_test
WHERE 1 = 1 AND firstname LIKE 'john' AND email LIKE '%doe%';
SELECT * FROM mysql_test
WHERE 1 = 1 AND email LIKE '%commando%'

차이는 그저 WHERE 1 = 1절이 하나 더 붙었을 뿐이지만 조건에 따라서 쿼리를 추가하거나 없애야할 때 처음에 무엇이 오냐에 따라서 WHERE이냐 AND냐를 고민할 필요 없이 그냥 AND로 처리하면 된다. 이것이 WHERE 1 = 1 TRICK이다.

 

잡설

인터넷에서도 WHERE 1 = 1 트릭 때문에 말이 많은데 DELETE나 UPDATE등의 쿼리 실행시 조건값이 null이면 모든 데이터에 쿼리의 영향이 가기 때문에 위험하다는 게 주장의 요지이다. 애초에 DELETE나 UPDATE같은 민감한 동작을 WHERE 1=1으로 뭉뜽그려서 수정한다는 발상 자체가 되게 이상하다고 느꼈다. 수년 전에 붙었던 논쟁이라 직접 끼진 않았지만 어플리캐이션 안에 구성돼있는 다이나믹쿼리에 DELETE나 UPDATE를 WHERE절로 때려 넣는 사람이 있나? 하는 생각이 들었다. 사실 그 문제는 dynamic query를 허술한 조건문으로 사용한다는 데에서 오는 문제인데 이 WHERE 1=1트릭과 함께 물고 늘어지는 것도 조금 납득이 가지 않고 말이다. WHERE 1=1트릭에는 죄가 없다. 사용하는 사람의 실수가 있을 뿐이지. 하지만 그걸 가지고 이건 나빴어! 절대 쓰면 안돼! 하고 이상한 논리와 함께 강력하게 주장하니 되게 이상하게 느껴졌었다.

 글의 서두에도 남겼듯이 다이나믹쿼리를 사용하지 않고 DB접근 객체나 라이브러리를 써서 안전하게 하는 게 첫번째 원칙이고, 만약에 다이나믹쿼리를 사용하게 됐을 시에 WHERE 1 = 1을 사용하면 쿼리구성이 쉽다는 게 글의 요지이다.

Comments