Найкращий спосіб тестування 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;

Тепер те, що ми маємо перевірити, притаманне нашій специфікації, а саме те, що для будь-якого кортежу (рік, місяць) існує один і лише один (абсолютний_місяць), і що (абсолютний_місяць) є послідовними. Давайте напишемо кілька тестів.

Наш тест представлятиме собою SQL- selectзапит із такою структурою: тестове ім'я та випадок справи, які проходять катетоване разом. Назва тесту - це лише довільна рядок. Опис справи є лише 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

Поки є достатньо даних тесту в value_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, я задоволений
цілим

1
приголомшливі хакі на sql
CodeFarmer

13
Це чудово, але навіщо використовувати назви однієї літери для стовпців та ледь розбірливих імен перегляду? Чому SQL має бути менш самодокументованим або читабельним, ніж Python?
snl

1
Дивовижне пояснення чогось корисного, на яке я ніколи не дивився у світі SQL / DB. Також я люблю те, як ви перевірили базу даних і тут.
Джекстін

Так само, як попередження, я бачив, що представлення sql, які приєднуються до представлень sql, на PostgreSQL дуже погано працює. Однак я ефективно використовував цю методику з M $ SQL.
Бен Ліянадж

6

Створіть базу даних тестової системи, яку ви можете перезавантажувати так часто, як тільки захочете. Завантажте свої дані або створіть свої дані та збережіть їх. Створіть простий спосіб її перезавантажити. Приєднайте систему розробки до цієї бази даних та підтвердіть свій код, перш ніж перейти до виробництва. Зривайте себе щоразу, коли вам вдається випустити проблему у виробництво. Створіть набір тестів, щоб перевірити відомі проблеми та з часом вирощувати тестовий набір.


4

Ви можете перевірити DbUnit , тому ви можете спробувати написати одиничні тести для своїх програм із фіксованим набором даних. Таким чином, ви зможете писати запити з більш-менш передбачуваними результатами.

Інша річ, яку ви можете зробити, це профайл вашого стека виконання SQL Server і з'ясувати, чи всі запити справді правильні, наприклад, якщо ви використовуєте лише один запит, який повертає і правильні, і неправильні результати, і тоді чітко запит буде Використовується питання, але що робити, якщо ваша програма надсилає різні запити в різні точки коду?

Будь-яка спроба виправити ваш запит тоді була б марною ... все-таки мовчазні запити все одно можуть призвести до неправильних результатів.


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

Що ще гірше, якщо я правильно пам’ятаю, що SQL Server принаймні дозволяє вам менше 10 рівнів перегляду, перш ніж оптимізатор викине свої віртуальні руки в повітря і почне робити повне сканування таблиці за кожним запитом, тому не варто займатись цим підходом.

Не забудьте перевірити чорт із ваших тестових кейсів!

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.