Stored procedure per restituire dati di tabella creati dinamicamente


10

Breve storia, stiamo lavorando con un fornitore esterno che ha un sistema di sondaggi. Il sistema non è necessariamente progettato in modo ottimale quando si crea un nuovo sondaggio e il sistema crea una nuova tabella, ovvero:

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)

Le tabelle vengono generate con il SurveyIdalla fine del nome ( Library_) e le colonne Domanda vengono generate con il QuestionIdalla fine del nome ( Q_). Per chiarire, le domande sono memorizzate in una tabella separata, quindi mentre gli ID delle domande sono sequenziali, non iniziano da 1 per ogni sondaggio. Le colonne delle domande si baseranno sull'ID assegnato loro nella tabella.

Sembra abbastanza semplice da interrogare, tranne per il fatto che è necessario estrarre i dati da tutte le tabelle dei sondaggi per essere inviati a un altro sistema ed è qui che si presenta il problema. Poiché le tabelle vengono create automaticamente quando un nuovo sondaggio viene aggiunto dal front- fine dell'applicazione, l'altro sistema non è in grado di gestire questo tipo di struttura. Hanno bisogno che i dati siano coerenti perché possano consumarli.

Quindi mi è stato assegnato il compito di scrivere una procedura memorizzata che estrarrà i dati da tutte le tabelle dei sondaggi e li posizionerà nel seguente formato:

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

Avendo i dati per tutte le tabelle nello stesso formato, possono essere consumati da chiunque, indipendentemente dal numero di tabelle e domande dell'indagine.

Ho scritto una procedura memorizzata che sembra funzionare ma mi chiedo se mi manca qualcosa o se esiste un modo migliore per gestire questo tipo di situazione.

Il mio codice:

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)

Ho creato un violino SQL con alcuni dati di esempio e il codice.

Esiste un modo diverso di scrivere questo tipo di query? Ci sono problemi evidenti con esso?

Sfortunatamente, ci sono molte incognite con questo ... quanti tavoli avremo e quante domande per sondaggio. Direi che avremo tra 25-50 sondaggi, con 2-5 domande ciascuno.


1
Ho paura di chiedere, ma "Quanti tavoli?"
RBarryYoung,

@RBarryYoung A questo punto, ciò è sconosciuto perché dipenderà dal numero di sondaggi creati. Questo è parte del problema.
Taryn

Dacci una gamma allora. È molto importante.
RBarryYoung,

Direi ovunque da 25-50 tavoli.
Taryn

Risposte:


2

Sulla base dei commenti delle persone in chat, ho deciso di modificare leggermente il mio script in INSERT INTOuna tabella temporanea anziché creare una lunga istruzione SQL da eseguire alla fine. Quindi alla fine la mia procedura memorizzata contiene quanto segue:

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

Vedi SQL Fiddle con lo script finale

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.