Thủ tục lưu trữ để trả về dữ liệu bảng được tạo động


10

Câu chuyện nhanh trở lại, chúng tôi đang làm việc với một nhà cung cấp bên ngoài có hệ thống khảo sát. Hệ thống không nhất thiết phải được thiết kế tốt nhất ở chỗ khi bạn tạo một khảo sát mới và hệ thống tạo một bảng mới, nghĩa là:

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)

Các bảng được tạo với phần SurveyIdcuối của tên ( Library_) và các cột Câu hỏi được tạo với QuestionIdphần cuối của nó ( Q_). Để làm rõ, các câu hỏi được lưu trữ trong một bảng riêng vì vậy trong khi các id câu hỏi là tuần tự, chúng không bắt đầu từ 1 cho mỗi khảo sát. Các cột câu hỏi sẽ được dựa trên id được gán cho chúng trong bảng.

Có vẻ đủ đơn giản để truy vấn, ngoại trừ chúng ta cần trích xuất dữ liệu từ tất cả các bảng khảo sát được gửi đến một hệ thống khác và đây là nơi xảy ra sự cố. Vì các bảng được tạo tự động khi một khảo sát mới được thêm vào trước ứng dụng cuối, hệ thống khác không thể xử lý loại cấu trúc này. Họ cần dữ liệu phù hợp để họ tiêu thụ.

Vì vậy, tôi được giao nhiệm vụ viết một thủ tục được lưu trữ sẽ trích xuất dữ liệu từ tất cả các bảng Khảo sát và đặt nó theo định dạng sau:

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

Bằng cách có dữ liệu cho tất cả các bảng ở cùng định dạng, sau đó bất kỳ ai cũng có thể sử dụng dữ liệu đó cho dù có bao nhiêu bảng khảo sát và câu hỏi tồn tại.

Tôi đã viết một thủ tục được lưu trữ có vẻ như đang hoạt động nhưng tôi tự hỏi liệu tôi có thiếu thứ gì không hoặc liệu có cách nào tốt hơn để xử lý loại tình huống này.

Mã của tôi:

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)

Tôi đã tạo SQL Fiddle với một số dữ liệu mẫu và mã.

Có một cách khác để loại truy vấn này nên được viết? Có bất kỳ vấn đề đáng chú ý với nó?

Thật không may, có rất nhiều điều chưa biết với điều này ... chúng ta sẽ có bao nhiêu bảng và bao nhiêu câu hỏi cho mỗi khảo sát. Tôi muốn nói rằng chúng tôi sẽ có từ 25-50 khảo sát, với 2-5 câu hỏi mỗi câu hỏi.


1
Tôi ngại hỏi, nhưng, "Có bao nhiêu bàn?"
RBarryYoung

@RBarryYoung Tại thời điểm này, điều đó chưa được biết vì nó sẽ phụ thuộc vào số lượng khảo sát được tạo. Đó là một phần của vấn đề.
Taryn

Hãy cho chúng tôi một phạm vi sau đó. Nó rất quan trọng.
RBarryYoung

Tôi sẽ nói bất cứ nơi nào từ 25-50 bảng.
Taryn

Câu trả lời:


2

Dựa trên nhận xét từ mọi người trong cuộc trò chuyện, tôi quyết định thay đổi tập lệnh của mình một chút thành INSERT INTObảng tạm thời thay vì tạo một câu lệnh SQL dài để thực thi ở cuối. Vì vậy, cuối cùng thủ tục lưu trữ của tôi có chứa như sau:

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

Xem SQL Fiddle với tập lệnh cuối cùng

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.