Dinamik olarak oluşturulan tablo verilerini döndürmek için Saklı Yordam


10

Hızlı bir hikaye, bir anket sistemi olan bir dış satıcı ile çalışıyoruz. Sistem yeni bir anket oluşturduğunuzda ve sistem yeni bir tablo oluşturduğunuzda, yani:

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)

Tablolar SurveyIdadın sonunda ( Library_) ile, Soru sütunları da QuestionIdsonunda ( Q_) ile oluşturulur. Açıklığa kavuşturmak için, sorular ayrı bir tabloda saklanır, böylece soru kimlikleri sıralı olsa da, her anket için 1'den başlamazlar. Soru sütunları, tabloda kendilerine atanan kimliği temel alacaktır.

Başka bir sisteme gönderilecek tüm anket tablolarından verileri çıkarmamız gerektiği ve sorunun devreye girdiği yer dışında sorgulamak için yeterince basit görünüyor. Ön taraf tarafından yeni bir anket eklendiğinde tablolar otomatik olarak oluşturulduğundan diğer sistem bu tür yapıları işleyemez. Verilerin tüketebilmeleri için tutarlı olmaları gerekir.

Bu yüzden tüm Anket tablolarından veri ayıklayıp aşağıdaki biçimde yerleştirecek saklı bir yordam yazmakla görevlendirildim:

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

Tüm tablolara ilişkin verilerin aynı formatta olması, kaç anket tablosu ve sorusu olursa olsun herkes tarafından tüketilebilir.

Çalışıyor gibi görünen bir saklı yordam yazdım ama bir şey eksik olup olmadığımı veya bu tür bir durumla başa çıkmanın daha iyi bir yolu olup olmadığını merak ediyorum.

Kodum:

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)

Bazı örnek veriler ve kod ile bir SQL Fiddle oluşturduk .

Bu tür bir sorgu yazmanın farklı bir yolu var mı? Bununla ilgili belirgin sorunlar var mı?

Ne yazık ki, bununla ilgili birçok bilinmeyen var ... kaç masaya sahip olacağız ve anket başına kaç soru var. Her biri 2-5 sorudan oluşan 25-50 anketimiz olacağını söyleyebilirim.


1
Sormaya korkuyorum, ama "Kaç masa?"
RBarryYoung

@RBarryYoung Bu noktada bilinmemektedir çünkü kaç anketin oluşturulduğuna bağlı olacaktır. Bu sorunun bir parçası.
Taryn

O zaman bize bir menzil verin. Çok önemli.
RBarryYoung

25-50 tablolar her yerde söyleyebilirim.
Taryn

Yanıtlar:


2

Sohbetteki kişilerin yorumlarına dayanarak INSERT INTO, sonunda yürütmek için bir uzun SQL ifadesi oluşturmak yerine betiğimi geçici bir tabloya değiştirmeye karar verdim . Sonuçta saklı yordam aşağıdakileri içerir:

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

Son komut dosyasıyla SQL Fiddle'ı görün

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.