AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / dba / Perguntas / 137388
Accepted
MSIS
MSIS
Asked: 2016-05-04 09:42:42 +0800 CST2016-05-04 09:42:42 +0800 CST 2016-05-04 09:42:42 +0800 CST

Testar se uma string é um palíndromo usando T-SQL

  • 772

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?

sql-server sql-server-2012
  • 9 9 respostas
  • 8134 Views

9 respostas

  • Voted
  1. Shaneis
    2016-05-04T09:47:35+08:002016-05-04T09:47:35+08:00

    Se você estiver usando o SQL Server, pode usar a função REVERSE () para verificar?

    SELECT CASE WHEN @string = REVERSE(@String) THEN 1 ELSE 0 END AS Palindrome;
    

    Incluindo o comentário de Martin Smith, se você estiver no SQL Server 2012+, poderá usar a função IIF() :

    SELECT IIF(@string = REVERSE(@String),1,0) AS Palindrome;
    
    • 60
  2. Best Answer
    Kenneth Fisher
    2016-05-04T12:18:59+08:002016-05-04T12:18:59+08:00

    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.

    CREATE function Palindrome (
        @String  Char
        , @StringLength  Int
        , @n Int
        , @Palindrome BIN
        , @StringLeftLength  Int
    

    SEMPRE inclua o comprimento com uma definição charou . varcharAaron Bertrand fala sobre isso em profundidade aqui . Ele está falando, varcharmas o mesmo vale para char. Eu usaria a varchar(255)para isso se você quiser apenas strings relativamente curtas ou talvez a varchar(8000)para as maiores ou mesmo varchar(max). Varcharé para strings de comprimento variável charé apenas para strings fixas. Como você não tem certeza do comprimento da string que está sendo passada em use varchar. Também não binaryé 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, @StringLeftLengthmas nunca o usa. Então não vou declarar.

    A próxima coisa que vou fazer é reformatar um pouco para tornar algumas coisas óbvias.

    BEGIN
        SET @n=1
        SET @StringLength = Len(@String) -- Missed an @
    
        WHILE @StringLength - @n >1 
            IF Left(@String,@n)=Right(@String, @StringLength) -- More missing @s
                SET @n = @n + 1 -- Another missing @
    
        SET @StringLength = @StringLength - 1  -- Watch those @s :)
    
        RETURN @Palindrome = 1 -- Assuming another typo here 
    
        ELSE 
            RETURN @Palindrome =0
    
    END
    

    Se você observar como fiz o recuo, notará que tenho isso:

        WHILE @StringLength - @n >1 
            IF Left(@String,@n)=Right(@String, @StringLength)
                SET @n = @n + 1
    

    Isso ocorre porque os comandos gostam WHILEe IFafetam apenas a primeira linha de código após eles. Você tem que usar um BEGIN .. ENDbloco se quiser vários comandos. Fixando assim temos:

        WHILE @StringLength - @n > 1 
            IF Left(@String,@n)=Right(@String, @StringLength)
                BEGIN 
                    SET @n = @n + 1
                    SET @StringLength = @StringLength - 1
                    RETURN @Palindrome = 1 
                END
            ELSE 
                RETURN @Palindrome = 0
    

    Você notará que eu adicionei apenas um BEGIN .. ENDbloco no arquivo IF. Isso ocorre porque, embora a IFinstrução tenha várias linhas (e até mesmo vários comandos), ela ainda é uma única instrução (cobrindo tudo executado no IFe as ELSEpartes 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.

                    SET @Palindrome = 1 
                END
            ELSE 
                SET @Palindrome = 0
    
        RETURN @Palindrome
    

    Agora estamos na lógica. Primeiro, deixe-me apontar que as funções LEFTe RIGHTque 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):

    LEFT('test',1) = RIGHT('test',4)
        t          =      test
    
    LEFT('test',2) = RIGHT('test',3)
        te         =      est
    

    Obviamente não era isso que você esperava. Você realmente gostaria de usar substringem seu lugar. Substring permite que você passe não apenas o ponto inicial, mas também o comprimento. Então você obteria:

    SUBSTRING('test',1,1) = SUBSTRING('test',4,1)
             t            =         t
    
    SUBSTRING('test',2,1) = SUBSTRING('test',3,1)
             e            =         s
    

    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 .. ENDbloco adicional, mas posso remover o outro.

            WHILE @StringLength - @n > 1 
                BEGIN
                    IF SUBSTRING(@String,@n,1) = SUBSTRING(@String, @StringLength,1)
                        SET @Palindrome = 1 
                    ELSE 
                        SET @Palindrome = 0
    
                    SET @n = @n + 1
                    SET @StringLength = @StringLength - 1
                END
    

    Você precisa mudar sua WHILEcondição para permitir o último teste.

            WHILE @StringLength > @n 
    

    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 nnã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:

    CREATE FUNCTION Palindrome (@String  varchar(255)) 
    RETURNS Binary
    AS
    
        BEGIN
            DECLARE @StringLength  Int
                , @n Int
                , @Palindrome binary
    
            SET @n = 1
            SET @StringLength = Len(@String)
            SET @Palindrome = 1
    
            WHILE @StringLength > @n 
                BEGIN
                    IF SUBSTRING(@String,@n,1) = SUBSTRING(@String, @StringLength,1)
                        SET @Palindrome = 1 
                    ELSE 
                        SET @Palindrome = 0
    
                    SET @n = @n + 1
                    SET @StringLength = @StringLength - 1
                END
            RETURN @Palindrome
        END
    

    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 ELSEe definir @Palindromecomo 0, não há sentido em continuar. Na verdade, nesse ponto, poderíamos apenas RETURN.

                    IF SUBSTRING(@String,@n,1) = SUBSTRING(@String, @StringLength,1)
                        SET @Palindrome = 1 
                    ELSE 
                        RETURN 0
    

    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 (the RETURN 0) and RETURN 1 (a positive response) only if it makes it all the way through the loop. You'll notice this actually simplifies our logic somewhat.

    CREATE FUNCTION Palindrome (@String  varchar(255)) 
    RETURNS Binary
    AS
    
        BEGIN
            DECLARE @StringLength  Int
                , @n Int
    
            SET @n = 1
            SET @StringLength = Len(@String)
    
            WHILE @StringLength > @n 
                BEGIN
                    IF SUBSTRING(@String,@n,1) <> SUBSTRING(@String, @StringLength,1)
                        RETURN 0
    
                    SET @n = @n + 1
                    SET @StringLength = @StringLength - 1
                END
            RETURN 1
        END
    
    • 17
  3. Martin Smith
    2016-05-04T10:53:34+08:002016-05-04T10:53:34+08:00

    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.

    CREATE TABLE dbo.Numbers (number int PRIMARY KEY);
    
    INSERT INTO dbo.Numbers
                (number)
    SELECT TOP 1000000 ROW_NUMBER() OVER (ORDER BY @@SPID)
    FROM   master..spt_values v1,
           master..spt_values v2 
    

    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 .

    DECLARE @Candidate VARCHAR(MAX) = 'aibohphobia'; /*the irrational fear of palindromes.*/
    
    SET @Candidate = LTRIM(RTRIM(@Candidate)); /*Ignoring any leading or trailing spaces. 
                          Could use `DATALENGTH` instead of `LEN` if these are significant*/
    
    SELECT CASE
             WHEN EXISTS (SELECT *
                          FROM   dbo.Numbers
                          WHERE  number <= LEN(@Candidate) / 2
                                 AND SUBSTRING(@Candidate, number, 1) 
                                  <> SUBSTRING(@Candidate, 1 + LEN(@Candidate) - number, 1))
               THEN 0
             ELSE 1
           END AS IsPalindrome 
    

    Se você não tem certeza de como funciona, você pode ver abaixo

    DECLARE @Candidate VARCHAR(MAX) = 'this is not a palindrome';
    
    SELECT SUBSTRING(@Candidate, number, 1)                       AS [Left],
           SUBSTRING(@Candidate, 1 + LEN(@Candidate) - number, 1) AS [Right]
    FROM   dbo.Numbers
    WHERE  number <= LEN(@Candidate) / 2; 
    

    insira a descrição da imagem aqui

    Este é basicamente o mesmo algoritmo descrito na pergunta, mas feito de maneira baseada em conjunto, em vez de código processual iterativo.

    • 15
  4. ypercubeᵀᴹ
    2016-05-04T11:26:34+08:002016-05-04T11:26:34+08:00

    O REVERSE()método "melhorado", ou seja, invertendo apenas metade da string:

    SELECT CASE WHEN RIGHT(@string, LEN(@string)/2) 
                     = REVERSE(LEFT(@string, LEN(@string)/2)) 
                THEN 1 ELSE 0 END AS Palindrome;
    

    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.

    • 12
  5. Hannah Vernon
    2016-05-04T10:25:24+08:002016-05-04T10:25:24+08:00

    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:

    IF OBJECT_ID('dbo.IsPalindrome') IS NOT NULL
    DROP FUNCTION dbo.IsPalindrome;
    GO
    

    Esta é a função em si:

    CREATE FUNCTION dbo.IsPalindrome
    (
        @Word NVARCHAR(500)
    ) 
    RETURNS BIT
    AS
    BEGIN
        DECLARE @IsPalindrome BIT;
        DECLARE @LeftChunk NVARCHAR(250);
        DECLARE @RightChunk NVARCHAR(250);
        DECLARE @StrLen INT;
        DECLARE @Pos INT;
    
        SET @RightChunk = '';
        SET @IsPalindrome = 0;
        SET @StrLen = LEN(@Word) / 2;
        IF @StrLen % 2 = 1 SET @StrLen = @StrLen - 1;
        SET @Pos = LEN(@Word);
        SET @LeftChunk = LEFT(@Word, @StrLen);
    
        WHILE @Pos > (LEN(@Word) - @StrLen)
        BEGIN
            SET @RightChunk = @RightChunk + SUBSTRING(@Word, @Pos, 1)
            SET @Pos = @Pos - 1;
        END
    
        IF @LeftChunk = @RightChunk SET @IsPalindrome = 1;
        RETURN (@IsPalindrome);
    END
    GO
    

    Aqui, testamos a função:

    IF dbo.IsPalindrome('This is a word') = 1 
        PRINT 'YES'
    ELSE
        PRINT 'NO';
    
    IF dbo.IsPalindrome('tattarrattat') = 1 
        PRINT 'YES'
    ELSE
        PRINT 'NO';
    

    Isso compara a primeira metade da palavra com o reverso da última metade da palavra (sem usar a REVERSEfunção). Este código lida adequadamente com palavras de comprimento ímpar e par. Em vez de percorrer a palavra inteira, simplesmente obtemos o LEFTda 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!

    • 8
  6. Kevin Suchlicki
    2016-05-05T07:43:53+08:002016-05-05T07:43:53+08:00

    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)

    create function dbo.IsPalindrome (@s varchar(max)) returns bit
    as
    begin
        return case when left(@s,1) = right(@s,1) then case when len(@s) < 3 then 1 else dbo.IsPalindrome(substring(@s,2,len(@s)-2)) end else 0 end;
    end;
    GO
    
    select dbo.IsPalindrome('a')
    1
    select dbo.IsPalindrome('ab')
    0
    select dbo.IsPalindrome('bab')
    1
    select dbo.IsPalindrome('gohangasalamiimalasagnahog')
    1
    
    • 6
  7. Andriy M
    2016-05-06T22:48:50+08:002016-05-06T22:48:50+08:00

    This is an inline TVF-friendly version of Martin Smith's set-based solution, additionally decorated with a couple of superfluous enhancements:

    WITH Nums AS
    (
      SELECT
        N = number
      FROM
        dbo.Numbers WITH(FORCESEEK) /*Requires a suitably indexed numbers table*/
    )
    SELECT
      IsPalindrome =
        CASE
          WHEN EXISTS
          (
            SELECT *
            FROM Nums
            WHERE N <= L / 2
              AND SUBSTRING(S, N, 1) <> SUBSTRING(S, 1 + L - N, 1)
          )
          THEN 0
          ELSE 1
        END
    FROM
      (SELECT LTRIM(RTRIM(@Candidate)), LEN(@Candidate)) AS v (S, L)
    ;
    
    • 6
  8. wBob
    2016-05-08T11:12:06+08:002016-05-08T11:12:06+08:00

    Apenas por diversão, aqui está uma função SQL Server 2016 Scalar definida pelo usuário com o recurso In-Memory OLTP:

    ALTER FUNCTION dbo.IsPalindrome2 ( @inputString NVARCHAR(500) )
    RETURNS BIT
    WITH NATIVE_COMPILATION, SCHEMABINDING
    AS
    BEGIN ATOMIC WITH (TRANSACTION ISOLATION LEVEL = SNAPSHOT, LANGUAGE = N'English')
    
        DECLARE @i INT = 1, @j INT = LEN(@inputString)
    
        WHILE @i < @j
        BEGIN
            IF SUBSTRING( @inputString, @i, 1 ) != SUBSTRING( @inputString, @j, 1 )
            BEGIN
                RETURN(0)
            END
            ELSE
                SELECT @i+=1, @j-=1
    
        END
    
        RETURN(1)
    
    END
    GO
    
    • 5
  9. Andy
    2016-05-04T10:58:59+08:002016-05-04T10:58:59+08:00

    Um grande problema que você encontrará é que, com qualquer valor maior que 1, LEFTou RIGHTretornará 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 seria

    RIGHT(LEFT(String,@n),1)=LEFT(RIGHT(String, @StringLength),1)
    

    Isso 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:

    SUBSTRING(String, @n, 1) = SUBSTRING(String, ((LEN(String) - @n) + 1), 1)
    

    Observe que SUBSTRINGé indexado por 1, portanto, o + 1in ((LEN(String) - @n) + 1).

    • 4

relate perguntas

  • SQL Server - Como as páginas de dados são armazenadas ao usar um índice clusterizado

  • Preciso de índices separados para cada tipo de consulta ou um índice de várias colunas funcionará?

  • Quando devo usar uma restrição exclusiva em vez de um índice exclusivo?

  • Quais são as principais causas de deadlocks e podem ser evitadas?

  • Como determinar se um Índice é necessário ou necessário

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host

    • 12 respostas
  • Marko Smith

    Como fazer a saída do sqlplus aparecer em uma linha?

    • 3 respostas
  • Marko Smith

    Selecione qual tem data máxima ou data mais recente

    • 3 respostas
  • Marko Smith

    Como faço para listar todos os esquemas no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Listar todas as colunas de uma tabela especificada

    • 5 respostas
  • Marko Smith

    Como usar o sqlplus para se conectar a um banco de dados Oracle localizado em outro host sem modificar meu próprio tnsnames.ora

    • 4 respostas
  • Marko Smith

    Como você mysqldump tabela (s) específica (s)?

    • 4 respostas
  • Marko Smith

    Listar os privilégios do banco de dados usando o psql

    • 10 respostas
  • Marko Smith

    Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Como faço para listar todos os bancos de dados e tabelas usando o psql?

    • 7 respostas
  • Martin Hope
    Jin conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane Como faço para listar todos os esquemas no PostgreSQL? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh Por que o log de transações continua crescendo ou fica sem espaço? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland Listar todas as colunas de uma tabela especificada 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney O MySQL pode realizar consultas razoavelmente em bilhões de linhas? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx Como posso monitorar o andamento de uma importação de um arquivo .sql grande? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison Como você mysqldump tabela (s) específica (s)? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas Como posso cronometrar consultas SQL usando psql? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas Como faço para listar todos os bancos de dados e tabelas usando o psql? 2011-02-18 00:45:49 +0800 CST

Hot tag

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve