2010-04-26 2 views
0

В настоящее время я работаю над функцией экспорта данных для приложения обследования. Мы используем SQL2k8. Мы сохраняем данные в нормализованном формате: QuestionId, RespondentId, Answer. У нас есть пара других таблиц, которые определяют, что текст вопроса для QuestionId и демографические данные для RespondentId ...Экспорт деагрегированных данных

В настоящее время я использую динамический SQL для генерации точки опоры, которая соединяет таблицу вопросов с таблицей ответов и создает экспорт, его работу ... Проблема в том, что она кажется медленной, и у нас нет таких данных (менее 50 тыс. респондентов).

Прямо сейчас я думаю: «Почему я плачу» для деагрегации данных для каждого запроса? Почему я не кешу? » Экспортируемые данные основаны на динамических критериях. Это может быть «дать мне респондентов, которые были заполнены по дате (или диапазону) х» или «людям, которые любят синий» и т. Д. Из-за этого, я думаю, мне нужно кэшировать на уровне респондентов, выяснить, какие респонденты экспортируются и затем выберите их объединенные кэшированные деагрегированные данные.

Для меня быстрое и грязное исправление - это абсолютно плоский стол, ответчик, вопрос1, вопрос2 и т. Д. Проблема в том, что у нас есть несколько клиентов, и это не масштабируется. И я не хочу поддерживать сглаженная таблица по мере изменения опроса.

Так что я думаю о том, чтобы положить столбец XML в таблице респондента и кэшировать результаты SELECT * FROM Data FOR XML AUTO WHERE RespondentId = x. После этого я смог бы получить мой экспорт с фильтрацией и XML-вызовами в столбец XML.

Что вы делаете для экспорта агрегированных данных в сплющенном формате (CSV, Excel и т. Д.)? Этот подход выглядит нормально? Я беспокоюсь о стоимости функций XML на больших наборах результатов (думаю, SELECT RespondentId, XmlCol.value ('// data/question_1', 'nvarchar (50)') AS [Почему есть воздух?], XmlCol.RinseAndRepeat). ..

Есть ли лучшая технология/подход для этого?

Спасибо!

EDIT: SQL-блок для тестирования. Этапы работы 1 & 2, чтобы заполнить данные, протестировать с шага 3, очистить с шагом 4 ... У тысячи респондентов на сто вопросов это уже кажется медленнее, чем хотелось бы.

SET NOCOUNT ON; 

-- step 1 - create seed data 
CREATE TABLE #Questions (QuestionId INT PRIMARY KEY IDENTITY (1,1), QuestionText VARCHAR(50)); 
CREATE TABLE #Respondents (RespondentId INT PRIMARY KEY IDENTITY (1,1), Name VARCHAR(50)); 
CREATE TABLE #Data (QuestionId INT NOT NULL, RespondentId INT NOT NULL, Answer INT); 

DECLARE @QuestionTarget INT = 100 
    ,@QuestionCount INT = 0 
    ,@RespondentTarget INT = 1000 
    ,@RespondentCount INT = 0 
    ,@RespondentId INT; 

WHILE @QuestionCount < @QuestionTarget BEGIN 
    INSERT INTO #Questions(QuestionText) VALUES(CAST(NEWID() AS CHAR(36))); 
    SET @QuestionCount = @QuestionCount + 1; 
END; 

WHILE @RespondentCount < @RespondentTarget BEGIN 
    INSERT INTO #Respondents(Name) VALUES(CAST(NEWID() AS CHAR(36))); 
    SET @RespondentId = SCOPE_IDENTITY(); 
    SET @QuestionCount = 1; 

    WHILE @QuestionCount <= @QuestionTarget BEGIN 
     INSERT INTO #Data(QuestionId, RespondentId, Answer) 
      VALUES(@QuestionCount, @RespondentId, ROUND(((10 - 1 -1) * RAND() + 1), 0)); 

     SET @QuestionCount = @QuestionCount + 1; 
    END; 

    SET @RespondentCount = @RespondentCount + 1; 
END; 

-- step 2 - index seed data 
ALTER TABLE #Data ADD CONSTRAINT [PK_Data] PRIMARY KEY CLUSTERED (QuestionId ASC, RespondentId ASC); 
CREATE INDEX DataRespondentQuestion ON #Data (RespondentId ASC, QuestionId ASC); 

-- step 3 - query data 
DECLARE @Columns NVARCHAR(MAX) 
    ,@TemplateSQL NVARCHAR(MAX) 
    ,@RunSQL NVARCHAR(MAX); 

SELECT @Columns = STUFF(
    (
     SELECT DISTINCT '],[' + q.QuestionText 
     FROM #Questions AS q 
     ORDER BY '],[' + q.QuestionText 
     FOR XML PATH('') 
    ), 1, 2, '') + ']'; 

SET @TemplateSql = 
'SELECT * 
FROM 
(
    SELECT r.Name, q.QuestionText, d.Answer 
    FROM #Respondents AS r 
     INNER JOIN #Data AS d ON d.RespondentId = r.RespondentId 
     INNER JOIN #Questions AS q ON q.QuestionId = d.QuestionId 
) AS d 
PIVOT 
(
    MAX(d.Answer) 
    FOR d.QuestionText 
    IN (xxCOLUMNSxx) 
) AS p;'; 

SET @RunSql = REPLACE(@TemplateSql, 'xxCOLUMNSxx', @Columns) 
EXECUTE sys.sp_executesql @RunSql; 

-- step 4 - clean up 
DROP INDEX DataRespondentQuestion ON #Data; 
DROP TABLE #Data; 
DROP TABLE #Questions; 
DROP TABLE #Respondents; 

ответ

0

Нет, ваш подход не выглядит нормально. Сохраняйте нормализованные данные. Если у вас есть правильные ключи, «стоимость» дезагрегирования будет минимальной. Для дальнейшей оптимизации производительности прекратите использование динамического SQL. Напишите некоторые хитро написанные запросы и инкапсулируйте их в хранимые процедуры. Это позволит серверу SQL кэшировать планы запросов, а не перестраивать их каждый раз.

Прежде чем выполнять все это, проверьте план запроса. Также возможно, что вы забыли индекс по крайней мере на одном из полей, которые вы ищете, что приведет к полному сканированию данных в таблице. Вы можете значительно увеличить свою производительность с помощью нескольких хорошо расположенных индексов.

+0

Динамический SQL необходим для получения текста вопроса в виде имен столбцов для точки опоры. Это не узкое место. Я выполняю динамический SQL, используя sp_executesql и параметры, где это применимо, поэтому план выполнения повторно используется. Я попытаюсь сделать несколько примеров SQL ... –

Смежные вопросы