IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

MS SQL Server Discussion :

Fonction split simple


Sujet :

MS SQL Server

  1. #1
    Membre confirmé
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Octobre 2008
    Messages
    698
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Boutique - Magasin

    Informations forums :
    Inscription : Octobre 2008
    Messages : 698
    Points : 586
    Points
    586
    Par défaut Fonction split simple
    Bonjour,

    Voilà j'écrivais fièrement une fonction d'extraction de chaines en fonction d'un séparateur et d'un indice... Demandez pas pourquoi j'ai trop honte.

    Le fait c'est que j'ai trouvé tellement d'aide sur ce forum que je me suis dit pourquoi pas partager ...

    Alors voilà, si un jour ça sert à qqun.

    Elle est facilement modifiable pour avoir toute la liste.


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    -- =============================================
    -- Author:		Juan Jimenez
    -- Create date: 08.08.2014
    -- Description:	Extraction de chaine a l'indice spécifié avec le séparateur spécifié
    -- =============================================
    CREATE FUNCTION fExtraitChaine 
    (
    	@Texte varchar(max),
    	@Indice int,
    	@Separateur varchar(max)
    )
    RETURNS varchar(max)
    AS
    BEGIN
    	Declare @Resultat varchar(max);
    	With Split AS
    				(
    					Select CASE CHARINDEX(@Separateur,@Texte,1) WHEN 0 THEN '' ELSE Substring(@Texte,1,CHARINDEX(@Separateur,@Texte,1)-1) END  Element, Substring(@Texte,CHARINDEX(@Separateur,@Texte,1)+len(@Separateur),len(@Texte)) Rest,CHARINDEX(@Separateur,@Texte,1) Pos,1 ID
    					UNION ALL
    					SELECT CASE CHARINDEX(@Separateur,R.Rest,1) WHEN 0 THEN R.Rest ELSE Substring(R.Rest,1,CHARINDEX(@Separateur,R.Rest,1)-1) END Element, Substring(R.Rest,CHARINDEX(@Separateur,R.Rest,1)+len(@Separateur),len(R.Rest)) Rest,CHARINDEX(@Separateur,R.Rest,1) Pos,R.Id+1 ID
    					FROM (Values(1)) Tbl(Bidon)
    					INNER JOIN Split R on 1 = 1 AND R.Element<>R.Rest
    					WHERE R.Id+1<=@Indice
    				)
    	Select @Resultat = Element
    	from Split
    	Where CHARINDEX(@Separateur,@Texte,1)<> 0 and Id=@Indice
     
    	RETURN @Resultat
    END
    GO

  2. #2
    Modérateur

    Profil pro
    dba
    Inscrit en
    Janvier 2010
    Messages
    5 643
    Détails du profil
    Informations personnelles :
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : dba

    Informations forums :
    Inscription : Janvier 2010
    Messages : 5 643
    Points : 13 092
    Points
    13 092
    Par défaut
    Bonjour et merci pour le partage.

    Les manipulations de chaine n'étant le fer de lance des SGBD en général, il serait préférable de les réduire au minimum. Ainsi, dans votre récursive, vous pourriez vous contenter de calculer les bornes (l'emplacement de chaque séparateur), et de ne faire qu'un SUBSTRING final :


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
     
    CREATE FUNCTION fExtraitChaine
    (
    	@Texte varchar(max),
    	@Indice int,
    	@Separateur varchar(max)
    )
    RETURNS varchar(max)
    AS
    BEGIN
    	Declare @Resultat varchar(max);
     
    	;WITH Split AS
    				(
    					SELECT 1 as i, CAST(0 AS BIGINT) as sep 
    					UNION ALL
    					SELECT i + 1, CHARINDEX(@Separateur,@Texte,sep + 1)
    					FROM Split  
    					WHERE i<=@Indice
    					AND CHARINDEX(@Separateur,@Texte,sep + 1) > 0
    				)
    	SELECT @Resultat = SUBSTRING(@Texte, sep + 1, CHARINDEX(@Separateur,@Texte + @separateur,sep + 1) - (sep + 1))
    	from split
    	WHERE i = @Indice
     
    	RETURN @Resultat
    END
    Cependant, il sera peut être plus performant de passer par du xml plutôt qu'une récursive, surtout si la chaine est longue et contient beaucoup de séparateurs :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
     
    CREATE FUNCTION fExtraitChaine
    (
    	@Texte varchar(max),
    	@Indice int,
    	@Separateur varchar(max)
    )
    RETURNS varchar(max)
    AS
    BEGIN
    	DECLARE @x XML
    	DECLARE @Result VARCHAR(MAX)
    	SET @x =  CAST('<c>' + REPLACE(@Texte, @Separateur, '</c><c>') + '</c>' AS XML)
    	SET @Result =  @x.value('c[sql:variable("@Indice")][1]', 'VARCHAR(MAX)')
    	RETURN @Result
    END

    Enfin, dans votre cas particulier, pour ne récupérer qu'une seule occurrence à partir de son indice, le mieux sera sûrement un traitement en boucle sur la variable @Texte :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
     
    CREATE FUNCTION fExtraitChaine
    (
    	@Texte varchar(max),
    	@Indice int,
    	@Separateur varchar(max)
    )
    RETURNS varchar(max)
    AS
    BEGIN
            --si l'indice est plus grand que le nombre de séparateurs, on renvoie NULL
    	IF PATINDEX(REPLICATE('%' + @Separateur, @Indice - 1) + '%', @Texte) =  0 
    		RETURN NULL
     
    	DECLARE @n INT = 1
    	WHILE	@n < @Indice
    		SELECT @Texte = SUBSTRING(@Texte, CHARINDEX(@Separateur,@Texte)+LEN(@Separateur), 2000000000), @n += 1
     
    	SET @texte = LEFT(@Texte,CHARINDEX(@Separateur,@Texte + @separateur) - 1)
     
    	RETURN @Texte
    END

  3. #3
    Membre expérimenté

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Septembre 2003
    Messages
    733
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2003
    Messages : 733
    Points : 1 668
    Points
    1 668
    Billets dans le blog
    8
    Par défaut
    Oui, Merci Donpi cela peut toujours servir !

    Je me suis inspiré des fonctions split de Elsulet (Nicolas Souquet) sur ce blog :
    http://blog.developpez.com/elsuket/p...et_inversement

    pour créer une variante, plus "light" de ta fonction fExtraitChaine_2

    Code SQL : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    CREATE FUNCTION dbo.fExtraitChaine_2 
    (
    	@Texte varchar(8000),
    	@Sep char(1), 
    	@Indice int
     
    )
    RETURNS varchar(8000)
    AS
    BEGIN
    	Declare @Resultat varchar(8000);
    	WITH Split AS
    				( SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL) ) AS Indice, 
                       N.v.value('.', ' varchar(32)') AS value
                      FROM  (VALUES (CAST('<v>' + REPLACE(RIGHT(LEFT(@Texte, LEN(@Texte) - 1), LEN(@Texte) - 2),   
                      @Sep, '</v><v>') + '</v>' AS xml))) AS X(v)
                      CROSS APPLY	X.v.nodes('/v') AS N(v)
    				)
    	SELECT @Resultat = value
    	FROM Split
    	WHERE Indice=@Indice; 
     
    	RETURN @Resultat
    END
    GO

    A+
    "Une idée mal écrite est une idée fausse !"
    http://hamid-mira.blogspot.com

  4. #4
    Modérateur

    Homme Profil pro
    Administrateur de base de données
    Inscrit en
    Janvier 2005
    Messages
    5 826
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Administrateur de base de données
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Janvier 2005
    Messages : 5 826
    Points : 12 371
    Points
    12 371
    Par défaut
    Bonjour,

    Les fonctions scalaires et de table à instructions multiples sont contre-performantes, puisqu'elles sont exécutées 1 fois par ligne de résultat.
    On peut donc faire évoluer la fonction comme suit :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    CREATE FUNCTION dbo.fExtraitChaine_3
    	(
    		@chaine varchar(max)
    		, @separateur varchar(2)
    		, @indice int = NULL
    	)
    RETURNS TABLE
    AS RETURN
    (
    	SELECT	D.indice
    		, D.valeur_scalaire
    	FROM	(
    			SELECT		ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS indice
    					, Y.vs.value('.', 'varchar(max)') AS valeur_scalaire
    			FROM		(
    						VALUES
    						(
    							CAST('<v>'+ REPLACE(@chaine, @separateur ,'</v><v>') + '</v>' AS  xml)
    						)
    					) AS X(v)
    			CROSS APPLY	X.v.nodes('/v') AS Y(vs)
    		) AS D
    	WHERE	D.indice = @indice
    	OR	@indice IS NULL
    )
    Et quelques exemples d'appel :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    SELECT	*
    FROM	dbo.fExtraitChaine_3('toto,titi,tutu,tata', ',', NULL)
    Ou encore avec le petit jeu de données suivant :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    CREATE TABLE chaine
    (
    	id tinyint NOT NULL IDENTITY (0,1)
    		CONSTRAINT PK_chaine PRIMARY KEY
    	, uneChaine varchar(128) NOT NULL
    )
    GO
     
    INSERT INTO dbo.chaine (uneChaine) VALUES ('jiji,jojo,juju,jaja')
    INSERT INTO dbo.chaine (uneChaine) VALUES ('toto,titi,tutu,tata')
    GO
     
    SELECT		C.id
    		, FEC.valeur_scalaire
    FROM		dbo.chaine AS C
    CROSS APPLY	dbo.fExtraitChaine_3(C.uneChaine, ',', NULL) AS FEC

  5. #5
    Expert éminent
    Avatar de StringBuilder
    Homme Profil pro
    Chef de projets
    Inscrit en
    Février 2010
    Messages
    4 153
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Chef de projets
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2010
    Messages : 4 153
    Points : 7 403
    Points
    7 403
    Billets dans le blog
    1
    Par défaut
    Juste une petite question.

    Avec la fonction SPLIT, n'est-on pas dans un cas où utiliser une fonction CLR est un vrai plus ?

    D'un point de vue performances, ça va être dur de faire mieux qu'un String.Split() en .NET j'imagine.
    Et d'un point de vue syntaxe aussi... (donc maintenabilité, tout ça, etc.)

    Votre avis ?
    On ne jouit bien que de ce qu’on partage.

  6. #6
    Membre confirmé
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Octobre 2008
    Messages
    698
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Boutique - Magasin

    Informations forums :
    Inscription : Octobre 2008
    Messages : 698
    Points : 586
    Points
    586
    Par défaut
    Citation Envoyé par StringBuilder Voir le message
    Juste une petite question.

    Avec la fonction SPLIT, n'est-on pas dans un cas où utiliser une fonction CLR est un vrai plus ?

    D'un point de vue performances, ça va être dur de faire mieux qu'un String.Split() en .NET j'imagine.
    Et d'un point de vue syntaxe aussi... (donc maintenabilité, tout ça, etc.)

    Votre avis ?
    Bas mon avis c'est que je vais faire un benchmarck (je sais pas écrire benchmarck) pour être sur

  7. #7
    Membre actif
    Étudiant
    Inscrit en
    Avril 2008
    Messages
    311
    Détails du profil
    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Avril 2008
    Messages : 311
    Points : 257
    Points
    257
    Par défaut
    Citation Envoyé par StringBuilder Voir le message
    Juste une petite question.

    Avec la fonction SPLIT, n'est-on pas dans un cas où utiliser une fonction CLR est un vrai plus ?

    D'un point de vue performances, ça va être dur de faire mieux qu'un String.Split() en .NET j'imagine.
    Et d'un point de vue syntaxe aussi... (donc maintenabilité, tout ça, etc.)

    Votre avis ?
    Bonjour, certainement mais parfois on a besoin d'un paramètre (chaine) pour traiter des listes variables d'éléments dans une procédure...

  8. #8
    Expert éminent
    Avatar de StringBuilder
    Homme Profil pro
    Chef de projets
    Inscrit en
    Février 2010
    Messages
    4 153
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Chef de projets
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2010
    Messages : 4 153
    Points : 7 403
    Points
    7 403
    Billets dans le blog
    1
    Par défaut
    C'est pour ça que je parle de fonctions CLR, donc des procédures stockées et fonctions stockées dans SQL Server, écrites avec .NET

    Donc pour les traitements de calcul intensif, ou manipulations de chaînes, là où T-SQL pêche énormément, on peut tirer partie de .NET directement dans SQL Server.

    http://msdn.microsoft.com/fr-fr/library/ms131103.aspx
    On ne jouit bien que de ce qu’on partage.

  9. #9
    Expert éminent
    Avatar de StringBuilder
    Homme Profil pro
    Chef de projets
    Inscrit en
    Février 2010
    Messages
    4 153
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Chef de projets
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2010
    Messages : 4 153
    Points : 7 403
    Points
    7 403
    Billets dans le blog
    1
    Par défaut
    Pour illustrer ma réponse, un exemple concret avec code complet :
    http://www.developpez.net/forums/d14...r/#post7927380
    On ne jouit bien que de ce qu’on partage.

  10. #10
    Membre actif
    Étudiant
    Inscrit en
    Avril 2008
    Messages
    311
    Détails du profil
    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Avril 2008
    Messages : 311
    Points : 257
    Points
    257
    Par défaut
    Bonjour, je ne connaissais pas cette fonction de SQL Server, d'où ma réponse HS.
    Merci pour ton poste explicatif très intéressant (que je commente de ce pas).
    A+

Discussions similaires

  1. caractéres spéciaux, fonction split, replace
    Par clochardevobsy dans le forum VB 6 et antérieur
    Réponses: 4
    Dernier message: 22/10/2005, 23h40
  2. Réponses: 5
    Dernier message: 13/10/2005, 12h46
  3. [String] fonction split
    Par tck-lt dans le forum Langage
    Réponses: 2
    Dernier message: 19/07/2005, 17h45
  4. procedure stockée et fonction "split"
    Par wehtam dans le forum MS SQL Server
    Réponses: 2
    Dernier message: 18/07/2005, 16h21
  5. [Fortran 90] Fonction Split()
    Par gchazelle dans le forum Fortran
    Réponses: 2
    Dernier message: 02/03/2005, 10h16

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo