2010-04-26 31 views
0

我目前正在爲調查應用程序開發數據導出功能。我們正在使用SQL2k8。我們以標準格式存儲數據:QuestionId,RespondentId,Answer。我們有一些其他表格,它們定義問題文本對於RespondentId的QuestionId和人口統計信息...導出分解彙總數據

當前我正在使用一些動態SQL來生成將問題表連接到答案表的數據透視表,創造一個出口,它的工作......問題是它看起來很慢,我們沒有那麼多的數據(少於5萬受訪者)。

現在,我想:「我爲什麼要‘支付’,以解聚合數據對於每個查詢?我爲什麼不緩存呢?」導出的數據基於動態標準。它可以是「給我在x日期(或範圍)上完成的受訪者」或「喜歡藍色的人」等。因此,我認爲我必須緩存在受訪者級別,瞭解受訪者的出口情況以及然後選擇他們的組合緩存去聚合數據。

對我來說,快速和骯髒的修復是一個完全平坦的表,RespondentId,Question1,Question2等。問題是,我們有多個客戶端,並沒有規模,我不想維護隨着調查的變化,平整的桌子。

所以我在考慮把一個XML列答辯表和緩存一個SELECT *的結果從數據中FOR XML AUTO WHERE RespondentId = X。有了這個,我就可以通過篩選和XML調用將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; 

回答

0

不,你的做法似乎並不確定。保持你的規範化數據。如果你有適當的密鑰,解聚的「成本」將是最小的。要進一步優化性能,請停止使用動態SQL。寫一些巧妙的書面查詢並將其封裝在存儲過程中。這將允許SQL服務器緩存查詢計劃,而不是每次重建它們。

你做任何在此之前,但是,檢查查詢計劃。您還可能在至少一個正在搜索的字段中忘記索引,這會導致對數據進行全表掃描。您可以通過幾個良好的索引來大幅提高性能。

+0

需要動態SQL才能將問題文本作爲數據透視表的列名稱。這不是瓶頸。我正在使用sp_executesql和適用的參數執行動態SQL,因此執行計劃正在被重用。我會嘗試模擬一些示例SQL ... – 2010-04-26 03:38:36