web-dev-qa-db-fra.com

Procédure stockée pour renvoyer des données de table créées dynamiquement

En bref, nous travaillons avec un fournisseur extérieur qui dispose d'un système d'enquête. Le système n'est pas nécessairement conçu le mieux en ce sens que lorsque vous créez une nouvelle enquête et que le système crée une nouvelle table, à savoir:

Tables
____
Library_1 -- table for Survey 1
SurveyId int
InstanceId int
Q_1 varchar(50)

Library_2 -- table for Survey 2
SurveyId int
InstanceId int
Q_2 int
Q_3 int
Q_4 varchar(255)

Les tables sont générées avec le SurveyId à la fin du nom (Library_) et les colonnes Question sont générées avec le QuestionId à la fin (Q_). Pour clarifier, les questions sont stockées dans un tableau séparé, alors que les identifiants des questions sont séquentiels, ils ne commencent pas à 1 pour chaque enquête. Les colonnes de questions seront basées sur l'ID qui leur est attribué dans le tableau.

Semble assez simple pour interroger, sauf que nous devons extraire les données de toutes les tables d'enquête pour les envoyer à un autre système et c'est là que le problème se présente. Puisque les tables sont créées automatiquement lorsqu'une nouvelle enquête est ajoutée par le front- application finale, l'autre système ne peut pas gérer ce type de structure. Ils ont besoin que les données soient cohérentes pour pouvoir les consommer.

J'ai donc été chargé d'écrire une procédure stockée qui extraira les données de toutes les tables d'enquête et les placera au format suivant:

SurveyId    InstanceId    QNumber    Response
________    __________    _______    ________
1           1             1          great
1           2             1          the best
2           9             2          10
3           50            50         test

En ayant les données de tous les tableaux dans le même format, elles peuvent être utilisées par n'importe qui, quel que soit le nombre de tableaux d'enquête et de questions.

J'ai écrit une procédure stockée qui semble fonctionner mais je me demande si je manque quelque chose ou s'il existe une meilleure façon de gérer ce type de situation.

Mon code:

declare @sql varchar(max) = ''
declare @RowCount int = 1
declare @TotalRecords int = (SELECT COUNT(*) FROM SurveyData)

Declare @TableName varchar(50) = ''
Declare @ColumnName varchar(50) = ''

WHILE @RowCount <= @TotalRecords
    BEGIN

        SELECT @TableName = tableName, @ColumnName = columnName
        FROM SurveyData
        WHERE @RowCount = rownum


        SET @sql = @sql + 
            ' SELECT s.SurveyId
                , s.InstanceId
                , CASE WHEN columnName = ''' +  @ColumnName + ''' THEN REPLACE(columnName, ''Q_'', '''') ELSE '''' END as QuestionNumber
                , Cast(s.' + @ColumnName + ' as varchar(1000)) as ''Response''
            FROM SurveyData t 
            INNER JOIN ' + @TableName + ' s' +
                ' ON REPLACE(t.tableName, ''Library_'', '''') = s.SurveyID ' +
            ' WHERE t.columnName = ''' + @ColumnName + ''''

        IF @RowCount != @TotalRecords
            BEGIN
                set @sql = @sql + ' UNION ALL'
            END

        SET @RowCount = @RowCount + 1       
    END


exec(@sql)

J'ai créé un SQL Fiddle avec quelques exemples de données et le code.

Existe-t-il une manière différente d'écrire ce type de requête? Y a-t-il des problèmes notables avec cela?

Malheureusement, il y a beaucoup d'inconnues avec cela ... combien de tableaux nous aurons et combien de questions par enquête. Je dirais que nous aurons entre 25 et 50 sondages, avec 2 à 5 questions chacun.

10
Taryn

Sur la base des commentaires des personnes dans le chat, j'ai décidé de changer légèrement mon script en INSERT INTO une table temporaire au lieu de créer une longue instruction SQL à exécuter à la fin. Donc, à la fin, ma procédure stockée contient les éléments suivants:

create table #SurveyData
(
    tableName varchar(50),
    columnName varchar(50),
    columnId int,
    rownum int
)

create table #results
(
    SurveyId int,
    InstanceId int,
    QuestionNumber int,
    Response varchar(1000)
)

-- insert the survey table structures for use
insert into #SurveyData (tableName, columnName, columnId, rownum)
select tables1.name, cols1.name, column_id, ROW_NUMBER() over(order by tables1.name, column_id)
from sys.all_columns cols1
inner join 
(
    SELECT *
    FROM sys.all_objects
    WHERE type = 'U' 
    AND upper(name) like 'LIBRARY%' 
) Tables1
    ON cols1.object_id = tables1.object_id
WHERE cols1.name Like 'Q_%'
ORDER BY tables1.name, column_id;


declare @sql varchar(max) = '';
declare @RowCount int = 1;
declare @TotalRecords int = (SELECT COUNT(*) FROM #SurveyData);

Declare @TableName varchar(50) = '';
Declare @ColumnName varchar(50) = '';

WHILE @RowCount <= @TotalRecords
    BEGIN

        SELECT @TableName = tableName, @ColumnName = columnName
        FROM #SurveyData
        WHERE @RowCount = rownum

        SET @sql = 'INSERT INTO #results ' +
                    ' SELECT s.SurveyId
                        , s.InstanceId
                        , CASE WHEN columnName = ''' +  @ColumnName + ''' THEN REPLACE(columnName, ''Q_'', '''') ELSE '''' END as QuestionNumber
                        , Cast(s.' + @ColumnName + ' as varchar(1000)) as ''Response''
                    FROM #SurveyData t 
                    INNER JOIN ' + @TableName + ' s' +
                    ' ON REPLACE(t.tableName, ''Library_'', '''') = s.SurveyID ' +
                    ' WHERE t.columnName = ''' + @ColumnName + ''''

        exec(@sql)

        SET @RowCount = @RowCount + 1       
    END

    SELECT SurveyId, InstanceId, QuestionNumber, Response
    FROM #results

drop table #SurveyData
drop table #results

Voir SQL Fiddle avec le script final

2
Taryn