쿼리를 실행했더니 ERROR 1064 (42000): You have an error in your SQL syntax; ... near '...' at line 1이 떴다면, 서버가 그 문장을 끝까지 해석하지 못하고 멈춘 것입니다. 데이터나 권한 문제가 아니라 파서가 문장 구조를 못 읽은 상태입니다. near 뒤에 인용된 조각이 파서가 멈춘 지점이고, 고칠 자리는 대개 그 조각의 바로 앞입니다.
near 뒤 문자열은 멈춘 지점이지 틀린 지점이 아닙니다
공식 오류 참조에서 1064번은 심볼 ER_PARSE_ERROR, SQLSTATE 42000이고 메시지 형식은 %s near '%s' at line %d입니다. 가운데 자리가 파서가 더 진행하지 못한 위치의 원문 조각입니다. 공식 예약어 문서의 예가 이 성질을 잘 보여줍니다.
CREATE TABLE interval (begin INT, end INT);
-- ERROR 1064 (42000): You have an error in your SQL syntax ... near 'interval (begin INT, end INT)'
near가 가리키는 것은 interval부터입니다. 파서는 CREATE TABLE까지 정상으로 읽다가 다음 토큰이 예약어라 더 가지 못했습니다. 인용된 조각만 들여다보면 답이 안 나오고 그 왼쪽을 봐야 합니다.
제 맥에는 MySQL이 설치돼 있지 않아 위 출력은 매뉴얼 기준으로 인용했습니다. 대신 같은 원리를 기본 설치된 sqlite3로 실행해 봤습니다. 멈춘 위치를 캐럿으로 표시해 줍니다.
$ sqlite3 :memory: 'CREATE TABLE t(id INTEGER, "order" INTEGER); SELECT order FROM t;'
Error: in prepare, near "order": syntax error
SELECT order FROM t;
^--- error here
번호와 문구는 제품마다 다르지만 파서가 예약어 토큰 앞에서 멈추는 동작은 같습니다. line 번호는 서버로 보낸 문장 기준이라, 애플리케이션이 쿼리를 한 줄로 조립하면 아무리 길어도 항상 line 1입니다.
원인별로 좁혀 가는 순서
예약어를 컬럼이나 테이블 이름으로 쓴 경우
공식 문서에서 (R)이 붙은 단어가 예약어입니다. ORDER, GROUP, KEY, RANK, DESC, INTERVAL, SYSTEM처럼 업무 용어로 쓰고 싶은 단어가 많습니다. 이름으로 쓰려면 백틱으로 감쌉니다.
-- 파서가 order 앞에서 멈춥니다
SELECT order, rank FROM sales_log;
-- 백틱으로 식별자임을 알려 줍니다
SELECT `order`, `rank` FROM sales_log;
버전이 올라가면 예약어도 늘어납니다. RANK는 윈도우 함수가 들어온 8.0부터 예약어라, 5.7에서 돌던 쿼리가 올린 뒤 1064로 바뀌기도 합니다. 예외는 하나입니다. 점 뒤 한정 이름은 식별자 자리가 확실하므로 mydb.interval처럼 인용 없이 통과합니다.
따옴표 종류와 닫히지 않은 문자열
백틱은 이름을, 작은따옴표는 문자열 값을 감쌉니다. 둘을 바꿔 쓰거나 한쪽을 안 닫으면 바로 멈춥니다. 문서나 메신저에서 복사한 쿼리에는 스마트 따옴표가 섞여 들어오기도 합니다. sqlite3로 두 경우를 실행해 봤습니다.
$ sqlite3 :memory: "CREATE TABLE t(id INTEGER, name TEXT); SELECT * FROM t WHERE name = 'kim;"
Error: in prepare, unrecognized token: "'kim;"
$ sqlite3 :memory: "CREATE TABLE t(id INTEGER, name TEXT); SELECT * FROM t WHERE name = ‘kim’;"
Error: in prepare, no such column: ‘kim’
두 번째가 더 까다롭습니다. 스마트 따옴표는 문자열이 아니라 식별자로 읽혀서 메시지가 원인과 동떨어진 말을 합니다. 눈으로 구분이 안 되면 편집기에서 해당 유니코드 문자를 찾는 편이 빠릅니다.
코드에서 문자열을 조립하다 빠진 공백과 쉼표
PHP, 파이썬, JDBC에서 쿼리를 이어 붙일 때 조각 사이에 공백이 없으면 두 단어가 붙어 버립니다.
$ sqlite3 :memory: "CREATE TABLE t(id INTEGER, name TEXT); SELECT * FROM tWHERE id = 1;"
Error: in prepare, near "=": syntax error
SELECT * FROM tWHERE id = 1;
^--- error here
tWHERE가 테이블 이름으로 읽혀서 파서는 그다음 =에서야 멈췄습니다. 멈춘 자리와 원인 자리가 한 단어 떨어진 전형적인 모습입니다. 조용히 통과해서 더 곤란한 경우도 있습니다. 선택 목록에서 쉼표가 빠진 SELECT id name FROM t는 오류가 아니라 id에 name 별칭을 붙인 문장이 됩니다. 실제로 sqlite3에서 종료 코드 0으로 성공하는 것을 확인했습니다.
값을 문자열로 붙이지 않고 자리 표시자로 넘기는 준비된 구문을 쓰면 이런 실수는 대부분 사라집니다. 값에 작은따옴표가 있어도 드라이버가 처리하므로 이스케이프 실수도 줄어듭니다. 여러 테이블을 엮는 문장은 조각이 길어 실수가 잦은데, MySQL JOIN 쿼리 기본형처럼 완성된 형태를 먼저 클라이언트에서 통과시킨 뒤 코드로 옮기면 안전합니다.
서버 버전과 제품이 다른 문법
공통 테이블 식(WITH)과 윈도우 함수는 8.0부터라, 5.7 서버에 보내면 WITH 앞에서 멈춥니다. 메시지에 "check the manual that corresponds to your MySQL server version"이 들어 있는 이유입니다. MariaDB는 별도 제품이라 도입 버전도 다릅니다. 접속한 서버부터 확인합니다.
SELECT VERSION();
구분할 것이 하나 있습니다. 서브쿼리의 IN 조합에 LIMIT을 쓰면 1064가 아니라 ER_NOT_SUPPORTED_YET 계열이 납니다. 문법은 읽혔는데 그 조합을 아직 지원하지 않는다는 뜻이라, 파생 테이블로 감싸 형태를 바꿔야 합니다.
CREATE TABLE과 덤프 파일 복원
테이블 생성문은 자료형, DEFAULT, AUTO_INCREMENT 자리에서 오타가 잦습니다. 마지막 컬럼 뒤에 쉼표를 남긴 채 괄호를 닫으면 그 괄호 앞에서 멈춥니다. 덤프 복원에는 함정이 하나 더 있습니다. DELIMITER는 서버로 가는 SQL 문이 아니라 mysql 클라이언트가 직접 해석하는 명령이라, 이 명령을 모르는 관리 도구로 덤프를 실행하면 DELIMITER 줄 자체가 1064를 냅니다. 프로시저나 트리거가 든 덤프는 mysql 클라이언트로 복원하는 것이 기본입니다.
문제 구간을 빨리 좁히는 방법
긴 쿼리는 반으로 잘라 실행해 보는 것이 가장 빠릅니다. 앞 절반이 통과하면 뒤 절반에 문제가 있습니다. 서브쿼리나 JOIN이 여러 개면 안쪽 문장을 하나씩 실행합니다. 애플리케이션에서만 1064가 난다면 코드가 만든 최종 문자열을 mysql 클라이언트에 그대로 붙여 넣습니다.
mysql -u <user> -p <db> -e "SELECT \`order\` FROM sales_log LIMIT 1;"
EXPLAIN은 이 단계에서 쓸모가 없습니다. 실행 계획을 보려면 문장이 먼저 파싱을 통과해야 해서, 문법이 깨진 쿼리는 EXPLAIN을 붙여도 같은 1064를 냅니다. 실행 계획은 문장이 통과한 뒤 SELECT 쿼리 성능을 볼 때 쓰는 도구입니다.
예약어 목록을 정확히 확인하는 법
기억에 의존하지 말고 접속한 버전의 문서를 봅니다. Keywords and Reserved Words 페이지에 버전별 목록이 있고, (R)이 붙은 것이 인용해야 하는 예약어입니다. 오류 번호별 정의는 Server Error Reference에서 확인할 수 있습니다.
MySQL 8.0부터는 서버에 직접 물어볼 수도 있습니다. 매뉴얼 기준으로 다음 뷰가 서버가 아는 키워드와 예약 여부를 돌려줍니다.
SELECT WORD FROM INFORMATION_SCHEMA.KEYWORDS WHERE RESERVED = 1 ORDER BY WORD;
새 테이블을 설계할 때 컬럼 이름을 이 목록과 대조해 두면 나중에 백틱을 붙이러 다닐 일이 줄어듭니다.
자주 묻는 질문
near 뒤가 빈 문자열이면 어디를 봐야 하나요
near '' at line 1처럼 조각이 비어 있으면 파서가 문장 끝까지 갔는데 필요한 토큰을 못 만났다는 뜻입니다. 닫지 않은 괄호나 따옴표, 조건을 안 적은 WHERE, 조립 과정에서 잘려 나간 뒷부분이 여기 해당합니다. 문장 끝부터 거꾸로 읽으며 짝을 맞춰 보는 편이 빠릅니다.
관리 도구에서는 되는데 코드에서만 1064가 납니다
도구에 붙여 넣은 문장과 코드가 실제 보낸 문장이 다른 경우가 대부분입니다. 조립된 최종 문자열을 로그로 출력해 둘을 비교합니다. 값이 비어 WHERE id = 뒤가 사라졌거나, 세미콜론으로 이은 여러 문장을 드라이버가 허용하지 않는 설정일 수도 있습니다.
1064와 1062, 1146은 어떻게 다른가요
세 오류는 실패한 단계가 다릅니다. 아래 표로 갈래를 먼저 나누면 헛수고를 줄일 수 있습니다.
| 번호 | 심볼 | 실패한 단계 | 확인할 곳 |
|---|---|---|---|
| 1064 | ER_PARSE_ERROR | 문장 해석 | 문법, 예약어, 따옴표 |
| 1062 | ER_DUP_ENTRY | 값 저장 | 기본 키와 UNIQUE 인덱스 |
| 1146 | ER_NO_SUCH_TABLE | 대상 찾기 | 테이블 이름과 스키마 |
1062라면 문법은 통과한 것이므로 중복 키가 어디서 겹쳤는지 확인하는 순서를 따라가면 되고, 1146이 리눅스 서버에서만 난다면 테이블 이름 대소문자 설정을 함께 살펴보는 것이 좋습니다.
'개발 문제 해결' 카테고리의 다른 글
| 리눅스 용량 큰 파일 찾기: df·du·find로 범인 잡는 순서 (0) | 2026.09.11 |
|---|---|
| adb 명령어 모음: 기기 연결 안 될 때부터 apk 설치와 로그 추출까지 (0) | 2026.09.11 |
| Linux 파일을 지웠는데 용량이 안 줄어들 때: df와 du가 다른 이유 (0) | 2026.09.08 |
| targetSdk 36 업데이트 후 상태바와 하단 버튼이 겹칠 때: WindowInsets 확인 순서 (0) | 2026.09.08 |
| Android 16KB 페이지 크기 오류, 내 앱의 어떤 라이브러리부터 확인할까 (0) | 2026.09.08 |
댓글