Você não escreveria um aplicativo com funções de 200 linhas. Você decomporia essas funções longas em funções menores, cada uma com uma única responsabilidade claramente definida.
Por que escrever seu SQL assim?
Decomponha suas consultas, assim como você decompõe suas funções. Isso os torna mais curtos, mais simples, mais fáceis de compreender, testar , refatorar. E permite que você adicione "shims" entre eles e "wrappers" ao redor deles, assim como você faz no código procedural.
Como você faz isso? Ao transformar cada coisa significativa que uma consulta faz em uma visualização. Então você compõe consultas mais complexas a partir dessas visualizações mais simples, da mesma forma que compõe funções mais complexas a partir de funções mais primitivas.
E o melhor de tudo é que, para a maioria das composições de visualizações, você obterá exatamente o mesmo desempenho de seu RDBMS. (Para alguns, você não vai; e daí? A otimização prematura é a raiz de todos os males. Codifique corretamente primeiro e, em seguida , otimize se necessário.)
Aqui está um exemplo do uso de várias visualizações para decompor uma consulta complicada.
No exemplo, como cada exibição adiciona apenas uma transformação, cada uma pode ser testada de forma independente para encontrar erros e os testes são simples.
Aqui está a tabela base no exemplo:
create table month_value(
eid int not null, month int, year int, value int );
Esta tabela é falha, porque usa duas colunas, mês e ano, para representar um dado, um mês absoluto. Aqui está nossa especificação para a nova coluna calculada:
Faremos isso como uma transformação linear, de forma que ela classifique da mesma forma que (ano, mês) e que, para qualquer tupla (ano, mês), haja um e somente valor, e todos os valores são consecutivos:
create view cm_absolute_month as
select *, year * 12 + month as absolute_month from month_value;
Agora, o que temos que testar é inerente à nossa especificação, ou seja, que para qualquer tupla (ano, mês), há um e apenas um (mês_determinado), e que (mês_bsoluto) são consecutivos. Vamos escrever alguns testes.
Nosso teste será uma selectconsulta SQL , com a seguinte estrutura: um nome de teste e uma instrução case catenada. O nome do teste é apenas uma string arbitrária. A declaração do caso é apenas uma case whendeclaração de testethen 'passed' else 'failed' end .
As instruções de teste serão apenas seleções SQL (subconsultas) que devem ser verdadeiras para que o teste passe.
Aqui está nosso primeiro teste:
--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.
Executar essa consulta produz este resultado: For every (year, month) there is one and only one (absolute_month): passed
Desde que haja dados de teste suficientes em month_value, este teste funcionará.
Também podemos adicionar um teste para dados de teste suficientes:
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 );
Agora vamos testar consecutivamente:
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 );
Agora vamos colocar nossos testes, que são apenas consultas, em um arquivo e executar esse script no banco de dados. Na verdade, se armazenarmos nossas definições de visualização em um script (ou scripts, eu recomendo um arquivo por visualizações relacionadas) para ser executado no banco de dados, podemos adicionar nossos testes para cada visualização ao mesmo script, de modo que o ato de (re -) criar nossa visão também executa os testes da visão. Dessa forma, ambos obtemos testes de regressão quando recriamos as visualizações e, quando a criação da visualização é executada na produção, a visualização também será testada na produção.