Torne as alterações do banco de dados do SQL Server compatíveis com versões anteriores ao alterar a interface de uma função

Torne as alterações do banco de dados do SQL Server compatíveis com versões anteriores ao alterar a interface de uma função

Torne as alterações do banco de dados do SQL Server compatíveis com versões anteriores ao alterar a interface de uma função 1
cupom com desconto - o melhor site de cupom de desconto cupomcomdesconto.com.br


Por: Aaron Bertrand | Atualizado: 2020-03-18 | Comentários (1) | Relacionados: Mais> T-SQL

Problema

Um colega estava recentemente tentando implantar uma alteração em um usuário do SQL Server
função definida, onde ela
removeu um dos parâmetros de entrada. Infelizmente, essa função também foi chamada
de procedimentos armazenados, no banco de dados de origem e em outros bancos de dados na instância,
bem como do código ad hoc no aplicativo. Isso criou um cenário de galinha e ovo,
como ela não pôde alterar a função primeiro sem interromper o aplicativo,
e ela não pôde alterar o aplicativo primeiro sem interromper a função
chamadas.

Solução

Primeiro, vamos criar uma função fictícia com um parâmetro de entrada que pudermos
eliminar viável (porque não faz sentido):

CREATE FUNCTION dbo.CalculateSalesCommission
(
  @ProductID int, @ShoeSize tinyint
)
RETURNS decimal(5,4)
AS
BEGIN
  RETURN
 (
    SELECT CASE WHEN @ProductID % 7 = 0 THEN 0.04 ELSE 0.03 END
    + @ShoeSize / 100.0
  );
END
GO -- sample usage:
SELECT [41,12] = dbo.CalculateSalesCommission(41,12), -- 0.1500
       [42,12] = dbo.CalculateSalesCommission(42,12), -- 0.1600
       [42,14] = dbo.CalculateSalesCommission(42,14); -- 0.1800

Em seguida, podemos visualizar vários procedimentos armazenados e consultas ad hoc que chamam
a função, por exemplo:

CREATE PROCEDURE dbo.GetCommissions
  @ProductID int
AS
BEGIN
  -- pretend this comes from a table, or context_info, or ...:
  DECLARE @ShoeSize tinyint = 12;   SELECT [Commission] = dbo.CalculateSalesCommission(@ProductID,@ShoeSize);
END
GO

Agora, voltando à mudança que precisamos fazer. Claramente, o sapato de um vendedor
tamanho não deve ter nada a ver com a comissão de vendas, portanto, queremos remover
esse cálculo. Mas se alterarmos a função primeiro, para remover o parâmetro de entrada
e o cálculo:

ALTER FUNCTION dbo.CalculateSalesCommission
(
  @ProductID int--, @ShoeSize tinyint
)
RETURNS decimal(5,4)
AS
BEGIN
  RETURN
 (
    SELECT CASE WHEN @ProductID % 7 = 0 THEN 0.04 ELSE 0.03 END
    --+ @ShoeSize / 100.0
  );
END
GO

Os procedimentos e consultas ad hoc, que ainda fazem referência aos dois parâmetros,
começará a ser interrompido com o seguinte erro:

Msg 8144, Level 16, State 2, Procedure dbo.GetCommissions, Line 8
Procedure or function dbo.CalculateSalesCommission has too many arguments specified.

E se tentarmos mudar o procedimento primeiro, pare de passar o argumento:

Leia Também  Anunciando dois cursos gratuitos de fundamentos ao vivo nesta quarta e quinta-feira

ALTER PROCEDURE dbo.GetCommissions
  @ProductID int
AS
BEGIN
  --DECLARE @ShoeSize tinyint = 12;   SELECT [Commission] = dbo.CalculateSalesCommission(@ProductID);--, @ShoeSize);
END
GO

Bem, isso não funcionará (e a resolução de nomes adiada não ajudará
você aqui):

Msg 313, Level 16, State 2, Procedure GetCommissions, Line 8
An insufficient number of arguments were supplied for the procedure or function dbo.CalculateSalesCommission.

Então, sim, definitivamente temos um cenário de ovos e galinha aqui.

O mais importante é que mudemos a lógica para não nos importarmos mais
tamanho de sapato. Se o argumento ainda é passado para a função é em grande parte irrelevante
para os negócios, se o valor repassado for apenas ignorado. Você poderia implementar
isso em várias etapas ou apenas implemente uma ou outra, dependendo da urgência,
a diferença entre alterar o banco de dados e o código ao seu redor e sua tolerância
para pedaços de código sem sentido que ficam por aí.

Opção 1 – Tornar o argumento não operacional (sem operação)

Porque você não pode alterar imediatamente a função para descartar o argumento
completamente, sem alterar simultaneamente todo o código que o chama, você pode
comece tornando-o “mais ou menos” opcional e remova a lógica dentro do
função:

cupom com desconto - o melhor site de cupom de desconto cupomcomdesconto.com.br
ALTER FUNCTION dbo.CalculateSalesCommission
(
  @ProductID int, @ignore_me tinyint = NULL
)
RETURNS decimal(5,4)
AS
BEGIN
  RETURN
 (
    SELECT CASE WHEN @ProductID % 7 = 0 THEN 0.04 ELSE 0.03 END
  );
END
GO

Eu digo “mais ou menos” opcional porque o valor que você passa é ignorado,
mas você ainda precisa passar alguma coisa. Isso permite que o código existente
continue chamando a função, com o valor do tamanho do sapato, como se nada tivesse mudado.
Você poderia então mudar lentamente cada pedaço de código, um de cada vez, para (a) eliminar qualquer
lógica envolvida na determinação do tamanho do sapato e (b)
NULO ou PADRÃO palavras-chave
para a função:

SELECT [Commission] = dbo.CalculateSalesCommission(41, NULL);
-- or
SELECT [Commission] = dbo.CalculateSalesCommission(41, DEFAULT);

Você pode gradualmente introduzir essa alteração nos procedimentos e no código do aplicativo
durante qualquer período de tempo, para que a alteração do código não precise ser fortemente acoplada
para a mudança de função. Você pode começar dessa maneira se for mais importante
remova as informações sobre o tamanho dos sapatos dos chamadores (novamente, digamos, determinando
tamanho era um cálculo caro ou violado

Leia Também  Instrução MySQL Create Table com exemplos

GDPR, ou os dados simplesmente não estavam mais disponíveis). Feito isso (ou você
pode pular esta etapa), você pode introduzir uma segunda função.

Opção 2 – Mova a lógica para uma nova função

Para se afastar da função antiga com dois argumentos (sejam eles
ainda estão passando valores reais de tamanho de sapato ou agora estão aceitando NULL / DEFAULT),
você pode criar uma nova função com um nome temporário. Você criaria isso com
apenas o único argumento, coloque a lógica lá e – quando estiver pronto –
basta fazer a função antiga chamar a nova função. Isso permite que algum código antigo continue
chamando a forma antiga da função, mas permite ajustar outro código para chamar o
nova função sem o argumento inútil.

CREATE FUNCTION dbo.CalculateSalesCommissionPartDeux
(
  @ProductID int-- no second argument
)
RETURNS decimal(5,4)
AS
BEGIN
  RETURN
 (
    SELECT CASE WHEN @ProductID % 7 = 0 THEN 0.04 ELSE 0.03 END
  );
END
GO

Uma vez instalado, você pode alterar a função original para remover o
lógica completamente e apenas chame a nova função, passando apenas o primeiro argumento:

ALTER FUNCTION dbo.CalculateSalesCommission
(
  @ProductID int, @ShoeSize tinyint = NULL
)
RETURNS decimal(5,4)
AS
BEGIN
  RETURN
 (
    SELECT dbo.CalculateSalesCommissionPartDeux(@ProductID);
  );
END
GO

Nesse caso, também, o código existente pode continuar chamando a função antiga, mas
estará usando a nova lógica (e essa lógica ainda estará em apenas um lugar).

Quando você atualiza todo o código de chamada para chamar apenas a nova função com
o argumento único e você tem certeza de que a função antiga não está mais sendo chamada,
você pode largá-lo. Pelo menos para funções escalares, você pode monitorar

Leia Também  Treinamento Online do SQLpassion em junho de 2019 |

sys.dm_exec_function_stats para ter certeza, mas tenha um backup em algum lugar apenas
caso precise restaurá-lo por qualquer motivo (não estou preocupado; você está
usando

fonte de controle, certo?). Você pode até renomear a nova função para ter
o nome antigo e crie um sinônimo para redirecionar chamadas para o nome temporário até
você pode limpar tudo isso também.

Conclusão

Não há mágica aqui – alterar uma API pode ser doloroso. Pode ser
tentador deixar parâmetros não utilizados nas interfaces de função, mas acho que remover
essas coisas obsoletas são melhores em geral para uma arquitetura de longo prazo. E lá
certamente existem abordagens que você pode adotar para tornar essas alterações mais suaves e graduais,
para que você não precise alterar a base de código de toda a sua infraestrutura
de uma só vez. Isso pode levar mais passos do que muitos de nós gostariam.

Próximos passos

Continue lendo para obter dicas relacionadas e outros recursos que envolvem fazer alterações ao contrário
compatível:

Última atualização: 2020-03-18

Sobre o autor

Aaron Bertrand (@AaronBertrand) é um tecnólogo apaixonado com experiência no setor que remonta ao ASP clássico e SQL Server 6.5. Ele é editor-chefe do blog relacionado ao desempenho, SQLPerformance.com, e também blogs no sqlblog.org.

Ver todas as minhas dicas