Gespeicherte Prozedur zum Zurückgeben dynamisch erstellter Tabellendaten

10

Kurze Hintergrundgeschichte: Wir arbeiten mit einem externen Anbieter zusammen, der über ein Umfragesystem verfügt. Das System ist nicht unbedingt das Beste, wenn Sie eine neue Umfrage erstellen und das System eine neue Tabelle erstellt, dh:

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)

Die Tische sind mit der erzeugte SurveyIdam Ende des Namens ( Library_) und die Frage Spalten werden mit der erzeugte QuestionIdam Ende davon ( Q_). Zur Verdeutlichung werden die Fragen in einer separaten Tabelle gespeichert, sodass die Fragen-IDs zwar sequentiell sind, jedoch nicht bei jeder Umfrage bei 1 beginnen. Die Fragenspalten basieren auf der ihnen in der Tabelle zugewiesenen ID.

Scheint einfach genug zum Abfragen zu sein, außer dass wir die Daten aus allen Umfragetabellen extrahieren müssen, um sie an ein anderes System zu senden, und hier tritt das Problem auf. Da die Tabellen automatisch erstellt werden, wenn eine neue Umfrage von der Front hinzugefügt wird. Ende der Anwendung kann das andere System diese Art von Struktur nicht verarbeiten. Sie benötigen konsistente Daten, damit sie konsumiert werden können.

Daher wurde ich beauftragt, eine gespeicherte Prozedur zu schreiben, die die Daten aus allen Umfragetabellen extrahiert und in das folgende Format bringt:

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

Wenn die Daten für alle Tabellen im gleichen Format vorliegen, können sie von jedem Benutzer verwendet werden, unabhängig davon, wie viele Umfragetabellen und Fragen vorhanden sind.

Ich habe eine gespeicherte Prozedur geschrieben, die anscheinend funktioniert, aber ich frage mich, ob mir etwas fehlt oder ob es einen besseren Weg gibt, mit dieser Art von Situation umzugehen.

Mein 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)

Ich habe eine SQL-Geige mit einigen Beispieldaten und dem Code erstellt.

Gibt es eine andere Art und Weise, wie diese Art von Abfrage geschrieben werden sollte? Gibt es irgendwelche auffälligen Probleme damit?

Leider gibt es viele Unbekannte damit ... wie viele Tabellen wir haben werden und wie viele Fragen pro Umfrage. Ich würde sagen, dass wir zwischen 25 und 50 Umfragen mit jeweils 2 bis 5 Fragen haben werden.

Taryn
quelle
1
Ich habe Angst zu fragen, aber: "Wie viele Tische?"
RBarryYoung
@RBarryYoung Derzeit ist dies nicht bekannt, da es davon abhängt, wie viele Umfragen erstellt werden. Das ist ein Teil des Problems.
Taryn
Geben Sie uns dann eine Reichweite. Es ist sehr wichtig.
RBarryYoung
Ich würde sagen, irgendwo zwischen 25 und 50 Tischen.
Taryn

Antworten:

2

Aufgrund von Kommentaren von Personen im Chat habe ich beschlossen, mein Skript geringfügig in INSERT INTOeine temporäre Tabelle zu ändern, anstatt eine lange SQL-Anweisung zu erstellen, die am Ende ausgeführt werden soll. Am Ende enthält meine gespeicherte Prozedur also Folgendes:

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

Siehe SQL Fiddle mit dem endgültigen Skript

Taryn
quelle