Por que o SQL não é mais refatorável? [fechadas]

39

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:

  1. Isso já é comum e estou trabalhando com pessoas inexperientes

  2. Programadores experientes não escrevem SQL complexo porque preferem resolver problemas de processamento de dados com código processual

  3. Algo mais

ebrts
fonte
12
Existem organizações que permitem consultar apenas um banco de dados através de visualizações e modificá-lo através de procedimentos armazenados.
Pieter B
3
O SQL ficou muito mais agradável para mim quando finalmente aceitei que nunca seria tão SECO quanto o meu código processual normal.
Graham
1
4. O SQL é realmente antigo e não é atualizado materialmente há décadas. Para coisas super complexas, muitas equipes optam por procedimentos armazenados. Você pode adicionar cláusulas diferentes para isso. Às vezes, você só precisa executar tarefas para preparar dados em uma tabela temporária e depois participar dela. Veja como são diferentes as linguagens declarativas e processuais.
Berin Loritsch
8
Também uma razão é que há um problema de desempenho horrível chamado "junção triangular" que pode ocorrer quando você usa visualizações (por acidente, é claro). Se sua consulta ingressar na Visualização A e na Visualização B, mas a Visualização A também em sua implementação reutilizar a Visualização B, você começará a ver esse problema. Assim, as pessoas geralmente começam escrevendo uma única consulta monolítica para poder ver o que realmente funcionaria melhor em termos de refatoração de visualizações e, em seguida, o prazo final atingido, e o monólito entra em produção. Mais ou menos como 98% de todos os desenvolvedores de software, realmente :) :)
Stephen Byrne
3
"Imagine se outros tipos de programadores precisassem enviar uma solicitação cada vez que criassem uma função" ... umm. Você não faz revisões de código?
svidgen

Respostas:

25

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:

with custs as (
    select acct# as accountNumber, cfname as firstName, clname as lastName,
    from wrdCsts
    where -- various criteria
)
, accounts as (
    select acct# as accountNumber, crBal as currentBalance
    from crzyAcctTbl
)
select firstName, lastName, currentBalance
from custs
inner join accounts on custs.accountNumber = accounts.accountNumber

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:

  • DB / 2
  • PostGreSQL
  • Oráculo
  • MS SQL Server
  • MySQL (versão mais recente; ainda meio nova)
  • provavelmente outros

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.

Meower68
fonte
4
Tínhamos o Sr. Big CTE como o homem que escrevia o pior SQL. O problema era que as CTEs eram poucas opções de abstração e o otimizador não pode desfazer todos os algoritmos que você usa.
Joshua
3
Além disso, o ORM também pode fazer algumas coisas hediondas em termos de desempenho ... especialmente quando você está apenas usando getters e setters para buscar um monte de dados. O Hibernate é notório por usar centenas de consultas individuais em vez de uma consulta unida grande, o que é um problema quando há sobrecarga em cada consulta.
user3067860
2
@ Josué Você pode escrever um código incorreto em qualquer idioma. Incluindo SQL. Mas a refatoração para CTEs, feita corretamente, pode criar projetos de baixo para cima, mais fáceis de serem analisados ​​pelos humanos.
Costumo
2
As outras respostas são ótimas, mas é isso que eu estava procurando pessoalmente. 'Por que eu não sei sobre CTEs' foi a maior parte do meu problema.
ebrts
2
@ Meower68 Não existe o risco de o uso extensivo do CTE impedir que as pessoas aprendam se junte adequadamente e aprendam sobre um bom design de banco de dados? Apoio o valor das CTEs, mas também facilita o trabalho com subconsultas, onde você não deveria.
Pieter B
36

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.

Greg Burghardt
fonte
"O SQL lida apenas com estruturas de dados concretas, não com comportamentos abstratos (ou uma abstração em qualquer sentido da palavra)." Essa é uma afirmação estranha, pois, do meu ponto de vista, o SQL lida inteiramente com o comportamento abstrato e não com a programação concreta em nenhum sentido da palavra! Apenas considere todos os enormes graus de complexidade abstraídos da simples palavra "JOIN": você diz que deseja um resultado mesclado extraído de dois conjuntos de dados diferentes e deixa ao DBMS determinar as técnicas concretas envolvidas, lidar com indexação, lidar com a diferença entre tabelas e subconsultas, etc ...
Mason Wheeler
5
@MasonWheeler: Acho que estava pensando mais no SQL do ponto de vista dos dados em que trabalha, não na implementação dos recursos da linguagem. As tabelas em um banco de dados não parecem uma abstração. Eles são concretos, pois em uma tabela chamada "phone_numbers" contém números de telefone. Um número de telefone não é um conceito abstrato.
Greg Burghardt
12

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.

Toni Kostelac
fonte
6

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?

  • SQL, de várias maneiras, não é uma linguagem de programação.
  • Design de banco de dados incorreto.
  • Pessoas que não são realmente fluentes em SQL.
  • Não há poder sobre o banco de dados (por exemplo, não é permitido o uso de visualizações)
  • Objetivos diferentes com refatoração.

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

Pieter B
fonte
6
É provável que as "subconsultas" sejam uma agregação de um banco de dados devidamente normalizado, assim como a normalização ad-hoc de um banco de dados não normalizado
Caleth
@ Caleth, isso é verdade.
Pieter B
5
Mesmo em bancos de dados bem normalizados, muitas vezes ainda é necessário associar-se a subconsultas, em vez de associar-se diretamente a tabelas. Por exemplo, se você precisar se juntar a dados agrupados.
Barmar
1
@ Barmar definitivamente, daí o meu 9 de 10 comentário. As subconsultas têm seu lugar, mas eu as vejo sendo usadas em excesso por pessoas inexperientes.
Pieter B
Gosto da sua métrica de "número de subconsultas" como uma indicação da normalização do banco de dados (ou a falta dela).
Jason
2

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.

Christophe
fonte
+1 Alguns ótimos pontos aqui. Dado o quão ruim é um SQL, a reticência dos DBAs para permitir visualizações geralmente é totalmente compreensível. Além disso, o SQL pode se beneficiar definitivamente da revisão por pares, se estiver com fome de recursos e / ou for executado com freqüência.
Robbie Dee
1

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.

Robbie Dee
fonte
1
Eu nunca ouvi falar de uma construção "mart". O que é isso?
bispo
1
Marts são apenas um subconjunto do repositório (banco de dados mestre). Se houver consultas complexas específicas que precisam ser executadas, um banco de dados especial poderá ser criado especificamente para atender a essas solicitações. Um exemplo muito comum é um mercado de relatórios.
Robbie Dee
1
Confuso por que isso foi rebaixado. Não responde diretamente à pergunta, mas fornece uma resposta implícita bastante clara da "opção 3: existem muitas maneiras de lidar com isso, que são amplamente usadas".
Dewi Morgan
TIL sobre data marts. Tenha um +1!
bispo