Sou iniciante em T-SQL. Quero decidir se uma string de entrada é um palíndromo, com saída = 0 se não for e saída = 1 se for. Ainda estou descobrindo a sintaxe. Não estou nem recebendo uma mensagem de erro. Estou procurando soluções diferentes e algum feedback, para obter uma melhor compreensão e conhecimento de como o T-SQL funciona, para me tornar melhor nisso - ainda sou um estudante.
A ideia-chave, a meu ver, é comparar os caracteres mais à esquerda e à direita entre si, para verificar a igualdade e, em seguida, comparar o segundo caractere da esquerda com o 2º do último, etc. Fazemos um loop: Se os personagens forem iguais entre si, continuamos. Se chegarmos ao fim, produzimos 1, caso contrário, produzimos 0.
Você poderia criticar:
CREATE function Palindrome(
@String Char
, @StringLength Int
, @n Int
, @Palindrome BIN
, @StringLeftLength Int
)
RETURNS Binary
AS
BEGIN
SET @ n=1
SET @StringLength= Len(String)
WHILE @StringLength - @n >1
IF
Left(String,@n)=Right(String, @StringLength)
SET @n =n+1
SET @StringLength =StringLength -1
RETURN @Binary =1
ELSE RETURN @Palindrome =0
END
Acho que estou no caminho certo, mas ainda estou muito longe. Alguma ideia?
Se você estiver usando o SQL Server, pode usar a função REVERSE () para verificar?
Incluindo o comentário de Martin Smith, se você estiver no SQL Server 2012+, poderá usar a função IIF() :
Como há um bom número de soluções, irei com a parte "crítica" da sua pergunta. Algumas observações: corrigi alguns erros de digitação e anotei onde fiz. Se eu estiver errado sobre eles serem um erro de digitação, mencione nos comentários e eu explicarei o que está acontecendo. Vou apontar várias coisas que você já deve saber, então, por favor, não se ofenda se eu souber. Alguns comentários podem parecer exigentes, mas não sei onde você está em sua jornada, portanto, deve presumir que está apenas começando.
SEMPRE inclua o comprimento com uma definição
char
ou .varchar
Aaron Bertrand fala sobre isso em profundidade aqui . Ele está falando,varchar
mas o mesmo vale parachar
. Eu usaria avarchar(255)
para isso se você quiser apenas strings relativamente curtas ou talvez avarchar(8000)
para as maiores ou mesmovarchar(max)
.Varchar
é para strings de comprimento variávelchar
é apenas para strings fixas. Como você não tem certeza do comprimento da string que está sendo passada em usevarchar
. Também nãobinary
ébin
.Em seguida, você não precisa colocar todas essas variáveis como parâmetros. Declare-os dentro do seu código. Apenas coloque algo na lista de parâmetros se você planeja passá-lo ou retirá-lo. (Você verá como isso fica no final.) Você também o tem,
@StringLeftLength
mas nunca o usa. Então não vou declarar.A próxima coisa que vou fazer é reformatar um pouco para tornar algumas coisas óbvias.
Se você observar como fiz o recuo, notará que tenho isso:
Isso ocorre porque os comandos gostam
WHILE
eIF
afetam apenas a primeira linha de código após eles. Você tem que usar umBEGIN .. END
bloco se quiser vários comandos. Fixando assim temos:Você notará que eu adicionei apenas um
BEGIN .. END
bloco no arquivoIF
. Isso ocorre porque, embora aIF
instrução tenha várias linhas (e até mesmo vários comandos), ela ainda é uma única instrução (cobrindo tudo executado noIF
e asELSE
partes da instrução).Em seguida, você receberá um erro após ambos os arquivos
RETURNs
. Você pode retornar uma variável OU um literal. Você não pode definir a variável e retorná-la ao mesmo tempo.Agora estamos na lógica. Primeiro, deixe-me apontar que as funções
LEFT
eRIGHT
que você está usando são ótimas, mas fornecerão o número de caracteres que você passar da direção solicitada. Então, digamos que você passou na palavra "teste". Na primeira passagem, você obterá isso (removendo variáveis):Obviamente não era isso que você esperava. Você realmente gostaria de usar
substring
em seu lugar. Substring permite que você passe não apenas o ponto inicial, mas também o comprimento. Então você obteria:Em seguida, você está incrementando as variáveis que usa em seu loop apenas em uma condição da instrução IF. Puxe a variável incrementando totalmente para fora dessa estrutura. Isso exigirá um
BEGIN .. END
bloco adicional, mas posso remover o outro.Você precisa mudar sua
WHILE
condição para permitir o último teste.E por último, mas não menos importante, do jeito que está agora, não testamos o último caractere se houver um número ímpar de caracteres. Por exemplo, com 'ana' o
n
não é testado. Tudo bem, mas acho que precisamos contabilizar uma palavra com uma única letra (se você quiser que ela conte como positiva). Então, podemos fazer isso definindo o valor na frente.E agora finalmente temos:
Um último comentário. Eu sou um grande fã de formatação em geral. Isso pode realmente ajudá-lo a ver como seu código funciona e ajudar a apontar possíveis erros.
Editar
Como Sphinxxx mencionou, ainda temos uma falha em nossa lógica. Depois de acertar o
ELSE
e definir@Palindrome
como 0, não há sentido em continuar. Na verdade, nesse ponto, poderíamos apenasRETURN
.Given that we are now only using
@Palindrome
for "it's still possible this is a palindrome" there is really no point in having it. We can get rid of the variable and switch our logic to short circuit on failure (theRETURN 0
) andRETURN 1
(a positive response) only if it makes it all the way through the loop. You'll notice this actually simplifies our logic somewhat.Você também pode usar uma abordagem de tabela do Numbers.
Se você ainda não possui uma tabela de números auxiliares, pode criar uma da seguinte maneira. Isso é preenchido com um milhão de linhas e, portanto, será bom para comprimentos de string de até 2 milhões de caracteres.
O abaixo compara cada caractere à esquerda com seu parceiro correspondente à direita e, se alguma discrepância for encontrada, pode causar um curto-circuito e retornar 0. Se a string tiver um comprimento ímpar, o caractere do meio não será verificado, pois isso não alterará o resultado .
Se você não tem certeza de como funciona, você pode ver abaixo
Este é basicamente o mesmo algoritmo descrito na pergunta, mas feito de maneira baseada em conjunto, em vez de código processual iterativo.
O
REVERSE()
método "melhorado", ou seja, invertendo apenas metade da string:Não espero que nada estranho aconteça se a string tiver um número ímpar de caracteres; o caractere do meio não precisa ser verificado.
Uma observação foi levantada por @hvd de que isso pode não lidar com pares substitutos corretamente em todos os agrupamentos.
@srutzky comentou que lida com Caracteres Suplementares / Pares Substitutos da mesma maneira que o
REVERSE()
método, pois eles só funcionam corretamente quando o Collation padrão do banco de dados atual termina em_SC
.Sem usar
REVERSE
, que é o que vem imediatamente à mente, mas ainda usando uma função 1 ; Eu construiria algo como o seguinte.Esta parte simplesmente removeu a função existente, se ela já existir:
Esta é a função em si:
Aqui, testamos a função:
Isso compara a primeira metade da palavra com o reverso da última metade da palavra (sem usar a
REVERSE
função). Este código lida adequadamente com palavras de comprimento ímpar e par. Em vez de percorrer a palavra inteira, simplesmente obtemos oLEFT
da primeira metade da palavra e, em seguida, percorremos a última metade da palavra para obter a parte invertida da metade direita. Se a palavra for de comprimento ímpar, pulamos a letra do meio, pois por definição ela será a mesma para as duas "metades".1 - as funções podem ser muito lentas!
Without using REVERSE... It's always fun to use a recursive solution ;) (I did mine in SQL Server 2012, earlier versions might have limitations on recursion)
This is an inline TVF-friendly version of Martin Smith's set-based solution, additionally decorated with a couple of superfluous enhancements:
Apenas por diversão, aqui está uma função SQL Server 2016 Scalar definida pelo usuário com o recurso In-Memory OLTP:
Um grande problema que você encontrará é que, com qualquer valor maior que 1,
LEFT
ouRIGHT
retornará vários caracteres, não o caractere nessa posição. Se você quiser manter esse método de teste, uma maneira realmente simples de modificá-lo seriaIsso sempre pegará o caractere mais à direita da string esquerda e o caractere mais à esquerda da string direita.
Talvez uma maneira menos indireta de verificar isso, no entanto, seja usar
SUBSTRING
:Observe que
SUBSTRING
é indexado por 1, portanto, o+ 1
in((LEN(String) - @n) + 1)
.