Экспорт деагрегированных данных - PullRequest
0 голосов
/ 26 апреля 2010

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

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

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

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

Итак, я думаю о том, чтобы поместить столбец 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). ..

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

Спасибо!

РЕДАКТИРОВАТЬ: Блок 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;

1 Ответ

0 голосов
/ 26 апреля 2010

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...