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.