Todo mundo sabe que novos desenvolvedores escrevem funções longas. À medida que avança, você melhora a divisão do código em pedaços menores e a experiência ensina o valor de fazê-lo.
Digite SQL. Sim, a maneira SQL de pensar sobre código é diferente da maneira processual de pensar sobre código, mas esse princípio parece igualmente aplicável.
Digamos que eu tenho uma consulta que assume o formulário:
select * from subQuery1 inner join subQuerry2 left join subquerry3 left join join subQuery4
Usando alguns IDs ou datas etc.
Essas subconsultas são complexas e podem conter subconsultas próprias. Em nenhum outro contexto de programação, eu pensaria que a lógica para subconsultas complexas 1-4 pertence à linha da minha consulta pai que une todas elas. Parece tão simples que essas subconsultas devem ser definidas como visualizações, assim como seriam funções se eu estivesse escrevendo código processual.
Então, por que essa prática comum não é? Por que as pessoas costumam escrever essas consultas SQL monolíticas longas? Por que o SQL não incentiva o uso extensivo de visualizações, assim como a programação procedural, incentiva o uso extensivo de funções. (Em muitos ambientes empresariais, a criação de visualizações não é algo fácil de executar. São necessárias solicitações e aprovações. Imagine se outros tipos de programadores precisassem enviar uma solicitação sempre que criassem uma função!)
Eu pensei em três respostas possíveis:
Isso já é comum e estou trabalhando com pessoas inexperientes
Programadores experientes não escrevem SQL complexo porque preferem resolver problemas de processamento de dados com código processual
Algo mais
Respostas:
Eu acho que o principal problema é que nem todos os bancos de dados suportam expressões de tabela comuns.
Meu empregador usa o DB / 2 para muitas coisas. As últimas versões suportam CTEs, de modo que eu sou capaz de fazer coisas como:
O resultado é que podemos ter nomes de tabelas / campos muito abreviados e estou criando essencialmente exibições temporárias, com nomes mais legíveis, que posso usar. Claro, a consulta fica mais longa. Mas o resultado é que eu posso escrever algo que é claramente separado (usando CTEs da maneira que você usaria funções para ficar SECO) e acabar com um código bastante legível. E como sou capaz de quebrar minhas subconsultas e ter uma subconsulta fazendo referência a outra, nem tudo é "inline". Ocasionalmente, escrevi um CTE, depois tive outros quatro CTEs fazendo referência a ele, depois tive a união de consulta principal os resultados desses quatro últimos.
Isso pode ser feito com:
Mas isso ajuda bastante a tornar o código mais limpo, mais legível e mais SECO.
Eu desenvolvi uma "biblioteca padrão" de CTEs que posso conectar a várias consultas, iniciando rapidamente minha nova consulta. Alguns deles também estão começando a ser adotados por outros desenvolvedores da minha organização.
Com o tempo, pode fazer sentido transformar alguns deles em visualizações, para que esta "biblioteca padrão" esteja disponível sem a necessidade de copiar / colar. Mas meus CTEs acabam sendo aprimorados, ainda que ligeiramente, para várias necessidades que eu não consegui fazer com que um único CTE fosse usado TÃO AMARELO, sem mods, que valeria a pena criar uma visualização.
Parece que parte de sua queixa é "por que eu não sei sobre CTEs?" ou "por que meu banco de dados não suporta CTEs?"
Quanto às atualizações ... sim, você pode usar CTEs, mas, na minha experiência, você deve usá-las dentro da cláusula set E na cláusula where. Seria bom se você pudesse definir um ou mais à frente de toda a instrução de atualização e, em seguida, apenas ter as partes "consulta principal" nas cláusulas set / where, mas não funciona dessa maneira. E não há como evitar nomes obscuros de tabela / campo na tabela que você está atualizando.
Você pode usar CTEs para exclusões. Pode levar vários CTEs para determinar os valores de PK / FK para os registros que você deseja excluir dessa tabela. Novamente, você não pode evitar nomes obscuros de tabela / campo na tabela que está modificando.
Na medida em que você pode fazer uma seleção em uma inserção, você pode usar CTEs para inserções. Como sempre, você pode estar lidando com nomes de tabela / campo obscuros na tabela que está modificando.
O SQL NÃO permite criar o equivalente a um objeto de domínio, agrupando uma tabela com getters / setters. Para isso, você precisará usar algum tipo de ORM, junto com uma linguagem de programação / OO mais procedural. Eu escrevi coisas dessa natureza em Java / Hibernate.
fonte
O bloqueio da criação de visualizações do banco de dados geralmente é feito por organizações paranóicas de problemas de desempenho no banco de dados. Este é um problema de cultura organizacional, e não um problema técnico com o SQL.
Além disso, grandes consultas SQL monolíticas são gravadas muitas vezes, porque o caso de uso é tão específico que muito pouco do código SQL pode ser realmente reutilizado em outras consultas. Se uma consulta complexa é necessária, geralmente é para um caso de uso muito diferente. Copiar o SQL de outra consulta geralmente é um ponto de partida, mas devido a outras subconsultas e JOINs na nova consulta, você acaba modificando o SQL copiado apenas o suficiente para interromper qualquer tipo de abstração que uma "função" em outro idioma faria. ser usado para. O que me leva à razão mais importante pela qual é difícil refatorar o SQL.
O SQL lida apenas com estruturas de dados concretas, não com um comportamento abstrato (ou uma abstração em qualquer sentido da palavra). Como o SQL é escrito em torno de idéias concretas, não há nada a abstrair em um módulo reutilizável. As visualizações do banco de dados podem ajudar com isso, mas não no mesmo nível de uma "função" em outro idioma. Uma exibição de banco de dados não é tanto uma abstração, mas uma consulta. Bem, na verdade, uma exibição de banco de dados é uma consulta. É essencialmente usado como uma tabela, mas executado como uma subconsulta, então, novamente, você está lidando com algo concreto, não abstrato.
É com abstrações que o código se torna mais fácil de refatorar, porque uma abstração oculta detalhes de implementação do consumidor dessa abstração. O SQL direto não fornece essa separação, embora extensões procedurais ao SQL, como PL / SQL para Oracle ou Transact-SQL para SQL Server, comecem a embaçar um pouco as linhas.
fonte
O que acho que pode estar faltando na sua pergunta / ponto de vista é que o SQL executa operações em conjuntos (usando operações de conjunto etc.).
Quando você opera nesse nível, naturalmente desiste de um certo controle sobre o motor. Você ainda pode forçar algum código de estilo de procedimento usando cursores, mas como a experiência mostra 99/100 vezes, não deveria fazê-lo.
A refatoração do SQL é possível, mas não está usando os mesmos princípios de refatoração de código como estamos acostumados no código no nível do aplicativo. Em vez disso, você otimiza como usa o próprio mecanismo SQL.
Isso pode ser feito de várias maneiras. Se você estiver usando o Microsoft SQL Server, poderá usar o SSMS para fornecer um plano de execução aproximado e para ver quais etapas você pode executar para ajustar seu código.
No caso de dividir o código em módulos menores, como o @ greg-burghardt mencionou, o SQL geralmente é um trecho de código criado para esse fim e, como resultado. Faz aquela coisa que você precisa fazer e nada mais. Ele está aderindo ao S no SOLID, tem apenas um motivo para ser alterado / afetado e é quando você precisa dessa consulta para fazer outra coisa. O restante do acrônimo (OLID) não se aplica aqui (AFAIK não há injeção de dependência, interfaces ou dependências como tais no SQL), dependendo do tipo de SQL que você está usando, você pode estender determinadas consultas agrupando-as em uma função de procedimento / tabela armazenada ou usando-as como subconsultas, diria que o princípio de aberto-aberto ainda se aplicaria, de certa forma. Mas eu discordo.
Eu acho que você precisa mudar seu paradigma em termos de como você está visualizando o código SQL. Devido à natureza definida, ele não pode fornecer muitos dos recursos que as linguagens no nível do aplicativo podem (genéricos etc.). O SQL nunca foi projetado para ser algo assim, é uma linguagem para consultar conjuntos de dados e cada conjunto é único à sua maneira.
Dito isto, existem maneiras pelas quais você pode tornar seu código mais bonito, se a legibilidade for uma alta prioridade na organização. Armazenando bits de blocos SQL usados com freqüência (conjuntos de dados comuns que você usa) em procedimentos armazenados / funções de valor de tabela e, em seguida, consultando-os e armazenando-os em tabelas / variáveis de tabela temporárias, seguido pelo uso desses para unir as peças em uma transação maciça que você escreveria de outra maneira é uma opção. IMHO não vale a pena fazer algo assim com o SQL.
Como uma linguagem, ela foi projetada para ser facilmente legível e compreensível por qualquer pessoa, mesmo não programadores. Como tal, a menos que você esteja fazendo algo muito inteligente, não há necessidade de refatorar o código SQL em partes menores do tamanho de bytes. Pessoalmente, escrevi consultas SQL massivas enquanto trabalhava em uma solução ETL / Reporting de data warehouse e tudo ainda estava muito claro em termos do que estava acontecendo. Qualquer coisa que parecesse um pouco estranha para outras pessoas receberia um breve conjunto de comentários ao lado para fornecer uma breve explicação.
Eu espero que isso ajude.
fonte
Vou focar nas "subconsultas" no seu exemplo.
Por que eles são usados com tanta frequência? Porque eles usam a maneira natural de pensar em uma pessoa: eu tenho esse conjunto de dados e quero executar uma ação em um subconjunto dele e associá-lo a um subconjunto de outros dados. 9 em 10 vezes que vejo uma subconsulta, ela é usada incorretamente. Minha piada sobre subconsultas é: as pessoas que têm medo de junções usam subconsultas.
Se você ver essas subconsultas, também é um sinal de design de banco de dados não ideal.
Quanto mais Normalizado for o seu Banco de Dados, mais associações você obtém, mais o seu banco de dados se parece com uma grande planilha de excel, mais subselecionações você obtém.
A refatoração no SQL geralmente tem um objetivo diferente: obter mais desempenho, melhores tempos de consulta, "evitando verificações de tabela". Eles podem até tornar o código menos legível, mas são muito valiosos.
Então, por que você vê tantas consultas monolíticas enormes não refatoradas?
(para mim, quanto mais experiente eu tenho com SQL, menos grandes são minhas consultas, o SQL tem maneiras para pessoas de todos os níveis de habilidade realizarem seus trabalhos, não importa o que aconteça.)
fonte
Segregação de deveres
No espírito do SQL, o banco de dados é um ativo compartilhado que contém os dados da empresa e a proteção é de vital importância. Entra no DBA como guardião do templo.
A criação de uma nova visualização no banco de dados deve servir a um propósito duradouro e ser compartilhada por uma comunidade de usuários. Na visualização DBA, isso é aceitável apenas se a visualização for justificada pela estrutura dos dados. Toda mudança de uma visão é então associada a riscos para todos os usuários atuais, mesmo aqueles que não usam o aplicativo, mas que descobriram a visão. Por fim, a criação de novos objetos requer gerenciar autorizações e, no caso da exibição, consistentemente com as autorizações das tabelas subjacentes.
Tudo isso explica por que os DBAs não gostam de adicionar visualizações que são apenas para o código de algum aplicativo individual.
Design SQL
Se você decompor uma de suas consultas complexas e agradáveis, poderá descobrir que as subconsultas geralmente precisam de um parâmetro que depende de outra subconsulta.
Portanto, transformar subconsultas em vista não é necessariamente tão simples quanto declarado. Você deve isolar os parâmetros variáveis e projetar sua visualização para que os parâmetros possam ser adicionados como critérios de seleção na visualização.
Infelizmente, ao fazer isso, às vezes você impõe o acesso a mais dados e com menos eficácia do que em uma consulta personalizada.
Extensões proprietárias
Você poderia esperar alguma refatoração, transferindo algumas responsabilidades para extensões procedurais do SQL, como PL / SQL ou T-SQL. No entanto, eles dependem do fornecedor e criam uma dependência tecnológica. Além disso, essas extensões são executadas no servidor de banco de dados, criando mais carga de processamento em um recurso que é muito mais difícil de dimensionar do que um servidor de aplicativos.
Mas qual é o problema no final?
Por fim, a segregação de funções e o design do SQL, com suas vantagens e limitações, é um problema real? No final, esses bancos de dados provaram lidar com sucesso e com confiabilidade dados muito críticos, inclusive em ambientes de missão crítica.
Portanto, para obter uma refatoração bem-sucedida:
considere uma melhor comunicação . Tente entender as restrições do seu DBA. Se você provar a um DBA que uma nova visão é justificada pelas estruturas de dados, que não é uma solução alternativa descartável e que não tem impacto na segurança, ele certamente concordará em permitir que seja criado. Porque, então, seria um interesse compartilhado.
limpe sua própria casa primeiro : nada obriga a gerar muito SQL em muitos lugares. Refatorar o código do aplicativo, para isolar os acessos SQL e criar as classes ou funções para fornecer subconsultas reutilizáveis, se elas forem usadas com freqüência.
melhore a conscientização da equipe : verifique se o aplicativo não está executando tarefas que poderiam ser executadas com mais eficiência pelo mecanismo DBMS. Como você corretamente apontou, a abordagem processual e a abordagem orientada a dados não são igualmente dominadas por diferentes membros da equipe. Depende do histórico deles. Mas, para otimizar o sistema como um todo, sua equipe precisa entendê-lo como um todo. Portanto, crie consciência, para ter certeza de que jogadores menos experientes não reinventem a roda e compartilhem seus pensamentos de DB com membros mais experientes.
fonte
Re pontos 1 e 3: as visualizações não são a única maneira. Também existem tabelas temporárias, marcadores, variáveis de tabela, colunas agregadas, CTEs, funções, procedimentos armazenados e possivelmente outras construções, dependendo do RDBMS.
Os DBAs (e eu estou falando como alguém que foi DBA e desenvolvedor) tendem a ver o mundo de uma maneira bastante binária, por isso são frequentemente contra coisas como visualizações e funções devido à penalidade de desempenho percebida.
Ultimamente, a necessidade de junções complexas diminuiu com o reconhecimento de que as tabelas desnormalizadas, apesar de serem sub-ideais do ponto de vista da NF , têm alto desempenho.
Há também a tendência de fazer consultas no lado do cliente com tecnologias como LINQ, que você levanta no ponto 2.
Embora eu concorde que o SQL possa ser um desafio para modularizar, grandes avanços foram feitos, embora sempre exista uma dicotomia entre o código do lado do cliente e o SQL - embora o 4GL tenha obscurecido um pouco as linhas.
Eu acho que realmente depende de quão longe seus DBAs / arquitetos / líderes de tecnologia estão dispostos a ceder nesse sentido. Se eles se recusarem a permitir qualquer coisa, exceto o baunilha do SQL com muitas junções, poderão ocorrer consultas enormes. Se você está preso a isso, não bata com a cabeça em uma parede de tijolos, aumente-a. Geralmente, existem maneiras melhores de fazer as coisas com um pouco de compromisso - especialmente se você puder provar os benefícios.
fonte