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 select
query 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 when
dichiarazione 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.