Se possível, não faça isso.
Essa é a resposta - é um antipadrão. Se o cliente conhece a mesa da qual deseja os dados, então SELECT FROM ThatTable
. Se um banco de dados for projetado de forma que isso seja necessário, ele parece ter sido projetado de forma subotimizada. Se uma camada de acesso a dados precisa saber se existe um valor em uma tabela, é fácil compor o SQL nesse código e colocar esse código no banco de dados não é bom.
Para mim, isso parece instalar um dispositivo dentro de um elevador onde se pode digitar o número do andar desejado. Depois que o botão Go é pressionado, ele move uma mão mecânica sobre o botão correto para o andar desejado e o pressiona. Isso apresenta muitos problemas potenciais.
Atenção: não há intenção de zombaria, aqui. Meu exemplo bobo de elevador foi * o melhor dispositivo que eu poderia imaginar * para apontar problemas de forma sucinta com essa técnica. Ele adiciona uma camada inútil de indireção, movendo a escolha do nome da tabela de um espaço de chamada (usando um DSL robusto e bem compreendido, SQL) para um híbrido usando código SQL obscuro / bizarro do lado do servidor.
Essa divisão de responsabilidade por meio do movimento da lógica de construção da consulta em SQL dinâmico torna o código mais difícil de entender. Ele viola uma convenção padrão e confiável (como uma consulta SQL escolhe o que selecionar) em nome de um código personalizado repleto de potencial de erro.
Aqui estão pontos detalhados sobre alguns dos problemas potenciais com esta abordagem:
O SQL dinâmico oferece a possibilidade de injeção de SQL que é difícil de reconhecer no código do front-end ou apenas no código do back-end (é necessário inspecioná-los juntos para ver isso).
Os procedimentos e funções armazenados podem acessar recursos aos quais o proprietário da função / SP tem direitos, mas o chamador não. Pelo que entendi, sem cuidados especiais, então por padrão, quando você usa código que produz SQL dinâmico e o executa, o banco de dados executa o SQL dinâmico sob os direitos do autor da chamada. Isso significa que você não poderá usar objetos privilegiados ou terá que abri-los para todos os clientes, aumentando a área de superfície de ataque potencial para dados privilegiados. Definir a função SP / no momento da criação para sempre ser executado como um usuário específico (no SQL Server EXECUTE AS
) pode resolver esse problema, mas torna as coisas mais complicadas. Isso exacerba o risco de injeção de SQL mencionado no ponto anterior, tornando o SQL dinâmico um vetor de ataque muito atraente.
Quando um desenvolvedor precisa entender o que o código do aplicativo está fazendo para modificá-lo ou consertar um bug, ele achará muito difícil obter a consulta SQL exata que está sendo executada. O SQL Profiler pode ser usado, mas exige privilégios especiais e pode ter efeitos negativos no desempenho dos sistemas de produção. A consulta executada pode ser registrada pelo SP, mas isso aumenta a complexidade para benefícios questionáveis (exigindo acomodação de novas tabelas, eliminação de dados antigos, etc.) e não é óbvio. Na verdade, alguns aplicativos são arquitetados de forma que o desenvolvedor não tenha credenciais de banco de dados, portanto, torna-se quase impossível para ele realmente ver a consulta sendo enviada.
Quando ocorre um erro, como ao tentar selecionar uma tabela que não existe, você receberá uma mensagem semelhante a "nome de objeto inválido" do banco de dados. Isso vai acontecer exatamente da mesma forma, quer você esteja compondo o SQL no back-end ou no banco de dados, mas a diferença é que algum desenvolvedor pobre que está tentando solucionar o sistema precisa inserir um nível mais profundo em outra caverna abaixo daquela onde o o problema existe, para mergulhar no procedimento maravilhoso que faz tudo para tentar descobrir qual é o problema. Os logs não mostrarão "Error in GetWidget", mas sim "Error in OneProcedureToRuleThemAllRunner". Essa abstração geralmente tornará o sistema pior .
Um exemplo em pseudo-C # de troca de nomes de tabelas com base em um parâmetro:
string sql = $"SELECT * FROM {EscapeSqlIdentifier(tableName)};"
results = connection.Execute(sql);
Embora isso não elimine todos os problemas imagináveis, as falhas que descrevi com a outra técnica estão ausentes neste exemplo.