Il modo migliore per testare le query SQL [chiuso]


109

Ho riscontrato un problema in cui continuiamo ad avere query SQL complesse con errori. Essenzialmente questo si traduce nell'invio di posta ai clienti errati e altri "problemi" come quello.

Qual è l'esperienza di tutti con la creazione di query SQL in questo modo? Stiamo creando nuove coorti di dati ogni due settimane.

Quindi ecco alcuni dei miei pensieri e i loro limiti:

  • Creazione di dati di test Sebbene ciò provi che disponiamo di tutti i dati corretti, non impone l'esclusione di anomalie nella produzione. Si tratta di dati che oggi sarebbero considerati errati ma che potrebbero essere stati corretti 10 anni fa; non è stato documentato e quindi lo sappiamo solo dopo che i dati sono stati estratti.

  • Creare diagrammi di Venn e mappe di dati Questo sembra essere un modo solido per testare il design di una query, tuttavia non garantisce che l'implementazione sia corretta. Permette agli sviluppatori di pianificare in anticipo e pensare a cosa sta succedendo mentre scrivono.

Grazie per qualsiasi contributo che puoi dare al mio problema.

Risposte:


164

Non scriveresti un'applicazione con funzioni lunghe 200 righe. Scomponereste quelle funzioni lunghe in funzioni più piccole, ciascuna con una singola responsabilità chiaramente definita.

Perché scrivere il tuo SQL in questo modo?

Decomponi le tue query, proprio come decomponi le tue funzioni. Questo li rende più brevi, più semplici, più facili da comprendere, più facili da testare , più facili da refactoring. E ti permette di aggiungere "shim" tra di loro e "wrapper" attorno ad essi, proprio come fai nel codice procedurale.

Come fai a fare questo? Rendendo ogni cosa significativa una query in una vista. Quindi componi query più complesse da queste viste più semplici, proprio come componi funzioni più complesse da funzioni più primitive.

E la cosa grandiosa è che, per la maggior parte delle composizioni di visualizzazioni, otterrai esattamente le stesse prestazioni dal tuo RDBMS. (Per alcuni non lo farai; e allora? L'ottimizzazione prematura è la radice di tutti i mali. Codifica prima correttamente, quindi ottimizza se necessario.)

Ecco un esempio di utilizzo di più viste per scomporre una query complicata.

Nell'esempio, poiché ogni vista aggiunge solo una trasformazione, ciascuna può essere testata in modo indipendente per trovare errori e i test sono semplici.

Ecco la tabella di base nell'esempio:

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

Questa tabella è difettosa, perché utilizza due colonne, mese e anno, per rappresentare un dato, un mese assoluto. Ecco le nostre specifiche per la nuova colonna calcolata:

Lo faremo come una trasformazione lineare, in modo tale da ordinare lo stesso di (anno, mese) e in modo tale che per ogni tupla (anno, mese) ci sia un solo valore e tutti i valori siano consecutivi:

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

Ora quello che dobbiamo testare è inerente alla nostra specifica, vale a dire che per ogni tupla (anno, mese), ce n'è uno e solo uno (absolute_month) e che (absolute_month) sono consecutivi. Scriviamo alcuni test.

Il nostro test sarà una selectquery SQL , con la seguente struttura: un nome di test e un'istruzione case catenati insieme. Il nome del test è solo una stringa arbitraria. L'istruzione case è solo una case whendichiarazione di prova then 'passed' else 'failed' end.

Le istruzioni di test saranno solo selezioni SQL (sottoquery) che devono essere vere affinché il test venga superato.

Ecco il nostro primo 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.

L'esecuzione di tale query produce questo risultato: For every (year, month) there is one and only one (absolute_month): passed

Finché sono disponibili dati di test sufficienti in month_value, questo test funziona.

Possiamo anche aggiungere un test per dati di test sufficienti:

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

Ora proviamo che è consecutivo:

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

Ora mettiamo i nostri test, che sono solo query, in un file ed eseguiamo lo script sul database. Infatti, se memorizziamo le nostre definizioni di vista in uno script (o script, consiglio un file per viste correlate) da eseguire sul database, possiamo aggiungere i nostri test per ciascuna vista allo stesso script, in modo che l'atto di (ri -) la creazione della nostra vista esegue anche i test della vista. In questo modo, entrambi riceviamo test di regressione quando ricreamo le viste e, quando la creazione della vista viene eseguita rispetto alla produzione, la vista verrà testata anche in produzione.


27
Questa è la prima volta che vedo codice pulito e unit test in sql, sono felice per la giornata :)
Maxime ARNSTAMM

1
fantastici hack sql
CodeFarmer

13
Questo è fantastico, ma perché usare i nomi di una lettera per le colonne e i nomi delle viste appena leggibili? Perché SQL dovrebbe essere meno auto-documentante o leggibile di Python?
snl

1
Fantastica spiegazione per qualcosa di utile che non ho mai visto nel mondo SQL / DB. Inoltre, adoro il modo in cui hai testato il database anche qui.
Jackstine

Proprio come un avvertimento, ho visto che le viste sql che si uniscono alle viste sql hanno prestazioni molto scarse su PostgreSQL. Tuttavia, ho usato questa tecnica in modo efficace con M $ SQL.
Ben Liyanage

6

Crea un database di sistema di test che puoi ricaricare tutte le volte che desideri. Carica i tuoi dati o crea i tuoi dati e salvalo. Produci un modo semplice per ricaricarlo. Collega il tuo sistema di sviluppo a quel database e convalida il codice prima di passare alla produzione. Datti dei calci ogni volta che riesci a far entrare in produzione un problema. Crea una suite di test per verificare i problemi noti e ampliare la tua suite di test nel tempo.


4

Potresti voler controllare DbUnit , quindi potresti provare a scrivere unit test per i tuoi programmi con un set fisso di dati. In questo modo dovresti essere in grado di scrivere query con risultati più o meno prevedibili.

L'altra cosa che potresti voler fare è profilare lo stack di esecuzione di SQL Server e scoprire se tutte le query sono effettivamente quelle corrette, ad esempio, se stai usando solo una query che restituisce risultati sia corretti che errati, allora chiaramente la query è usato è in questione, ma cosa succede se la tua applicazione invia query diverse in punti diversi del codice?

Qualsiasi tentativo di correggere la tua query sarebbe inutile ... le query canaglia potrebbero comunque essere quelle che producono comunque risultati sbagliati.


2

Ri: 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  

Nota che questo controlla solo che i valori am per mesi consecutivi siano consecutivi, non che esistano dati consecutivi (che è probabilmente quello che avevi inteso inizialmente). Questo passerà sempre se nessuno dei tuoi dati di origine è consecutivo (ad esempio, hai solo mesi pari), anche se il tuo calcolo è completamente disattivato.

Inoltre mi manca qualcosa o la seconda metà di quella clausola ON aumenta il valore del mese sbagliato? (cioè controlla che il 12/2011 sia successivo al 1/2010)

Quel che è peggio, se ricordo bene, SQL Server ti consente almeno meno di 10 livelli di visualizzazione prima che l'ottimizzatore lanci le sue mani virtuali in aria e inizi a eseguire scansioni complete della tabella su ogni richiesta, quindi non esagerare con questo approccio.

Ricordati di mettere alla prova i tuoi casi di test!

In caso contrario, la creazione di un insieme molto ampio di dati per comprendere la maggior parte o tutte le possibili forme di input, utilizzando SqlUnit o DbUnit o qualsiasi altra * Unità per automatizzare il controllo dei risultati attesi rispetto a tali dati, e riesaminarli, mantenerli e aggiornarli come necessario generalmente sembra essere la strada da percorrere.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.