Stored Procedure untuk mengembalikan data tabel yang dibuat secara dinamis

10

Kisah cepat kembali, kami bekerja dengan vendor luar yang memiliki sistem survei. Sistem tidak perlu dirancang yang terbaik ketika Anda membuat survei baru dan sistem membuat tabel baru, yaitu:

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)

Tabel dihasilkan dengan SurveyIddi akhir nama ( Library_) dan kolom Pertanyaan dibuat dengan QuestionIddi akhir nama ( Q_). Untuk memperjelas, pertanyaan disimpan dalam tabel terpisah sehingga sementara id pertanyaan berurutan mereka tidak mulai dari 1 untuk setiap survei. Kolom pertanyaan akan didasarkan pada id yang diberikan kepadanya dalam tabel.

Tampaknya cukup sederhana untuk di-query, kecuali kita perlu mengekstrak data dari semua tabel survei untuk dikirim ke sistem lain dan di sinilah masalahnya. Karena tabel dibuat secara otomatis ketika survei baru ditambahkan oleh front- aplikasi akhir, sistem lain tidak dapat menangani jenis struktur ini. Mereka membutuhkan data agar konsisten untuk dikonsumsi.

Jadi saya ditugaskan menulis prosedur tersimpan yang akan mengekstrak data dari semua tabel Survei dan menempatkannya dalam format berikut:

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

Dengan memiliki data untuk semua tabel dalam format yang sama, maka dapat dikonsumsi oleh siapa pun tidak peduli berapa banyak tabel survei dan pertanyaan.

Saya menulis prosedur tersimpan yang tampaknya berfungsi tetapi saya bertanya-tanya apakah saya kehilangan sesuatu atau apakah ada cara yang lebih baik untuk menangani situasi semacam ini.

Kode saya:

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)

Saya telah membuat SQL Fiddle dengan beberapa sampel data dan kode.

Apakah ada cara lain untuk jenis kueri ini yang harus ditulis? Apakah ada masalah nyata dengan itu?

Sayangnya, ada banyak yang tidak diketahui dengan ini ... berapa banyak tabel yang akan kita miliki dan berapa banyak pertanyaan per survei. Saya akan mengatakan bahwa kita akan memiliki antara 25-50 survei, dengan 2-5 pertanyaan masing-masing.

Taryn
sumber
1
Saya takut bertanya, tetapi, "Berapa meja?"
RBarryYoung
@RBarryYoung Pada titik ini, itu tidak diketahui karena akan tergantung pada berapa banyak survei yang dibuat. Itu adalah bagian dari masalah.
Taryn
Beri kami rentang itu. Itu sangat penting.
RBarryYoung
Saya akan mengatakan di mana saja dari 25-50 tabel.
Taryn

Jawaban:

2

Berdasarkan komentar dari orang-orang dalam obrolan, saya memutuskan untuk mengubah skrip saya sedikit ke INSERT INTOtabel temp daripada membuat satu pernyataan SQL panjang untuk dieksekusi di akhir. Jadi pada akhirnya prosedur tersimpan saya berisi yang berikut:

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

Lihat SQL Fiddle dengan skrip terakhir

Taryn
sumber