Comment diviser une chaîne pour pouvoir accéder à l'élément x ?

À l'aide de SQL Server, comment fractionner une chaîne pour pouvoir accéder à l'élément x ?

Prenez une chaîne 'Hello John Smith'. Comment puis-je diviser la chaîne par espace et accéder à l'élément à l'index 1 qui doit renvoyer 'John' ?

请先 登录 后评论

5 réponses

Dillie-O

Eh bien, le mien n'est pas si simple, mais voici le code que j'utilise pour diviser une variable d'entrée délimitée par des virgules en valeurs individuelles et la mettre dans une variable de table. Je suis sûr que vous pouvez modifier légèrement cela pour diviser en fonction d'un espace, puis effectuer une requête SELECT de base sur cette variable de table pour obtenir vos résultats.

-- Create temporary table to parse the list of accounting cycles.
DECLARE @tblAccountingCycles table
(
    AccountingCycle varchar(10)
)

DECLARE @vchAccountingCycle varchar(10)
DECLARE @intPosition int

SET @vchAccountingCycleIDs = LTRIM(RTRIM(@vchAccountingCycleIDs)) + ','
SET @intPosition = CHARINDEX(',', @vchAccountingCycleIDs, 1)

IF REPLACE(@vchAccountingCycleIDs, ',', '') <> ''
BEGIN
    WHILE @intPosition > 0
    BEGIN
        SET @vchAccountingCycle = LTRIM(RTRIM(LEFT(@vchAccountingCycleIDs, @intPosition - 1)))
        IF @vchAccountingCycle <> ''
        BEGIN
            INSERT INTO @tblAccountingCycles (AccountingCycle) VALUES (@vchAccountingCycle)
        END
        SET @vchAccountingCycleIDs = RIGHT(@vchAccountingCycleIDs, LEN(@vchAccountingCycleIDs) - @intPosition)
        SET @intPosition = CHARINDEX(',', @vchAccountingCycleIDs, 1)
    END
END

Le concept est à peu près le même. Une autre alternative consiste à tirer parti de la compatibilité .NET dans SQL Server 2005 lui-même. Vous pouvez essentiellement écrire vous-même une méthode simple dans .NET qui diviserait la chaîne, puis l'exposerait en tant que procédure/fonction stockée.

请先 登录 后评论
Brendan

Voici une UDF qui le fera. Il renverra un tableau des valeurs délimitées, n'a pas essayé tous les scénarios mais votre exemple fonctionne bien.


CREATE FUNCTION SplitString 
(
    -- Add the parameters for the function here
    @myString varchar(500),
    @deliminator varchar(10)
)
RETURNS 
@ReturnTable TABLE 
(
    -- Add the column definitions for the TABLE variable here
    [id] [int] IDENTITY(1,1) NOT NULL,
    [part] [varchar](50) NULL
)
AS
BEGIN
        Declare @iSpaces int
        Declare @part varchar(50)

        --initialize spaces
        Select @iSpaces = charindex(@deliminator,@myString,0)
        While @iSpaces > 0

        Begin
            Select @part = substring(@myString,0,charindex(@deliminator,@myString,0))

            Insert Into @ReturnTable(part)
            Select @part

    Select @myString = substring(@mystring,charindex(@deliminator,@myString,0)+ len(@deliminator),len(@myString) - charindex(' ',@myString,0))


            Select @iSpaces = charindex(@deliminator,@myString,0)
        end

        If len(@myString) > 0
            Insert Into @ReturnTable
            Select @myString

    RETURN 
END
GO

Vous l'appelleriez ainsi :


Select * From SplitString('Hello John Smith',' ')

Edit : Solution mise à jour pour gérer les délimiteurs avec un len>1 comme dans :


select * From SplitString('Hello**John**Smith','**')
请先 登录 后评论
Nathan Bedford

Je ne crois pas que SQL Server ait une fonction de division intégrée, donc à part un UDF, la seule autre réponse que je connaisse est de détourner la fonction PARSENAME :

SELECT PARSENAME(REPLACE('Hello John Smith', ' ', '.'), 2) 

PARSENAME prend une chaîne et la divise sur le caractère point. Il prend un nombre comme deuxième argument, et ce nombre spécifie quel segment de la chaîne renvoyer (de l'arrière vers l'avant).

SELECT PARSENAME(REPLACE('Hello John Smith', ' ', '.'), 3)  --return Hello

Le problème évident est lorsque la chaîne contient déjà un point. Je pense toujours qu'utiliser une UDF est le meilleur moyen... d'autres suggestions ?

请先 登录 后评论
vzczc

Tout d'abord, créez une fonction (en utilisant CTE, l'expression de table commune élimine le besoin d'une table temporaire)

 create function dbo.SplitString 
    (
        @str nvarchar(4000), 
        @separator char(1)
    )
    returns table
    AS
    return (
        with tokens(p, a, b) AS (
            select 
                1, 
                1, 
                charindex(@separator, @str)
            union all
            select
                p + 1, 
                b + 1, 
                charindex(@separator, @str, b + 1)
            from tokens
            where b > 0
        )
        select
            p-1 zeroBasedOccurance,
            substring(
                @str, 
                a, 
                case when b > 0 then b-a ELSE 4000 end) 
            AS s
        from tokens
      )
    GO

Ensuite, utilisez-le comme n'importe quel tableau (ou modifiez-le pour l'adapter à votre proc stocké existant) comme ceci.

select s 
from dbo.SplitString('Hello John Smith', ' ')
where zeroBasedOccurance=1

Mettre à jour

La version précédente échouait pour une chaîne d'entrée de plus de 4 000 caractères. Cette version prend en charge la limitation :

create function dbo.SplitString 
(
    @str nvarchar(max), 
    @separator char(1)
)
returns table
AS
return (
with tokens(p, a, b) AS (
    select 
        cast(1 as bigint), 
        cast(1 as bigint), 
        charindex(@separator, @str)
    union all
    select
        p + 1, 
        b + 1, 
        charindex(@separator, @str, b + 1)
    from tokens
    where b > 0
)
select
    p-1 ItemIndex,
    substring(
        @str, 
        a, 
        case when b > 0 then b-a ELSE LEN(@str) end) 
    AS s
from tokens
);

GO

L'utilisation reste la même.

请先 登录 后评论
Nathan Skerl

Vous pouvez tirer parti d'une table numérique pour effectuer l'analyse de chaîne.

Créer une table de nombres physiques :

    create table dbo.Numbers (N int primary key);
    insert into dbo.Numbers
        select top 1000 row_number() over(order by number) from master..spt_values
    go

Créer une table de test avec 1 000 000 lignes

    create table #yak (i int identity(1,1) primary key, array varchar(50))

    insert into #yak(array)
        select 'a,b,c' from dbo.Numbers n cross join dbo.Numbers nn
    go

Créer la fonction

    create function [dbo].[ufn_ParseArray]
        (   @Input      nvarchar(4000), 
            @Delimiter  char(1) = ',',
            @BaseIdent  int
        )
    returns table as
    return  
        (   select  row_number() over (order by n asc) + (@BaseIdent - 1) [i],
                    substring(@Input, n, charindex(@Delimiter, @Input + @Delimiter, n) - n) s
            from    dbo.Numbers
            where   n <= convert(int, len(@Input)) and
                    substring(@Delimiter + @Input, n, 1) = @Delimiter
        )
    go

Utilisation (produit 3 millions de lignes en 40 secondes sur mon ordinateur portable)

    select * 
    from #yak 
    cross apply dbo.ufn_ParseArray(array, ',', 1)

nettoyage

    drop table dbo.Numbers;
    drop function  [dbo].[ufn_ParseArray]

Les performances ici ne sont pas étonnantes, mais appeler une fonction sur une table d'un million de lignes n'est pas la meilleure idée. Si vous effectuez une chaîne divisée sur plusieurs lignes, j'éviterais la fonction.

请先 登录 后评论