web-dev-qa-db-de.com

Beste Möglichkeit zum Testen von SQL-Abfragen

Ich bin auf ein Problem gestoßen, bei dem immer wieder komplexe SQL-Abfragen mit Fehlern ausgehen. Dies führt im Wesentlichen dazu, dass E-Mails an die falschen Kunden gesendet werden, und zu ähnlichen Problemen.

Welche Erfahrungen haben alle mit der Erstellung solcher SQL-Abfragen gemacht? Wir erstellen jede zweite Woche neue Datenkohorten.

Hier sind einige meiner Gedanken und deren Einschränkungen:

  • Erstellen von Testdaten Dies würde zwar beweisen, dass wir alle korrekten Daten haben, erzwingt jedoch nicht den Ausschluss von Anomalien in der Produktion. Das sind Daten, die heute als falsch angesehen werden, aber vor 10 Jahren möglicherweise korrekt waren. es wurde nicht dokumentiert und daher wissen wir erst, nachdem die Daten extrahiert wurden.

  • Erstellen von Venn-Diagrammen und Datenkarten Dies scheint eine solide Methode zu sein, um das Design einer Abfrage zu testen, garantiert jedoch nicht, dass die Implementierung korrekt ist. Es bringt die Entwickler dazu, vorauszuplanen und darüber nachzudenken, was gerade beim Schreiben passiert.

Vielen Dank für jede Eingabe, die Sie zu meinem Problem geben können.

101
Bluephlame

Sie würden keine Anwendung mit Funktionen schreiben, die 200 Zeilen lang sind. Sie würden diese langen Funktionen in kleinere Funktionen zerlegen, von denen jede eine klar definierte Verantwortung hat.

Warum schreiben Sie Ihre SQL so?

Zerlegen Sie Ihre Abfragen genau wie Sie Ihre Funktionen zerlegen. Dies macht sie kürzer, einfacher, verständlicher, einfacher zu testen und einfacher zu refaktorisieren. Und es ermöglicht Ihnen, "Shims" zwischen ihnen und "Wrapper" um sie herum hinzuzufügen, genau wie Sie es im prozeduralen Code tun.

Wie machst Du das? Indem Sie jede wichtige Sache zu einer Abfrage in einer Ansicht machen. Dann erstellen Sie komplexere Abfragen aus diesen einfacheren Ansichten , genauso wie Sie komplexere Funktionen aus primitiveren Funktionen erstellen.

Und das Tolle ist, dass Sie für die meisten Kompositionen von Views genau dieselbe Leistung aus Ihrem RDBMS herausholen können. (Für manche ist das nicht der Fall. Was also? Vorzeitige Optimierung ist die Wurzel allen Übels. Code zuerst richtig, und dann bei Bedarf optimieren.)

Hier ist ein Beispiel für die Verwendung mehrerer Ansichten zum Zerlegen einer komplizierten Abfrage.

Da in diesem Beispiel in jeder Ansicht nur eine Transformation hinzugefügt wird, kann jede unabhängig getestet werden, um Fehler zu finden, und die Tests sind einfach.

Hier ist die Basistabelle im Beispiel:

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

Diese Tabelle ist fehlerhaft, weil sie zwei Spalten (Monat und Jahr) verwendet, um ein Datum, einen absoluten Monat, darzustellen. Hier ist unsere Spezifikation für die neue berechnete Spalte:

Wir machen das als lineare Transformation, so dass sie die gleiche Sortierung wie (Jahr, Monat) hat und dass es für jedes Tuple (Jahr, Monat) einen einzigen Wert gibt und alle Werte aufeinanderfolgend sind:

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

Was wir nun testen müssen, ist unserer Spezifikation inhärent, nämlich dass es für jedes Tupel (Jahr, Monat) nur ein einziges (absolutes_Monat) gibt und dass (absolutes_Monat) s aufeinanderfolgend sind. Lassen Sie uns einige Tests schreiben.

Unser Test wird eine SQL select -Anfrage mit der folgenden Struktur sein: ein Testname und eine zusammen verkettete case-Anweisung. Der Testname ist nur eine beliebige Zeichenfolge. Die case-Anweisung ist nur case when Testanweisungen then 'passed' else 'failed' end.

Die Testanweisungen sind nur SQL-Auswahlen (Unterabfragen), die wahr sein müssen, damit der Test bestanden wird.

Hier ist unser erster Test:

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

Das Ausführen dieser Abfrage führt zu folgendem Ergebnis: For every (year, month) there is one and only one (absolute_month): passed

Solange in month_value genügend Testdaten vorhanden sind, funktioniert dieser Test.

Wir können auch einen Test für ausreichende Testdaten hinzufügen:

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

Jetzt testen wir es nacheinander:

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

Lassen Sie uns nun unsere Tests, bei denen es sich nur um Abfragen handelt, in eine Datei packen und das Skript für die Datenbank ausführen. Wenn wir unsere Ansichtsdefinitionen in einem Skript (oder Skripten, ich empfehle eine Datei pro zugehöriger Ansicht) speichern, die für die Datenbank ausgeführt werden soll, können wir unsere Tests für jede Ansicht zur gleichen hinzufügen ) Skript, so dass der Vorgang des (Neu-) Erzeugens unserer Ansicht auch die Tests der Ansicht ausführt. Auf diese Weise erhalten wir beide Regressionstests, wenn wir Ansichten neu erstellen, und wenn die Ansichtserstellung mit der Produktion in Konflikt steht, wird die Ansicht auch in der Produktion getestet.

145
tpdi

Erstellen Sie eine Testsystemdatenbank, die Sie beliebig oft neu laden können. Laden Sie Ihre Daten oder erstellen Sie Ihre Daten und speichern Sie sie ab. Produzieren Sie einen einfachen Weg, um es neu zu laden. Hängen Sie Ihr Entwicklungssystem an diese Datenbank an und validieren Sie Ihren Code, bevor Sie zur Produktion gehen. Kick dich jedes Mal, wenn du es schaffst, ein Problem in die Produktion zu bringen. Erstellen Sie eine Testsuite, um bekannte Probleme zu überprüfen und Ihre Testsuite im Laufe der Zeit zu erweitern.

6
ojblass

Möglicherweise möchten Sie DbUnit aktivieren, damit Sie versuchen können, Komponententests für Ihre Programme mit einem festen Datensatz zu schreiben. Auf diese Weise sollten Sie in der Lage sein, Abfragen mit mehr oder weniger vorhersehbaren Ergebnissen zu schreiben.

Das andere, was Sie möglicherweise tun möchten, ist, Ihren SQL Server-Ausführungsstapel zu profilieren und herauszufinden, ob alle Abfragen tatsächlich die richtigen sind, z verwendet wird in Frage, aber was ist, wenn Ihre Anwendung verschiedene Abfragen an verschiedenen Stellen im Code sendet?

Jeder Versuch, Ihre Anfrage zu korrigieren, ist dann erfolglos. Die falschen Anfragen sind möglicherweise immer noch diejenigen, die ohnehin die falschen Ergebnisse auslösen.

4
Jon Limjap

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  

Beachten Sie, dass hiermit nur überprüft wird, ob die Werte für aufeinanderfolgende Monate aufeinanderfolgend sind, und nicht, ob aufeinanderfolgende Daten vorhanden sind (was wahrscheinlich ursprünglich beabsichtigt war). Dies ist immer dann der Fall, wenn keine Ihrer Quelldaten aufeinanderfolgend sind (z. B. Sie haben nur geradzahlige Monate), auch wenn Ihre Berechnung vollständig fehlerhaft ist.

Vermisse ich auch etwas oder stößt die zweite Hälfte dieser ON-Klausel auf den falschen Monatswert? (d. h. prüft, ob 12/2011 nach 1/2010 liegt)

Was noch schlimmer ist, wenn ich mich recht erinnere, lässt SQL Server mindestens weniger als 10 Ansichten zu, bevor das Optimierungsprogramm seine virtuellen Hände in die Luft wirft und bei jeder Anforderung vollständige Tabellenscans durchführt.

Denken Sie daran, Ihre Testfälle zu testen!

Andernfalls erstellen Sie einen sehr umfangreichen Datensatz, um die meisten oder alle möglichen Arten von Eingaben zu erfassen. Verwenden Sie dazu SqlUnit oder DbUnit oder eine andere * Unit, um die Überprüfung auf erwartete Ergebnisse anhand dieser Daten zu automatisieren und zu überprüfen und zu verwalten und es nach Bedarf zu aktualisieren scheint im Allgemeinen der richtige Weg zu sein.

2