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 / 41961
Accepted
Ario
Ario
Asked: 2013-05-11 15:12:31 +0800 CST2013-05-11 15:12:31 +0800 CST 2013-05-11 15:12:31 +0800 CST

Como encontrar todas as posições de uma string dentro de outra string

  • 772

Como posso encontrar todas as posições patindexem uma tabela ou variável?

declare @name nvarchar(max)
set @name ='ali reza dar yek shabe barani ba yek  '
  + 'dokhtare khoshkel be disco raft va ali baraye'
  + ' 1 saat anja bud va sepas... ali...'
select patindex('%ali%',@name) as pos 

Isso retorna 1, mas eu quero todos os resultados, por exemplo:

pos
===
  1
 74
113
sql-server sql-server-2005
  • 7 7 respostas
  • 58483 Views

7 respostas

  • Voted
  1. Aaron Bertrand
    2013-05-13T05:14:26+08:002013-05-13T05:14:26+08:00

    Acho que isso será um pouco mais eficiente que o método de loop que você escolheu ( algumas evidências aqui ) e definitivamente mais eficiente que o CTE recursivo:

    CREATE FUNCTION dbo.FindPatternLocation
    (
        @string NVARCHAR(MAX),
        @term   NVARCHAR(255)
    )
    RETURNS TABLE
    AS
        RETURN 
        (
          SELECT pos = Number - LEN(@term) 
          FROM (SELECT Number, Item = LTRIM(RTRIM(SUBSTRING(@string, Number, 
          CHARINDEX(@term, @string + @term, Number) - Number)))
          FROM (SELECT ROW_NUMBER() OVER (ORDER BY [object_id])
          FROM sys.all_objects) AS n(Number)
          WHERE Number > 1 AND Number <= CONVERT(INT, LEN(@string)+1)
          AND SUBSTRING(@term + @string, Number, LEN(@term)) = @term
        ) AS y);
    

    Exemplo de uso:

    DECLARE @name NVARCHAR(MAX);
    
    SET @name = N'ali reza dar yek shabe barani ba yek'
        + '  dokhtare khoshkel be disco raft va ali baraye '
        + '1 saat anja bud va sepas... ali...';
    
    SELECT pos FROM dbo.FindPatternLocation(@name, 'ali');
    

    Resultados:

    pos
    ---
      1
     74
    113
    

    Se suas strings forem maiores que 2K, use sys.all_columns em vez de sys.all_objects. Se for maior que 8K, adicione uma junção cruzada.

    • 17
  2. Best Answer
    bummi
    2013-05-12T00:38:52+08:002013-05-12T00:38:52+08:00
    declare @name nvarchar(max)
    set @name ='ali reza dar yek shabe barani ba yek  dokhtare khoshkel be disco raft va ali baraye 1 saat anja bud va sepas... ali...'
    
    Declare @a table (pos int)
    Declare @pos int
    Declare @oldpos int
    Select @oldpos=0
    select @pos=patindex('%ali%',@name) 
    while @pos > 0 and @oldpos<>@pos
     begin
       insert into @a Values (@pos)
       Select @oldpos=@pos
       select @pos=patindex('%ali%',Substring(@name,@pos + 1,len(@name))) + @pos
    end
    
    Select * from @a
    

    Para torná-lo reutilizável, você pode usá-lo em uma função de tabela para chamá-lo como:

    Select * from  dbo.F_CountPats ('ali reza dar yek shabe barani ba yek  dokhtare khoshkel be disco raft va ali baraye 1 saat anja bud va sepas... ali...','%ali%')
    

    A função pode ficar assim

    Create FUNCTION [dbo].[F_CountPats] 
    (
    @txt varchar(max),
    @Pat varchar(max)
    )
    RETURNS 
    @tab TABLE 
    (
     ID int
    )
    AS
    BEGIN
    Declare @pos int
    Declare @oldpos int
    Select @oldpos=0
    select @pos=patindex(@pat,@txt) 
    while @pos > 0 and @oldpos<>@pos
     begin
       insert into @tab Values (@pos)
       Select @oldpos=@pos
       select @pos=patindex(@pat,Substring(@txt,@pos + 1,len(@txt))) + @pos
    end
    
    RETURN 
    END
    
    GO
    
    • 9
  3. msi77
    2013-05-12T08:05:15+08:002013-05-12T08:05:15+08:00

    --CTE Recursivo

    with cte as
    (select 'ali reza dar yek shabe barani ba yek  dokhtare khoshkel be disco raft va ali baraye 1 saat anja bud va sepas... ali...' as name
    ), 
    pos as
    (select patindex('%ali%',name) pos, name from cte
    union all
    select pos+patindex('%ali%',substring(name, pos+1, len(name))) pos, name from pos
    where patindex('%ali%',substring(name, pos+1, len(name)))>0
    )
    select pos from pos
    
    • 2
  4. Nils
    2015-07-29T00:08:53+08:002015-07-29T00:08:53+08:00

    Eu amo a resposta de Aaron Bertrand. Embora eu não entenda completamente, parece muito elegante.

    No passado, tive problemas com permissões ao usar sys.objects. Combinado com a necessidade de solucionar problemas do código, criei uma variação do código de Aaron e a adicionei abaixo.

    Este é o meu procedimento:

    CREATE PROCEDURE dbo.FindPatternLocations
    -- Params
    @TextToSearch nvarchar (max),
    @TextToFind nvarchar (255)
    
    AS
    BEGIN
    
        declare @Length int
        set @Length = (Select LEN(@TextToSearch))
    
        declare @LengthSearchString int
        set @LengthSearchString = (select LEN (@TextToFind))
    
        declare @Index int
        set @Index=1
    
        create table #Positions (
        [POSID] [int] IDENTITY(0,1) NOT FOR REPLICATION NOT NULL,
        POS int
        )
    
        insert into #Positions (POS) select 0 -- to return a row even if no findings occur
    
            set @Index = (select charindex(@TextToFind, @TextToSearch, @Index))
                        if @Index = 0 goto Ende -- TextToFind is not in TextToSearch
    
            insert into #Positions (POS) select @Index
    
    
            set @Index = @Index + @LengthSearchString
    
    while @Index <= @Length - @LengthSearchString   
        Begin
                set @Index = (select charindex(@TextToFind, @TextToSearch, @Index) )
                if @Index = 0 goto Ende -- no findings anymore
                insert into #Positions (POS) select @Index
                set @Index = @Index + @LengthSearchString
        end
    Ende:
    if (select MAX(posid) from #Positions) > 0 delete from #Positions where POSID = 0 -- row is not needed if TextToFind occurs
    select * from #Positions
    END
    GO
    

    O MAX(posid)valor também é o número de correspondências encontradas.

    • 0
  5. JotaPardo
    2017-01-05T07:36:04+08:002017-01-05T07:36:04+08:00

    Este é um código simples baseado na resposta de Aaron que:

    • Não limitado ao tamanho de sys.all_objects
    • Não perca o último 'X'

    CÓDIGO:

    DECLARE @termToFind CHAR(1) = 'X'
    DECLARE @string VARCHAR(40) = 'XX XXX  X   XX'
    
    SET @string += '.' --Add any data here (different from the one searched) to get the position of the last character
    
    DECLARE @stringLength BIGINT = len(@string)
    
    SELECT pos = Number - LEN(@termToFind)
    FROM (
        SELECT Number
            , Item = LTRIM(RTRIM(SUBSTRING(@string, Number, CHARINDEX(@termToFind, @string + @termToFind, Number) - Number)))
        FROM (
            --All numbers between 1 and the lengh of @string. Better than use sys.all_objects
            SELECT TOP (@stringLength) row_number() OVER (
                    ORDER BY t1.number
                    ) AS N
            FROM master..spt_values t1
            CROSS JOIN master..spt_values t2
            ) AS n(Number)
        WHERE Number > 1
            AND Number <= CONVERT(INT, LEN(@string))
            AND SUBSTRING(@termToFind + @string, Number, LEN(@termToFind)) = @termToFind
        ) AS y
    

    RESULTADO

    pos
    --------------------
    1
    2
    4
    5
    6
    9
    13
    14
    
    (8 row(s) affected)
    
    • 0
  6. OzrenTkalcecKrznaric
    2019-11-23T07:38:03+08:002019-11-23T07:38:03+08:00

    Desculpe, pessoal, por aparecer tão tarde, mas gostaria de facilitar as coisas para as pessoas que desejam expandir isso. Eu estava olhando cada uma dessas implementações, peguei a que me pareceu melhor (Aaron Bertrand), simplifiquei e aqui está, você tem o "template". Use-o com sabedoria.

    CREATE FUNCTION dbo.CHARINDICES (
        @search_expression NVARCHAR(4000),
        @expression_to_be_searched NVARCHAR(MAX)
    ) RETURNS TABLE AS RETURN (
        WITH tally AS (
            SELECT Number = ROW_NUMBER() OVER (ORDER BY [object_id])
            FROM sys.all_objects)
        SELECT DISTINCT n = subIdx -- (4) if we don't perform distinct we'll get result for each searched substring, and we don't want that
        FROM 
            tally 
            CROSS APPLY (SELECT subIdx = CHARINDEX(@search_expression, @expression_to_be_searched, Number)) x -- (2) subIdx is found in the rest of the substring 
        WHERE 
            Number BETWEEN 1 AND LEN(@expression_to_be_searched) -- (1) run for each substring once
            AND SubIdx != 0  -- (3) we care only about the indexes we've found, 0 stands for "not found"
    )
    
    SELECT CHARINDEX('C', 'BACBABCBABBCBACBBABC')
    SELECT * FROM dbo.CHARINDICES('C', 'BACBABCBABBCBACBBABC')
    

    Apenas como referência - você pode derivar outros comportamentos disso, como expandir em PATINDEX():

    CREATE FUNCTION dbo.PATINDICES (
        @search_expression NVARCHAR(4000) = '%[cS]%',
        @expression_to_be_searched NVARCHAR(MAX) = 'W3Schools.com'
    ) RETURNS TABLE AS RETURN (
        WITH tally AS (
            SELECT num = ROW_NUMBER() OVER (ORDER BY [object_id])
            FROM sys.all_objects)
        SELECT DISTINCT n = subIdx + num - 1
        FROM 
            tally 
            CROSS APPLY (SELECT numRev = LEN(@expression_to_be_searched) - num + 1) x
            CROSS APPLY (SELECT subExp = RIGHT(@expression_to_be_searched, numRev)) y
            CROSS APPLY (SELECT subIdx = PATINDEX(@search_expression, subExp)) z
        WHERE 
            num BETWEEN 1 AND LEN(@expression_to_be_searched)
            AND SubIdx != 0
    )
    
    SELECT PATINDEX('%[cS]%', 'W3Schools.com')
    SELECT * FROM dbo.PATINDICES('%[cS]%', 'W3Schools.com')
    
    • 0
  7. Raghu
    2019-12-17T21:09:31+08:002019-12-17T21:09:31+08:00
    Declare @search varchar(5)
        sET @search='a'
        Declare @name varchar(40)
        Set @name='AmitabhBachan'
        Declare @init int
        Set @init=1
        Declare @hold int
        Declare @table table (POSITION Int)
        While( @init<= LEn(@name))
        Begin
       Set @hold=(Select CHARINDEX(@search,@name,@init))
       If (@hold!=0)
       BEgin 
       --Print @hold
       Insert into @table
       Select @hold
       Set @init=@hold+1
       End 
       Else
       If (@hold=0)
       BEgin
       Break
       End
      End
      Select * from @table
    
    • 0

relate perguntas

  • 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

  • Downgrade do SQL Server 2008 para 2005

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • 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

    Conceder acesso a todas as tabelas para um usuário

    • 5 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
    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
    pedrosanta Listar os privilégios do banco de dados usando o psql 2011-08-04 11:01:21 +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