SQL 쿼리를 테스트하는 가장 좋은 방법 [닫힌]


109

복잡한 SQL 쿼리가 계속 오류와 함께 나가는 문제가 발생했습니다. 기본적으로 이로 인해 잘못된 고객에게 메일이 전송되고 이와 같은 기타 '문제'가 발생합니다.

이와 같은 SQL 쿼리를 만드는 모든 사람의 경험은 무엇입니까? 우리는 격주로 새로운 데이터 집단을 만들고 있습니다.

그래서 여기에 내 생각과 그에 대한 한계가 있습니다.

  • 테스트 데이터 생성 이것은 우리가 모든 올바른 데이터를 가지고 있음을 증명하지만 프로덕션에서 이상 징후를 배제하지 않습니다. 그것은 오늘날 잘못된 것으로 간주되지만 10 년 전에는 정확했을 수있는 데이터입니다. 문서화되지 않았으므로 데이터가 추출 된 후에 만 ​​알 수 있습니다.

  • 벤 다이어그램 및 데이터 맵 생성 이것은 쿼리의 디자인을 테스트하는 확실한 방법 인 것 같지만 구현이 정확하다는 것을 보장하지는 않습니다. 개발자가 미리 계획하고 글을 쓸 때 무슨 일이 일어나고 있는지 생각하게합니다.

내 문제에 대해 의견을 보내 주셔서 감사합니다.

답변:


164

200 줄 길이의 함수로 애플리케이션을 작성하지 않을 것입니다. 이러한 긴 함수를 각각 명확하게 정의 된 단일 책임을 가진 더 작은 함수로 분해합니다.

왜 그렇게 SQL을 작성합니까?

함수를 분해하는 것처럼 쿼리 를 분해합니다. 이를 통해 더 짧고, 더 간단하고, 이해 하기 쉽고 , 테스트 하기 쉽고, 리팩토링하기가 더 쉽습니다. 그리고 절차 적 코드에서와 같이 그들 사이에 "심"과 "래퍼"를 추가 할 수 있습니다.

어떻게하나요? 쿼리가 수행하는 각 중요한 작업을 뷰로 만듭니다. 그럼 당신은 구성 이 더 원시적 인 기능에서 더 많은 복잡한 기능을 구성하는 것처럼,이 간단한 뷰에서 더 많은 복잡한 쿼리를.

그리고 가장 좋은 점은 대부분 의 뷰 구성에서 RDBMS에서 정확히 동일한 성능을 얻을 수 있다는 것입니다. (일부는 그렇게하지 않을 것입니다. 그래서 어떻습니까? 조기 최적화는 모든 악의 근원입니다. 먼저 올바르게 코딩 한 다음 필요한 경우 최적화하십시오.)

다음은 복잡한 쿼리를 분해하기 위해 여러보기를 사용하는 예입니다.

이 예에서는 각보기가 하나의 변환 만 추가하므로 각보기를 독립적으로 테스트하여 오류를 찾을 수 있으며 테스트는 간단합니다.

예제의 기본 테이블은 다음과 같습니다.

create table month_value( 
    eid int not null, month int, year int,  value int );

이 테이블은 절대 월인 하나의 기준을 나타 내기 위해 월과 연도의 두 열을 사용하기 때문에 결함이 있습니다. 새로운 계산 열에 대한 사양은 다음과 같습니다.

선형 변환으로 수행하여 (연도, 월)과 동일하게 정렬하고 임의의 (연도, 월) 튜플에 대해 하나의 값만 있고 모든 값이 연속되도록합니다.

create view cm_absolute_month as 
select *, year * 12 + month as absolute_month from month_value;

이제 테스트해야하는 것은 사양에 내재되어 있습니다. 즉, 모든 튜플 (연도, 월)에 대해 하나 (absolute_month) 만 있고 그 (absolute_month)는 연속적이라는 것입니다. 몇 가지 테스트를 작성해 봅시다.

테스트는 select테스트 이름과 케이스 문이 함께 연결된 구조 의 SQL 쿼리입니다. 테스트 이름은 임의의 문자열입니다. 케이스 진술은 단지 case when테스트 진술 then 'passed' else 'failed' end입니다.

테스트 문은 테스트를 통과하기 위해 참이어야하는 SQL 선택 (서브 쿼리) 일뿐입니다.

다음은 첫 번째 테스트입니다.

--a select statement that catenates the test name and the case statement
select concat( 
-- the test name
'For every (year, month) there is one and only one (absolute_month): ', 
-- the case statement
   case when 
-- one or more subqueries
-- in this case, an expected value and an actual value 
-- that must be equal for the test to pass
  ( select count(distinct year, month) from month_value) 
  --expected value,
  = ( select count(distinct absolute_month) from cm_absolute_month)  
  -- actual value
  -- the then and else branches of the case statement
  then 'passed' else 'failed' end
  -- close the concat function and terminate the query 
  ); 
  -- test result.

해당 쿼리를 실행하면 다음 결과가 생성됩니다. For every (year, month) there is one and only one (absolute_month): passed

month_value에 충분한 테스트 데이터가있는 한이 테스트가 작동합니다.

충분한 테스트 데이터에 대한 테스트도 추가 할 수 있습니다.

select concat( 'Sufficient and sufficiently varied month_value test data: ',
   case when 
      ( select count(distinct year, month) from month_value) > 10
  and ( select count(distinct year) from month_value) > 3
  and ... more tests 
  then 'passed' else 'failed' end );

이제 연속 테스트 해 봅시다.

select concat( '(absolute_month)s are consecutive: ',
case when ( select count(*) from cm_absolute_month a join cm_absolute_month b 
on (     (a.month + 1 = b.month and a.year = b.year) 
      or (a.month = 12 and b.month = 1 and a.year + 1 = b.year) )  
where a.absolute_month + 1 <> b.absolute_month ) = 0 
then 'passed' else 'failed' end );

이제 쿼리에 불과한 테스트를 파일에 넣고 데이터베이스에 대해 해당 스크립트를 실행 해 보겠습니다. 실제로, 데이터베이스에 대해 실행할 스크립트 (또는 스크립트, 관련 뷰당 하나의 파일 권장)에 뷰 정의를 저장하면 동일한 스크립트 에 각 뷰에 대한 테스트를 추가 할 수 있습니다. -) 뷰를 생성하면 뷰의 테스트도 실행됩니다. 이렇게하면 뷰를 다시 만들 때 회귀 테스트를받을 수 있고 뷰 생성이 프로덕션에 대해 실행될 때 뷰도 프로덕션에서 테스트됩니다.


27
이것은, 내가 :) 하루 행복 해요 난 깨끗한 코드와 단위 SQL에서 시험 볼이 처음이다
막심 ARNSTAMM

1
멋진 SQL 해킹
CodeFarmer

13
이것은 훌륭하지만 열에 대해 하나의 문자 이름을 사용하고 거의 읽을 수없는 뷰 이름을 사용하는 이유는 무엇입니까? SQL이 Python보다 자기 문서화 또는 가독성이 떨어지는 이유는 무엇입니까?
snl

1
SQL / DB 세계에서 본 적이없는 유용한 것에 대한 멋진 설명입니다. 또한 여기서 데이터베이스를 테스트 한 방식도 마음에 듭니다.
Jackstine

경고처럼 SQL 뷰에 조인하는 SQL 뷰가 PostgreSQL에서 매우 저조한 성능을 보이는 것을 보았습니다. 그러나 M $ SQL에서이 기술을 효과적으로 사용했습니다.
Ben Liyanage

6

원하는만큼 자주 다시로드 할 수있는 테스트 시스템 데이터베이스를 만듭니다. 데이터를로드하거나 데이터를 생성하고 저장하십시오. 쉽게 재 장전 할 수있는 방법을 만드십시오. 개발 시스템을 해당 데이터베이스에 연결하고 프로덕션으로 이동하기 전에 코드를 검증하십시오. 문제가 생산에 들어가도록 관리 할 때마다 자신을 걷어차십시오. 테스트 모음을 만들어 알려진 문제를 확인하고 시간이 지남에 따라 테스트 모음을 확장합니다.


4

DbUnit 을 확인하고 싶을 수 있으므로 고정 된 데이터 세트로 프로그램에 대한 단위 테스트를 작성해 볼 수 있습니다. 그러면 어느 정도 예측 가능한 결과로 쿼리를 작성할 수 있습니다.

다른 작업은 SQL Server 실행 스택을 프로파일 링하고 모든 쿼리가 실제로 올바른지 확인하는 것입니다. 예를 들어 올바른 결과와 잘못된 결과를 모두 반환하는 하나의 쿼리 만 사용하는 경우 쿼리가 used가 문제이지만 응용 프로그램이 코드의 다른 지점에서 다른 쿼리를 보내는 경우에는 어떨까요?

쿼리를 수정하려는 시도는 소용이 없을 것입니다. 어쨌든 불량 쿼리는 여전히 잘못된 결과를 발생시키는 것일 수 있습니다.


2

Re : tpdi

case when ( select count(*) from cm_abs_month a join cm_abs_month b  
on (( a.m + 1 = b.m and a.y = b.y) or (a.m = 12 and b.m = 1 and a.y + 1 = b.y) )   
where a.am + 1 <> b.am ) = 0  

이것은 연속적인 데이터가 존재하는지 (아마도 처음에 의도했던 것과 같음)가 아니라 연속 된 달에 대한 am 값이 연속적인지 확인합니다. 소스 데이터가 연속적이지 않은 경우 (예 : 짝수 달만 있음) am 계산이 완전히 꺼져 있어도 항상 통과됩니다.

또한 내가 뭔가를 놓치고 있습니까, 아니면 해당 ON 절의 후반부가 잘못된 월 값과 충돌합니까? (즉, 2011 년 12 월이 1/2010 이후인지 확인)

더 나쁜 것은 내가 올바르게 기억한다면 SQL Server는 최적화 프로그램이 가상 손을 공중에 던지고 모든 요청에 ​​대해 전체 테이블 스캔을 시작하기 전에 최소한 10 수준의 뷰를 허용하므로이 접근 방식을 과도하게 사용하지 마십시오.

테스트 케이스에서 도대체 테스트하는 것을 잊지 마십시오!

그렇지 않으면 SqlUnit 또는 DbUnit 또는 기타 * Unit을 사용하여 해당 데이터에 대한 예상 결과를 자동으로 확인 하고 필요에 따라 검토, 유지 관리 및 업데이트하는 등 가능한 모든 형식의 입력을 포함하는 매우 광범위한 데이터 집합을 만드는 것이 일반적으로 보입니다. 갈 길.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.