Krótka historia, współpracujemy z zewnętrznym dostawcą, który ma system ankiet. System niekoniecznie jest zaprojektowany najlepiej, gdy tworzysz nową ankietę, a system tworzy nową tabelę, tj .:
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)
Tabele są generowane SurveyId
przy końcu na nazwie ( Library_
), a kolumny Pytanie są generowane QuestionId
przy końcu na końcu ( Q_
). Aby wyjaśnić, pytania są przechowywane w osobnej tabeli, więc chociaż identyfikatory pytań są sekwencyjne, nie zaczynają się od 1 dla każdej ankiety. Kolumny pytań będą oparte na identyfikatorze przypisanym do nich w tabeli.
Wydaje się, że zapytanie jest wystarczająco proste, z tym wyjątkiem, że musimy wyodrębnić dane ze wszystkich tabel ankiet, aby wysłać je do innego systemu, i tu właśnie pojawia się problem. Ponieważ tabele są tworzone automatycznie po dodaniu nowej ankiety przez front- aplikacji końcowej, inny system nie obsługuje tego typu konstrukcji. Potrzebują spójnych danych, aby mogli je konsumować.
Miałem więc zadanie napisania procedury składowanej, która wyodrębni dane ze wszystkich tabel ankiet i umieści je w następującym formacie:
SurveyId InstanceId QNumber Response
________ __________ _______ ________
1 1 1 great
1 2 1 the best
2 9 2 10
3 50 50 test
Mając dane dla wszystkich tabel w tym samym formacie, mogą one zostać wykorzystane przez każdego, bez względu na liczbę tabel ankiet i pytań.
Napisałem procedurę składowaną, która wydaje się działać, ale zastanawiam się, czy coś mi brakuje lub czy istnieje lepszy sposób na poradzenie sobie z tego typu sytuacją.
Mój kod:
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)
Utworzyłem skrzypek SQL z przykładowymi danymi i kodem.
Czy istnieje inny sposób pisania tego typu zapytań? Czy są z tym jakieś zauważalne problemy?
Niestety, istnieje wiele niewiadomych z tym ... ile tabel będziemy mieli i ile pytań na ankietę. Powiedziałbym, że będziemy mieć od 25 do 50 ankiet, po 2-5 pytań.
Odpowiedzi:
Na podstawie komentarzy osób na czacie postanowiłem nieznacznie zmienić skrypt na
INSERT INTO
tabelę tymczasową zamiast tworzyć jedną długą instrukcję SQL do wykonania na końcu. W końcu moja procedura składowana zawiera:Zobacz SQL Fiddle z ostatnim skryptem
źródło