動的に作成されたテーブルデータを返すストアドプロシージャ


10

簡単に言えば、調査システムを備えた外部ベンダーと協力しています。システムは、新しい調査を作成し、システムが新しいテーブルを作成するときに、必ずしも次のように設計されているとは限りません。

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)

テーブルはで生成されたSurveyId名前の末尾に(Library_)と質問の列がで生成されQuestionId、それの終わり(でQ_)。 明確にするために、質問は別のテーブルに格納されているため、質問IDは連続していますが、各調査で1から開始されません。質問の列は、テーブルで割り当てられたIDに基づいています。

別のシステムに送信するためにすべての調査テーブルからデータを抽出する必要があることを除いて、クエリを実行するのに十分簡単なようです。これは問題が発生する場所です。新しい調査がフロントによって追加されると、テーブルが自動的に作成されるため、アプリケーションを終了すると、他のシステムはこのタイプの構造を処理できません。彼らは消費するためにデータが一貫している必要があります。

そのため、すべてのSurveyテーブルからデータを抽出して次の形式で配置するストアドプロシージャを作成する必要がありました。

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

すべてのテーブルのデータを同じ形式にすることで、調査テーブルや質問がいくつあっても、誰でもデータを利用できます。

動作しているように見えるストアドプロシージャを作成しましたが、何か不足しているのか、またはこの種の状況を処理するためのより良い方法があるのか​​と思っています。

私のコード:

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)

サンプルデータとコードを含むSQL Fiddleを作成しました。

このタイプのクエリを記述する別の方法はありますか?目立つ問題はありますか?

残念ながら、これには多くの未知数があります...テーブルの数と調査ごとの質問の数。 25から50のアンケートがあり、それぞれに2から5の質問があります。


1
申し訳ありませんが、「テーブルはいくつですか?」
RBarryYoung

@RBarryYoung現時点では、作成された調査の数に依存するため、不明です。それは問題の一部です。
タリン

次に範囲を指定してください。それは非常に重要です。
RBarryYoung

私は25-50テーブルからどこでも言うでしょう。
タリン

回答:


2

チャットでの人々のコメントに基づいてINSERT INTO、最後に実行する長いSQLステートメントを1つ作成する代わりに、スクリプトを一時テーブルに少し変更することにしました。結局、私のストアドプロシージャには次のものが含まれています。

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

最終的なスクリプトを含むSQL Fiddleを参照してください

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.