(라라벨+MSSQL) 7405 오류 나는 쿼리 실행하기

eojin·5일 전
0

1분 코드 스니펫

목록 보기
2/2

tl;dr

원래 이렇게 실행하던 쿼리라면

$result = DB::connection('foo')->select("SELECT * FROM bar");

이렇게 고쳐서 쓴다.

// 이 코드블록을 코드#1 이라 부르겠음
// 실행 잘됨
$pdo = DB::connection('foo')->getPdo();
$pdo->query('SET ANSI_NULLS ON; SET ANSI_WARNINGS ON;');
$st = $pdo->prepare("SELECT * FROM bar");
$st->execute();
$result = $st->fetchAll(\PDO::FETCH_OBJ);

사연

2개 이상의 데이터베이스를 무식하게 JOIN 걸어서 쓰고있는 우리 회사.. ㅎㅎ 덕분에 강제로 이것저것 배우는데 그중의 하나는 SQL 7405 오류이다. 자세히는 나도 모르니까 대충 이 정도만 알고 지나가자:

ANSI_NULLS 옵션이 ON인 경우, WHERE column_name = NULL을 사용하는 SELECT 문은 column_namenull 값이 있어도 0행을 반환합니다. WHERE column_name <> NULL을 사용하는 SELECT 문은 column_nameNull 이외의 값이 있어도 0행을 반환합니다.
(중략) 분산 쿼리를 실행할 때는 ANSI_NULLSON으로 설정해야 합니다.
출처

(ANSI_WARNINGS를) ON으로 설정한 경우 SUM, AVG, MAX, MIN, STDEV, STDEVP, VAR, VARP, COUNT 등의 집계 함수에 NULL 값이 있으면 경고 메시지가 생성됩니다. OFF로 설정한 경우에는 경고가 발생하지 않습니다.
(중략) 중요! 분산 쿼리를 실행할 때는 ANSI_WARNINGSON으로 설정해야 합니다.
출처

암튼.. 지금 나는 내 DBeaver에서 잘만 돌아가는 쿼리가 PHP 소스에서 돌아가질 않아서 돌아버리든지 돌아가실 지경이고..

// 이 코드블록을 코드#2 라고 부르겠음
// 실행않됌
$result = DB::connection('foo')->select(
    "SET ANSI_NULLS ON; SET ANSI_WARNINGS ON;
    SELECT * FROM bar"
);

어떡하나.. 하다가 설마?? 싶어서 DB 조회의 원점으로 돌아가 PDO를 꺼내서 직접 쿼리를 때리는 방식을 시도해 보았다.

어... 된다.

왜 되느냐???

상기 코드#2 는 라라벨에 의해 대충 다음과 같이 동작한다.

  1. foo 커넥션이 맺어진다.
  2. SET 부터 FROM bar까지가 한꺼번에 커넥션을 통해 날아간다.
  3. MSSQL 서버 입장에서는, 주어진 SET을 실행해서, 이번 커넥션에 또 추가로 들어오는 질의가 있으면 그때 적용해 줘야지, 하고 기억해둔다.
  4. MSSQL 서버 입장에서는, 근데 이번 커넥션의 이번 질의문에 아직 처리 안한 SELECT 구간이 남아 있으므로, 그걸 실행하게 된다.
  5. 자연히 7405 오류가 터진다. (이번 커넥션의 다음 질의에서는 안 터지겠지만.)
  6. foo 커넥션은 보람찬 하루일 끝마치고서 자랑스럽게 닫힌다. (이제 아까의 SET은 헛수고 가 되었다.)
  7. 개발자는 복장이 터진다.

요컨대 SET ANSI_NULLS ON 같은 SET 문들은, 그걸 적용시킬 다른 쿼리들과 같은 커넥션에서 미리 실행되어야 하는 것이다. 그리고 DBeaver, SQL Studio 같은 툴들은 이런 걸 알아서 잘 처리해주기 때문에 사용자는 이런 원리를 미처 깨닫지도 못한다. 그리고 라라벨은 그런걸 허용하지 않고, 받은 질의문을 통째로 1번의 커넥션 1번의 질의문으로 태워서 단 한 번 실행하고 종료시키므로, 당혹스러운 오류와 마주하게 되는 것이다.

반면 코드#1 은 순수 PHP PDO에 의해 다음과 같이 동작한다.

  1. foo 커넥션이 맺어진다.
  2. 해당 커넥션에 대해 SET ANSI_NULLS ON; SET ANSI_WARNINGS ON; 구문이 질의되어 잘 처리된다.
  3. 해당 커넥션에 대해 SELECT * FROM bar 구문이 질의되어 잘 처리된다.
  4. 십년 묵은 체증이 내린다.

핵심은 뭔가를 SET해야 할 경우, 그걸 SET한 바로 그 커넥션에 원하는 쿼리를 걸어야 한다는 것이다. 라라벨은 이런걸 지원을 안한다. 그러니 그냥 DB 질의의 원점으로 돌아가는 수밖에.

교훈

  • 하다 하다 안 되면 원점으로 돌아가서 생각하는 것도 방법이다.
  • MSSQL은 대략 좇치 안타.
profile
신입 PHP 개발자입니다.

0개의 댓글