Najlepszy sposób na testowanie zapytań SQL [zamknięty]

Więc natknąłem się na problem w tym, że ciągle mamy złożone zapytania SQL wyjść z błędami.

Zasadniczo powoduje to wysyłanie poczty do niewłaściwych klientów i inne "problemy" tego typu.

Jakie jest doświadczenie każdego z tworzenia takich zapytań SQL, zasadniczo tworzymy nowe kohorty danych co drugi tydzień.

Oto więc niektóre z moich myśli i ich ograniczenia.

Tworzenie danych testowych-o ile to udowodni, że mamy wszystkie poprawne dane nie wymuszają wykluczenia anomalii w produkcji. Są to dane, które dziś byłyby uważane za błędne, ale mogły być poprawne 10 lat temu, nie zostały udokumentowane i dlatego wiemy o tym dopiero po wydobyciu danych.

Tworzenie diagramów Venna i map danych-wydaje się to być solidnym sposobem na przetestowanie projektu zapytania, jednak nie gwarantuje poprawności implementacji. to sprawia, że deweloperzy przesuwają się do przodu i myślą o tym, co jest dzieje się tak, jak piszą.

Dzięki za wkład w mój problem.

Author: Bluephlame, 2009-04-16

4 answers

Nie napisałbyś aplikacji z funkcjami o długości 200 linii. Rozkładałbyś te długie funkcje na mniejsze funkcje, każda z jedną jasno określoną odpowiedzialnością.

Po co pisać swój SQL w ten sposób?

Rozkładaj swoje zapytania, tak jak rozkładasz swoje funkcje. To sprawia, że są krótsze, prostsze, łatwiejsze do zrozumienia, łatwiejsze do przetestowania, łatwiejsze do refaktoryzacji. I pozwala dodać "shims" między nimi i" wrappery " wokół nich, tylko jak w Kodeksie proceduralnym.

Jak ty to robisz? Poprzez umieszczenie każdej znaczącej rzeczy w widoku. Następnie tworzysz bardziej złożone zapytania z tych prostszych Widoków, Tak jak komponujesz bardziej złożone funkcje z bardziej prymitywnych funkcji.

I najlepsze jest to, że dla większości kompozycji widoków, otrzymasz dokładnie ten sam występ z Twoich RDBMS. (Dla niektórych nie będzie; i co z tego? Przedwczesna optymalizacja jest źródłem wszelkiego zła. Kod poprawnie najpierw następnie zoptymalizuj, jeśli zajdzie taka potrzeba.)

Oto przykład użycia kilku widoków do rozłożenia skomplikowanego zapytania.

W przykładzie, ponieważ każdy widok dodaje tylko jedną transformację, każdy może być niezależnie testowany w celu znalezienia błędów, a testy są proste.

Oto tabela bazowa w przykładzie:

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

Ta tabela jest błędna, ponieważ używa dwóch kolumn, miesiąca i roku, aby reprezentować jeden punkt odniesienia, miesiąc bezwzględny. Oto nasza specyfikacja dla nowej, obliczonej kolumny:

Zrobimy to jako przekształcenie liniowe, takie, że sortuje się tak samo jak (y, m), I takie, że dla dowolnej (y,m) krotki istnieje jedna i jedyna wartość, a wszystkie wartości są następujące po sobie: {10]}

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

Teraz to, co musimy przetestować, jest nieodłączną częścią naszej specyfikacji, a mianowicie, że dla dowolnej krotki (y, m) istnieje jedna i tylko jedna (am), a że (am)S są kolejne. Napiszmy kilka testów.

Naszym testem będzie zapytanie SQL select, z następująca struktura: nazwa testu i Oświadczenie o przypadku połączone razem. Nazwa testu jest tylko dowolnym ciągiem znaków. Instrukcja case jest po prostu case when instrukcją testową then 'passed' else 'false' end.

Instrukcje testowe będą tylko selektami SQL (zapytaniami podrzędnymi), które muszą być prawdziwe, aby test przeszedł pomyślnie.

Oto nasz pierwszy test:

--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.

Uruchomienie tego zapytania daje wynik: For every (y,m) there is one and only one (am): passed

Dopóki w month_value jest wystarczająca ilość danych testowych, test ten działa.

Możemy dodać test dla wystarczających danych testowych:

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 );

Teraz przetestujmy to kolejne:

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 );

Teraz włóżmy nasze testy, które są tylko zapytaniami, do pliku i uruchom ten skrypt przeciwko bazie danych. W rzeczy samej, jeśli przechowujemy nasze definicje widoku w skrypcie (lub skryptach, polecam jeden plik na powiązane widoki), który ma być uruchomiony z bazą danych, możemy dodać nasze testy dla każdego widoku do skryptu tego samego, tak aby akt (ponownego) tworzenia naszego widoku również uruchamiał testy widoku. Że sposób, oboje otrzymujemy testy regresji, gdy ponownie tworzymy widoki, a gdy Tworzenie widoku działa przeciwko produkcji, widok będzie również testowany w produkcji.

 121
Author: tpdi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:23

Utwórz bazę danych systemu testowego, którą możesz przeładowywać tak często, jak chcesz. Załaduj dane lub utwórz Dane i zapisz je. Wytworzyć łatwy sposób przeładowania. Dołącz swój system programistyczny do tej bazy danych i zweryfikuj kod przed rozpoczęciem produkcji. Kick się za każdym razem, gdy uda Ci się pozwolić problem dostać się do produkcji. Utwórz zestaw testów, aby zweryfikować Znane problemy i z czasem powiększać swój zestaw testów.

 5
Author: ojblass,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-04-16 03:03:07

Możesz sprawdzić DbUnit , więc możesz spróbować napisać testy jednostkowe dla swoich programów z ustalonym zestawem danych. W ten sposób powinieneś być w stanie pisać zapytania z mniej lub bardziej przewidywalnymi wynikami.

Inną rzeczą, którą możesz chcieć zrobić, jest profilowanie stosu wykonawczego SQL Server i sprawdzenie, czy wszystkie zapytania są rzeczywiście poprawne, np. jeśli używasz tylko jednego zapytania, które zwraca zarówno prawidłowe, jak i nieprawidłowe wyniki, to wyraźnie używane zapytanie jest w pytanie, ale co jeśli Twoja aplikacja wysyła różne zapytania w różnych punktach kodu?

Każda próba poprawienia zapytania wtedy byłaby daremna... nieuczciwe zapytania mogą nadal być tymi, które odpalają złe wyniki i tak.

 4
Author: Jon Limjap,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-20 07:29:37

[1] 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  

Zauważ, że to tylko sprawdza, czy wartości am dla kolejnych miesięcy będą kolejne, a nie, że kolejne dane istnieją (co prawdopodobnie jest tym, co zamierzałeś początkowo). To zawsze przejdzie, jeśli żadne z Twoich danych źródłowych nie jest kolejne (np. masz tylko parzyste miesiące), nawet jeśli twoje obliczenia am są całkowicie wyłączone.

Też coś przeoczyłem, czy druga połowa tego na klauzuli podskoczyła o złą wartość miesiąca? (tj. sprawdza, że 12/2011 nadchodzi po 1/2010)

Co gorsza, o ile dobrze pamiętam, SQL Server pozwala przynajmniej na mniej niż 10 poziomów widoków, zanim optymalizator wyrzuci swoje wirtualne ręce w powietrze i zacznie wykonywać pełne skanowanie tabeli na każde żądanie, więc nie przesadzaj-zrób to podejście.

Pamiętaj, aby przetestować do cholery swoich przypadków testowych!

W przeciwnym razie tworzenie bardzo szerokiego zestawu danych, które obejmowałyby większość lub wszystkie możliwe formy danych wejściowych, przy użyciu SqlUnit lub DbUnit lub dowolnej innej *jednostki do automatyzacji sprawdzania dla oczekiwanych wyników w stosunku do tych danych, i przegląd, utrzymanie i aktualizacja go w razie potrzeby ogólnie wydaje się być drogą do zrobienia.

 2
Author: Mars the Infomage,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-01 13:30:30