이렇게 실행해야만 결과가 나오는 쿼리라면
USE foo;
SET ANSI_NULLS ON; SET ANSI_WARNINGS ON;
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개 이상의 데이터베이스를 무식하게 DB link 걸어 JOIN 붙여 쓰고있는 우리 회사.. ㅎㅎ 덕분에 강제로 이것저것 배우는데 그중의 하나는 SQL 7405 오류이다. 자세히는 나도 모르니까 대충 이 정도만 알고 지나가자:
ANSI_NULLS
옵션이ON
인 경우,WHERE column_name = NULL
을 사용하는SELECT
문은column_name
에null
값이 있어도0
행을 반환합니다.WHERE column_name <> NULL
을 사용하는SELECT
문은column_name
에Null
이외의 값이 있어도0
행을 반환합니다.
(중략) 분산 쿼리를 실행할 때는ANSI_NULLS
를ON
으로 설정해야 합니다.
출처
(
ANSI_WARNINGS
를)ON
으로 설정한 경우SUM
,AVG
,MAX
,MIN
,STDEV
,STDEVP
,VAR
,VARP
,COUNT
등의 집계 함수에NULL
값이 있으면 경고 메시지가 생성됩니다.OFF
로 설정한 경우에는 경고가 발생하지 않습니다.
(중략) 중요! 분산 쿼리를 실행할 때는ANSI_WARNINGS
를ON
으로 설정해야 합니다.
출처
암튼.. 지금 나는 내 DBeaver에서 잘만 돌아가는 쿼리가 PHP 소스에서 돌아가질 않아서 돌아버리든지 돌아가실 지경이고..
// 이 코드블록을 코드#2 라고 부르겠음
// 실행않됌
$result = DB::connection('foo')->select(
"SET ANSI_NULLS ON; SET ANSI_WARNINGS ON;
SELECT * FROM bar"
);
// 아래 코드블록을 코드#3이라고 부르겠음
// 역시 실행 안됨
$db = DB::connection('foo');
$db->query('SET ANSI_NULLS ON; SET ANSI_WARNINGS ON;');
$result = $db->select("SELECT * FROM bar");
어떡하나.. 하다가 설마?? 싶어서 DB 조회의 원점으로 돌아가 PDO를 꺼내서 직접 쿼리를 때리는 방식을 시도해 보았다.
어... 된다.
상기 코드#2 는 라라벨에 의해 대충 다음과 같이 동작한다.
foo
커넥션이 맺어진다.SET
부터 FROM bar
까지가 한꺼번에 커넥션을 통해 날아간다.SET
을 실행해서, 이번 커넥션에 또 추가로 들어오는 질의가 있으면 그때 적용해 줘야지, 하고 기억해둔다.SELECT
구간이 남아 있으므로, 그걸 실행하게 된다.7405
오류가 터진다. (이번 커넥션의 다음 질의에서는 안 터지겠지만.)foo
커넥션은 보람찬 하루일 끝마치고서 자랑스럽게 닫힌다. (이제 아까의 SET
은 헛수고 가 되었다.)상기 코드#3 은 어느 훌륭한 개발자분의 질문 댓글 덕분에 테스트해본 것인데 커넥션이 2번 맺어진다는 점만 빼면 상술한 코드#2 동작과 거의 비슷하다. 제1행에서 생성된 $db
는 foo
커넥션을 열어놓고 있는 것이 아니다. 단지, 이후 query()
나 select()
를 실행해야 할 때 foo
커넥션을 1회 사용한다는 '방침'을 갖고 있을 뿐인 것이다. 따라서 제2행과 제3행은 각각의 커넥션으로 실행되고, 혹시나 했지만 역시나 제3행에서 라라벨은 어김없이 QueryException: General error 7405
를 반환한다.
요컨대 SET ANSI_NULLS ON
같은 SET
문들은, 그걸 적용시킬 다른 쿼리들과 같은 커넥션에서 미리 실행되어야 하는데, DBeaver, SQL Studio 같은 툴들은 이런 걸 알아서 잘 처리해주기 때문에 사용자는 미처 그걸 눈치채지도 못하고, Illuminate 쿼리 빌더는 그런 걸 허용하지 않는다는 것이다. 커넥션 설정값과 쿼리를 받아서 통째로 1번의 커넥션 1번의 질의로 단 한 번 실행하고 종료시키기 때문에, 코드#2와 코드#3의 노력은 숲으로 돌아간다.
반면 코드#1 은 순수 PHP PDO에 의해 다음과 같이 동작한다.
foo
커넥션이 맺어진다.SET ANSI_NULLS ON; SET ANSI_WARNINGS ON;
구문이 질의되어 잘 처리된다.SELECT * FROM bar
구문이 질의되어 잘 처리된다.
재밋게 잘 봤습니다~
혹시 SET 구문이 SELECT 문에 독립적으로(?) 선행되지 않는게 문제면 PDO까지 안가고 쿼리 빌더로 각각 실행하는 방식으로는 해결이 안될까요? (직접 안해보고 질문 남기는 게으름 사과드립니다 :) )