簡単に言えば、調査システムを備えた外部ベンダーと協力しています。システムは、新しい調査を作成し、システムが新しいテーブルを作成するときに、必ずしも次のように設計されているとは限りません。
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_
)とQuestion列は、末尾に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の質問があると思います
チャットの人々からのコメントに基づいて、スクリプトを少し変更して、最後に実行する長いSQLステートメントを1つ作成するのではなく、一時テーブルINSERT INTO
に変更しました。つまり、最後に、私のストアドプロシージャには次のものが含まれています。
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 を参照してください