Conseils et formations : vos deux atouts pour réussir !
Accueil > Développement > Articles
Une petite récursivité en SQL

Position du problème :


Devant développer des procédures génériques pour un client, je me suis posé la question suivante :
comment obtenir une liste CSV des colonnes constituant la clé primaire d'une table?

Et, au delà de ce problème particulier, comment obtenir une chaine à partir d'un resultset.

En effet, les vues sur le catalogue nous donne des ensembles résultats, ainsi :


select * from information_schema.table_CONSTRAintS


donne :


et, dans le même ordre d'idées, il est facile d'obtenir un data set des colonnes de clé primaire (mon besoin de départ) :

    select *

    from information_schema.table_CONSTRAintS pk

    inner join information_schema.CONSTRAint_COLUMN_USAGE col

    on pk.table_CATALOG = col.table_CATALOG and

    pk.table_SCHEMA = col.table_SCHEMA and

    pk.table_NAME = col.table_NAME

    where CONSTRAint_TYPE = 'primary key'

 

 

 

Bien, mais tout cela nous donne des datasets, pas un CSV ?


C'est là où intervient le raisonnement récursif :

. si j'avais cette liste à l''étape n-1 (c'est à dire les n-1 colonnes constituant la clé primaire), alors la suite serait :

ListeAn-1 CONCATENATION ',' CONCATENATION Colonnen

sachant que la liste au départ est vide....

c'est le but de la fonction écrité ci dessous :

    create function ColonnesPk (@NomSchema sysname,@Nomtable sysname)

    returnS nvarchar(max)

    AS

    begin

 

        declare @csColonnesPk varchar(max)

        declare @csSeparateur varchar(10)

 

 

        -- Requête récursive sur les colonnes de la clé primaire

        set @csColonnesPk = ''

        set @csSeparateur = ''

        select top 20 @csColonnesPk = @csColonnesPk + @csSeparateur + COLUMN_NAME ,@csSeparateur = ','

        from information_schema.table_CONSTRAintS pk

        inner join information_schema.CONSTRAint_COLUMN_USAGE col

        on pk.table_CATALOG = col.table_CATALOG and

        pk.table_SCHEMA = col.table_SCHEMA and

        pk.table_NAME = col.table_NAME

        where pk.table_CATALOG = db_name() and

        pk.table_SCHEMA = @NomSchema and

        pk.table_NAME = @Nomtable

 

 

        return @csColonnesPk

    end

    go

 

Quelques explications :

le séparateur sert à séparer les colonnes de la liste, il est donc à '' au départ, puis passe à ',' par la suite.
En effet on a une construction du style

{Vide}{Vide}{Clé0}{,}{Clé1}{,}
etc...
l'astuce est basée sur le fait que le dataset se déroule ligne à ligne, et donc nous permet de contaténer le tout dans @csColonnesPk.

Testons le tout :

    print dbo.ColonnesPk ('dbo','TRD')

    go


CHRONO,NAME,PROJECT
Magique, nest-ce pas ?!

Le tout est bien sûr généralisable à tout problème de ce type, par exemple :

    --

    -- Toutes les colonnes d'une table (sauf celle qui se nomme TRANSMIS)

    --

    create function Colonnes (@NomSchema sysname,@Nomtable sysname,@Prefixe nvarchar(max))

    returnS nvarchar(max)

    AS

    begin

 

        declare @csColonnes varchar(max)

        declare @csSeparateur varchar(10)

 

 

        -- Requête récursive sur les colonnes

        set @csColonnes = ''

        set @csSeparateur = ''

 

        select top 200 @csColonnes = @csColonnes + @Prefixe + @csSeparateur + COLUMN_NAME , @csSeparateur = ','

        from information_schema.COLUMNS

        where table_CATALOG = db_name() and

        table_SCHEMA = @NomSchema and

        table_NAME = @Nomtable and

        COLUMN_NAME <> 'TRANSMIS'

 

        return @csColonnes

    end

    go


qui donne :

PROJECT,CHRONO,RECTS,STATION,UNITNAME,VARTYPE,NAME,TITLE,.....

Conclusion :


Encore un moyen d'éviter les curseurs qui, décidément, ne sont pas un des points forts de SQLServer ... ceux qui lisent mes articles, savent que, heureusement je suis très satisfait de beaucoup d'autres aspects.

 ‭(Masqué)‬ WebPart1 Web Part

/Developpement/Une petite récursivité en SQL/