Лучший способ протестировать SQL-запросы [закрыто]

Итак, я столкнулся с проблемой, заключающейся в том, что у нас сложные SQL-запросы выходят с ошибками.

по сути это приводит к отправке почты неверным клиентам и другим подобным «проблемам».

Каков каждый опыт создания таких запросов SQL, по сути, мы создаем новые группы данных каждую неделю.

Итак, вот некоторые мои мысли и ограничения к ним.

Создание тестовых данных - хотя это доказывает, что у нас есть все правильные данные, это не приводит к исключению аномалий в производстве. Это данные, которые сегодня считались бы неправильными, но, возможно, были правильными 10 лет назад, они не были задокументированы, и поэтому мы узнаем об этом только после извлечения данных.

Создание диаграмм Венна и карт данных - кажется, это хороший способ проверить структуру запроса, однако это не гарантирует правильности реализации. это заставляет разработчиков двигаться вперед и думать о том, что происходит, когда они пишут.

Спасибо за любой вклад, который вы можете дать моей проблеме.

89 голосов | спросил Bluephlame 16 AMpThu, 16 Apr 2009 06:28:46 +040028Thursday 2009, 06:28:46

4 ответа


0

Вы бы не написали приложение с функциями длиной 200 строк. Вы бы разложили эти длинные функции на более мелкие функции, каждая с одной четко определенной ответственностью.

Зачем писать свой SQL таким образом?

Разложите ваши запросы, точно так же, как вы разлагаете свои функции. Это делает их короче, проще, легче для понимания, легче для тестирования , легче для рефакторинга. И это позволяет вам добавлять между ними «прокладки» и «обертки», как это делается в процедурном коде.

Как ты это делаешь? Делая каждую значимую вещь, запрос делает в представление. Затем вы составляете более сложные запросы из этих более простых представлений, так же, как вы создаете более сложные функции из более примитивных функций.

И самое замечательное то, что для большинства композиций представлений вы получите точно такую ​​же производительность от своей RDBMS. (Для некоторых вы этого не сделаете; ну и что? Преждевременная оптимизация - корень всего зла. Сначала кодируйте правильно, затем оптимизируйте, если вам нужно.)

Вот пример использования нескольких представлений разложить сложный запрос.

В этом примере, поскольку каждое представление добавляет только одно преобразование, каждое из них может быть независимо протестировано на наличие ошибок, и тесты просты.

Вот базовая таблица в примере:

create table month_value( 
    eid int not null, m int, y int,  v int );

Эта таблица некорректна, поскольку в ней используются два столбца, месяц и год, для представления одного элемента данных, абсолютного месяца. Вот наша спецификация для нового вычисляемого столбца:

  

Мы сделаем это как линейное преобразование, чтобы оно сортировалось так же, как (y, m), и чтобы в любом кортеже (y, m) было одно-единственное значение, а все значения были бы последовательными:

create view cm_abs_month as 
select *, y * 12 + m as am from month_value;

Теперь то, что мы должны проверить, присуще нашей спецификации, а именно, что для любого кортежа (y, m) существует один и только один (am), и что (am) являются последовательными. Давайте напишем несколько тестов.

Наш тест будет SQL-запросом select со следующей структурой: имя теста и оператор case, объединенные вместе. Имя теста - это просто произвольная строка. Оператор case - это просто case when тестовый оператор then 'passed' else 'false' end.

Операторы теста будут просто SQL-выборками (подзапросами), которые должны быть истинными для прохождения теста.

Вот наш первый тест:

--a select statement that catenates the test name and the case statement
select concat( 
-- the test name
'For every (y,m) there is one and only one (am): ', 
-- 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 y, m) from month_value) 
  --expected value,
  = ( select count(distinct am) from cm_abs_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 (y,m) there is one and only one (am): passed

Если в month_value достаточно тестовых данных, этот тест работает.

Мы также можем добавить тест для достаточного количества тестовых данных:

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

Теперь давайте проверим это последовательно:

select concat( '(am)s are consecutive: ',
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 
then 'passed' else 'failed' end );

Теперь давайте поместим наши тесты, которые являются просто запросами, в файл и запустим этот сценарий для базы данных. Действительно, если мы сохраняем наши определения представлений в сценарии (или сценариях, я рекомендую один файл для связанных представлений) для запуска с базой данных, мы можем добавить наши тесты для каждого представления в сценарий же , так что процесс (повторного) создания нашего представления также запускает тесты представления. Таким образом, мы оба получаем регрессионные тесты, когда мы воссоздаем представления, и, когда создание представления выполняется в рабочей среде, представление также будет проверяться в рабочей среде.

ответил tpdi 16 AMpThu, 16 Apr 2009 06:53:13 +040053Thursday 2009, 06:53:13
0

Создайте базу данных тестовой системы, которую вы можете загружать так часто, как вам хочется. Загрузите свои данные или создайте свои данные и сохраните их. Создайте простой способ перезагрузить его. Подключите свою систему разработки к этой базе данных и проверьте свой код, прежде чем приступить к работе. Пинайте себя каждый раз, когда вам удается разрешить проблему войти в производство. Создайте набор тестов для проверки известных проблем и со временем расширяйте набор тестов.

ответил ojblass 16 AMpThu, 16 Apr 2009 07:03:07 +040003Thursday 2009, 07:03:07
0

Возможно, вы захотите проверить DbUnit , поэтому вы можете попробовать написать модульные тесты для своих программ с фиксированной набор данных. Таким образом, вы сможете писать запросы с более или менее предсказуемыми результатами.

Другая вещь, которую вы можете захотеть сделать, - профилировать ваш стек выполнения SQL Server и выяснить, все ли запросы действительно являются правильными, например, если вы используете только один запрос, который возвращает как правильные, так и неправильные результаты, тогда используемый запрос находится под вопросом, но как быть, если ваше приложение отправляет разные запросы в разных точках кода?

Любая попытка исправить ваш запрос была бы бесполезной ... тем не менее, мошеннические запросы все равно могут приводить к неверным результатам.

ответил Jon Limjap 16 AMpThu, 16 Apr 2009 06:38:20 +040038Thursday 2009, 06:38:20
0

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 не соответствует значению месяца? (т.е. проверяет, что 12/2011 наступает после 1/2010)

Что еще хуже, если я правильно помню, SQL Server, по крайней мере, дает вам менее 10 уровней просмотра, прежде чем оптимизатор бросит свои виртуальные руки в воздух и начнет выполнять полное сканирование таблицы при каждом запросе, поэтому не переусердствуйте этот подход.

Не забудьте протестировать свои тесты!

В противном случае создание очень широкого набора данных, охватывающего большинство или все возможные формы ввода, с использованием SqlUnit или DbUnit или любого другого * модуля для автоматизации проверки ожидаемых результатов по этим данным и просмотра, обслуживания и обновления этих данных. по мере необходимости обычно кажется, что путь.

ответил Mars the Infomage 1 J000000Thursday10 2010, 17:30:30

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132