Eu sou iniciante em T-SQL. Eu quero decidir se uma string de entrada é um palíndromo, com output = 0 se não for e output = 1 se for. Ainda estou descobrindo a sintaxe. Eu nem estou recebendo uma mensagem de erro. Estou procurando soluções diferentes e algum feedback, para obter um melhor entendimento e conhecimento de como o T-SQL funciona, para me aperfeiçoar - ainda sou estudante.
A idéia principal, a meu ver, é comparar os caracteres mais à esquerda e à direita, verificar a igualdade e comparar o segundo caractere da esquerda com o 2º do último, etc. Fazemos um loop: se os caracteres forem iguais, continuaremos. Se chegamos ao fim, produzimos 1, se não, 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 longe. Alguma ideia?
LTRIM(RTRIM(...))
espaço em branco?Respostas:
Se você estiver usando o SQL Server, poderá 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 () :
fonte
Como existe um número razoável de soluções, eu irei à parte "crítica" da sua pergunta. Algumas notas: Corrigi alguns erros de digitação e observei onde fiz. Se eu estiver errado sobre o erro de digitação, mencione-o nos comentários e explico o que está acontecendo. Vou apontar várias coisas que você já deve saber, então, por favor, não se ofenda se eu soubesse. Alguns comentários podem parecer exigentes, mas eu não sei onde você está em sua jornada, portanto, suponha que você está apenas começando.
SEMPRE inclua o comprimento com a
char
ouvarchar
definition. Aaron Bertrand fala sobre isso em profundidade aqui . Ele está falando,varchar
mas o mesmo vale parachar
. Eu usaria umvarchar(255)
para isso se você quiser apenas cordas relativamente curtas ou talvez umavarchar(8000)
para cordas maiores ou até mesmovarchar(max)
.Varchar
é para cadeias de comprimento variávelchar
é apenas para cadeias fixas. Como você não tem certeza do comprimento da cadeia que está sendo passada em usovarchar
. Também ébinary
nãobin
.Em seguida, você não precisa colocar todas essas variáveis como parâmetros. Declare-os dentro do seu código. Coloque apenas algo na lista de parâmetros se você planeja passá-lo para dentro ou para fora. (Você verá como isso fica no final.) Além disso, você tem @StringLeftLength, mas nunca o usa. Então, eu não vou declarar isso.
A próxima coisa que vou fazer é reformatar um pouco para deixar algumas coisas óbvias.
Se você olhar para o jeito que eu fiz o recuo, você perceberá que eu tenho isso:
Isso ocorre porque comandos como
WHILE
eIF
afetam apenas a primeira linha de código após eles. Você precisa usar umBEGIN .. END
bloco se desejar vários comandos. Então, corrigindo o que temos:Você notará que eu adicionei apenas um
BEGIN .. END
bloco noIF
. IssoIF
ocorre porque, embora a declaração tenha várias linhas (e até contenha vários comandos), ainda é uma única declaração (cobrindo tudo o que é executado nas partesIF
e nasELSE
partes da declaração).Em seguida, você receberá um erro após os dois
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 salientar que as funções
LEFT
e queRIGHT
você está usando são ótimas, mas elas fornecem o número de caracteres que você passa da direção solicitada. Então, digamos que você passou na palavra "teste". Na primeira passagem, você obterá isso (removendo variáveis):Obviamente, isso não é o que você esperava. Você realmente gostaria de usar
substring
. Substring permite passar não apenas o ponto inicial, mas também o comprimento. Então você obteria:Em seguida, você está incrementando as variáveis que você usa em seu loop apenas em uma condição da instrução SE. Puxe a variável incrementada para fora dessa estrutura completamente. Isso vai exigir um
BEGIN .. END
bloco adicional , mas eu consigo remover o outro.Você precisa alterar 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, para mim, precisamos contabilizar uma única letra (se você quiser que isso seja positivo). Então, podemos fazer isso definindo o valor antecipadamente.E agora finalmente temos:
Um último comentário. Eu sou um grande fã de formatação em geral. Pode realmente ajudar você 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. Quando atingimos
ELSE
e definimos@Palindrome
como 0, não faz sentido continuar. De fato, naquele ponto, poderíamos apenasRETURN
.Dado que agora estamos usando apenas
@Palindrome
para "ainda é possível, isso é um palíndromo", não há realmente sentido em tê-lo. Podemos nos livrar da variável e mudar nossa lógica para um curto-circuito em caso de falha (aRETURN 0
) eRETURN 1
(uma resposta positiva) apenas se ela passar por todo o ciclo. Você notará que isso realmente simplifica um pouco nossa lógica.fonte
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 cadeia de até 2 milhões de caracteres.
A seguir, compara cada caractere à esquerda com seu parceiro correspondente à direita e, se forem encontradas discrepâncias, 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, pode ver a seguir
Esse é basicamente o mesmo algoritmo descrito na pergunta, mas feito de maneira baseada em conjunto, em vez de código processual iterativo.
fonte
O
REVERSE()
método "melhorou", ou seja, revertendo apenas metade da string:Não espero que algo estranho aconteça se a string tiver um número ímpar de caracteres; o personagem do meio não precisa ser verificado.
Uma observação foi levantada pelo @hvd de que isso pode não lidar corretamente com pares substitutos em todos os agrupamentos.
A @srutzky comentou que lida com pares de caracteres suplementares / substitutos da mesma maneira que o
REVERSE()
método, na medida em que eles só funcionam corretamente quando o agrupamento padrão do banco de dados atual termina_SC
.fonte
Sem usar
REVERSE
, é o que imediatamente vem à mente, mas ainda usando a função 1 ; Eu construiria algo como o seguinte.Esta parte simplesmente removeu a função existente, se ela já existir:
Esta é a própria função:
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). Esse código lida adequadamente com palavras pares e ímpares. Em vez de percorrer a palavra inteira, simplesmente obtemos aLEFT
primeira metade da palavra e, em seguida, percorremos a última metade da palavra para obter a parte invertida da metade direita. Se a palavra tiver um comprimento ímpar, pularemos a letra do meio, pois, por definição, será a mesma para as duas "metades".1 - as funções podem ser muito lentas!
fonte
Sem usar REVERSE ... É sempre divertido usar uma solução recursiva;) (fiz o meu no SQL Server 2012, as versões anteriores podem ter limitações de recursão)
fonte
Esta é uma versão em linha compatível com TVF da solução baseada em conjuntos de Martin Smith , decorada adicionalmente com algumas melhorias supérfluas:
fonte
Apenas por diversão, aqui está uma função definida pelo usuário escalar do SQL Server 2016 com o recurso OLTP na memória:
fonte
Um dos principais problemas que você encontrará é que, com qualquer valor maior que 1,
LEFT
ouRIGHT
retornará vários caracteres, não o personagem nessa posição. Se você quiser continuar com esse método de teste, uma maneira realmente simples de modificá-lo seriaIsso sempre pega o caractere mais à direita da string esquerda e o caractere mais à esquerda da string direita.
Talvez uma maneira menos indireta de verificar isso, porém, fosse usar
SUBSTRING
:Observe que
SUBSTRING
é indexado em 1, daí o+ 1
in((LEN(String) - @n) + 1)
.fonte