2010-04-26 7 views
0

Je travaille actuellement sur une fonctionnalité d'exportation de données pour une application d'enquête. Nous utilisons SQL2k8. Nous stockons les données dans un format normalisé: QuestionId, RespondentId, Answer. Nous avons quelques autres tables qui définissent le texte de la question pour le QuestionId et les données démographiques pour le RespondentId ...Exportation de données désagrégées

Actuellement, j'utilise du SQL dynamique pour générer un pivot qui joint la table de questions à la table de réponses et crée une exportation, son fonctionnement ... Le problème est qu'il semble lent et nous n'avons pas beaucoup de données (moins de 50k répondants).

À l'heure actuelle, je me demande «pourquoi est-ce que je« paie »pour désagréger les données de chaque requête, pourquoi ne les cache-t-on pas? Les données exportées sont basées sur des critères dynamiques. Cela pourrait être «donnez-moi les répondants qui ont terminé x date (ou intervalle)» ou «les gens qui aiment bleu», etc. Pour cette raison, je pense que je dois mettre en cache au niveau des répondants, savoir ce que les répondants sont exportés et sélectionnez ensuite leurs données combinées désagrégées en mémoire cache.

Pour moi, le correctif rapide et incorrect est une table totalement plate, RespondentId, Question1, Question2, etc. Le problème est, nous avons plusieurs clients et cela ne change pas ET je ne veux pas avoir à maintenir le table aplatie à mesure que l'enquête change. Je pense donc à mettre une colonne XML sur la table des répondants et à mettre en cache les résultats d'un SELECT * FROM Données pour XML AUTO WHERE RespondentId = x. Avec cela en place, je serais alors en mesure d'obtenir mon exportation avec filtrage et appels XML dans la colonne XML.

Que faites-vous pour exporter des données agrégées dans un format aplati (CSV, Excel, etc.)? Cette approche semble-t-elle correcte? Je m'inquiète du coût des fonctions XML sur des ensembles de résultats plus importants (pensez à SELECT RespondentId, XmlCol.value ('// data/question_1', 'nvarchar (50)') AS [Pourquoi y a-t-il de l'air?], XmlCol.RinseAndRepeat).

Y a-t-il une meilleure technologie/approche pour cela?

Merci!

EDIT: Bloc SQL pour test. Exécuter les étapes 1 & 2 pour initialiser les données, tester avec l'étape 3, nettoyer à l'étape 4 ... À un millier de répondants par cent questions, il semble déjà plus lent que je le voudrais.

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; 

Répondre

0

Non, votre approche ne semble pas correcte. Conservez vos données normalisées. Si vous avez les bonnes clés, le "coût" à désagréger sera minime. Pour optimiser davantage vos performances, arrêtez d'utiliser SQL dynamique. Écrivez des requêtes écrites intelligemment et encapsulez-les dans des procédures stockées. Cela permettra au serveur SQL de mettre en cache les plans de requête au lieu de les reconstruire à chaque fois.

Avant cela, vérifiez toutefois le plan de requête. Il est également possible que vous oubliez un index sur au moins l'un des champs que vous recherchez, ce qui se traduira par un balayage complet des données. Vous pouvez être en mesure d'augmenter considérablement votre performance avec quelques index bien placés.

+0

Le SQL dynamique est requis pour obtenir le texte de la question en tant que noms de colonne pour le pivot. Ce n'est pas un goulot d'étranglement. J'exécute le SQL dynamique en utilisant sp_executesql et les paramètres le cas échéant, de sorte que le plan d'exécution est en cours de réutilisation. Je vais essayer de simuler un exemple de SQL ... –

Questions connexes