Qual é a melhor maneira de criar uma função de função definida pelo usuário do gerador de senha no SQL Server se não for possível usar funções não determinísticas dentro das funções?
Gostaria de criar uma função que retorne um varchar(10) gerado aleatoriamente usando caracteres de uma determinada string como esta: "1234567890QWERTYUIOPASDFGHJKLZXCVBNM"
Tenho minhas próprias ideias que funcionam, mas não são soluções muito naturais.
A ideia por trás da função é que, se alguém exigir uma redefinição de senha, eu poderia fazer o seguinte:
UPDATE Users SET Password = dbo.GeneratePassword() WHERE UserID = @UserID
Uma função seria mais fácil de implantar e reutilizar do que uma tabela separada de senhas pré-alocadas.
Você tem algumas opções (TL;DR -> a função de trabalho está no final da Opção nº 2, MAS também consulte a seção Atualizar após a Opção nº 3!):
Se você está bem com apenas valores hexadecimais (0 - 9 e A - F), basta chamar CRYPT_GEN_RANDOM , que foi introduzido no SQL Server 2008:
retorna:
Como você pode ver, você precisa usar a opção de formato
2
naCONVERT
função para que ela não forneça caracteres que, embora mais diversos, possam ser mais difíceis de digitar, e você não obtenha o0x
. Mas você também pode ver que, mesmo com uma única linha, várias chamadas obterão valores de retorno diferentes (assim comoNEWID()
).Observe os valores de "comprimento" passados para a
CRYPT_GEN_RANDOM
função. Como um byte hexadecimal tem 2 caracteres, você só precisa gerar um valor de 5 bytes. OCONVERT(VARCHAR...
usa um "comprimento" de 10, pois cada byte hexadecimal se torna um caractere. ECONVERT(NVARCHAR...
usa um "comprimento" de 20, pois a cada 2 bytes se tornará um caractere (ao executar isso em um banco de dados com um agrupamento padrão que não termina em_SC
, que é a maioria dos casos).Você sempre pode criar seu próprio algoritmo e passar o valor
CRYPT_GEN_RANDOM
para o aspecto aleatório. AND , se você puder estruturar seu algoritmo para ser único , deSELECT
modo que possa ser feito em uma função com valor de tabela embutida / iTVF (geralmente usando CTEs), então tecnicamente você pode usar uma função não determinística (pelo menos efetivamente), pois o chamada de função passará na expressão em vez do resultado dessa expressão, como acontece com TVFs de várias instruções e UDFs escalares:E então execute os testes:
E só para ter certeza sobre várias linhas em um iTVF:
retorna:
Colocando todas essas informações em prática, podemos fazer o seguinte em relação à solicitação original:
E depois execute da seguinte forma:
CUIDADO: Consulte o Item nº 2 abaixo relacionado à execução disso para várias linhas!
Você pode criar uma função SQLCLR que teria acesso à funcionalidade de randomização. A principal desvantagem aqui é que uma função Scalar tem muito mais probabilidade de ter seu valor de retorno armazenado em cache do que uma iTVF.
Atualizar
Dado o contexto recém-fornecido para uso desta função da atualização da pergunta:
podemos abordar as seguintes preocupações, conforme expressas em um comentário sobre esta resposta:
Verdadeiro. E é reconhecidamente um pouco inconveniente. No entanto, isso não nega a capacidade dessa abordagem de resolver o problema em questão. Requer apenas alguns comentários/documentação sobre como usar corretamente esta função.
Verdadeiro. Mas não há necessidade de chamar essa função dentro de outra função. Isso não precisa ser uma função escalar para ser usado em um
UPDATE
:Com relação à manipulação de várias linhas (consulte o teste de várias linhas na Opção 2 acima), funções de qualquer tipo (criadas pelo usuário, pelo menos) -- Scalar UDF, Multistatement TVF, iTVF, SQLCLR UDF ou SQLCLR TVF -- não são garantido para executar por linha ! Talvez seja necessário ser criativo para convencer o Otimizador de consulta a não armazenar em cache o resultado, mas pode haver casos em que nada funciona. O exemplo a seguir mostra o mesmo iTVF sendo chamado duas vezes. Passar apenas
@PasswordLength
faz com que o valor de saída seja armazenado em cache. No entanto, quando você passa uma fórmula que incorpora um campo da linha atual, ela pode se comportar como você espera:retorna:
O ponto feito por @BaconBits em um comentário sobre a questão, a respeito de ser melhor não armazenar senhas legíveis, é um ponto válido e bom. No entanto, as circunstâncias podem impedir que essa opção seja possível, ou pelo menos possível neste momento.
Mais uma opção é usar view.
Uso:
Código:
Eu uso isso
Mas como o SQL-Server é tão maravilhoso, você não pode usar NEWID em uma função escalar, então criei uma visão adicional "dbo.random". Como abaixo: