【问题标题】:Exporting de-aggregated data导出去聚合的数据
【发布时间】:2010-04-26 00:01:26
【问题描述】:

我目前正在为调查应用程序开发数据导出功能。我们正在使用 SQL2k8。我们以标准化格式存储数据:QuestionId、RespondentId、Answer。我们还有几个其他表格,它们定义了 QuestionId 的问题文本和 RespondentId 的人口统计...

目前我正在使用一些动态 SQL 来生成一个将问题表连接到答案表并创建导出的数据透视表,它正在工作......问题是它看起来很慢而且我们没有那么多数据(少于 50,000 名受访者)。

现在我在想“为什么我要‘付费’去聚合每个查询的数据?我为什么不缓存它?”导出的数据基于动态标准。可能是“给我在 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 [Why is there air?], 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;

【问题讨论】:

    标签: sql-server xml caching export


    【解决方案1】:

    不,您的方法似乎不好。保留标准化数据。如果您有适当的密钥,则分解的“成本”将是最小的。要进一步优化性能,请停止使用动态 SQL。编写一些巧妙编写的查询,并将它们封装在存储过程中。这将允许 SQL Server 缓存查询计划,而不是每次都重新构建它们。

    但是,在您执行任何此操作之前,请检查查询计划。您也可能忘记了至少一个正在搜索的字段的索引,这将导致对数据进行全表扫描。您可以通过一些放置良好的索引来大幅提高性能。

    【讨论】:

    • 动态 SQL 需要获取问题文本作为数据透视表的列名。这不是瓶颈。我正在使用 sp_executesql 和适用的参数执行动态 SQL,因此正在重用执行计划。我将尝试模拟一些示例 SQL...
    猜你喜欢
    • 1970-01-01
    • 2021-07-23
    • 2019-01-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多