2010-02-11 5 views
2

DB: SQL Server 2005Colonnes dynamiques - SQL Server - Mois sous forme de colonnes

Nous avons une table qui contient des données de cette manière:

Project    Year  Jan     Feb     Mar     Apr     May     Jun     Jul     Aug     Sep     Oct     Nov     Dec 
-------------------- ----------- --------------------- --------------------- --------------------- --------------------- --------------------- --------------------- --------------------- --------------------- --------------------- --------------------- --------------------- --------------------- 
11-11079    2008  0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     75244.90 
11-11079    2009  466.00    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00 
11-11079    2010  855.00    0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00     0.00 
01-11052    2009  56131.00    0.00     36962.00    -61596.00    2428.00    84.00     0.00     0.00     0.00     0.00     0.00     0.00 

Quelqu'un souhaite que les données à afficher sur une seule ligne pour l'ensemble du projet. Les colonnes seraient dynamiques en fonction du nombre d'années passées dans le futur. Un exemple serait:

Project  Jan-2009  Feb-2009  Mar-2009  Apr-2009... Dec-2009  Jan-2010 
-------------- ------------ ------------ ------------ ----------- ------------ --------- 
11-11079  466.00  0.00   0.00   0.00  0.00   855.00  
01-11052  56131.00  0.00   36962.00  -61596.00 2428.00  0.00 

J'ai lu de nombreux exemples où la date est rempli dans une colonne pour chaque entrée, mais je n'ai pas trouvé de cas où les mois sont le nom de la colonne et l'année est dans la ligne .

SQL dynamique avec une table pivotante?
Ou une manipulation à grande échelle en utilisant SQL, tables temporaires, jointures et unions?
Des réflexions sur l'utilisation de la fonction de table pivotante SSIS?

+1

S'il vous plaît ne pas utiliser le terme « MS SQL » dans un titre ou dans votre question. Il n'y a pas un tel produit. En l'utilisant, il est difficile pour une recherche de trouver votre question et de confondre les choses entre SQL Server et MySQL. –

+0

heh - Je l'ai ignoré les questions TSQL que je connais la réponse pour que je l'ai lu MSSQL MySQL –

+0

John - ces noms sont proches et confus, mais en toute justice pour les gens qui postent ici, Microsoft SQL Server est appelé MSSQL d'une certaine façon. Jetez un oeil au chemin vers les binaires :-) C: \ Program Files \ Microsoft SQL Server \ MSSQL10.TEST2008 \ MSSQL Ou PowerShell> Get-Service, qui produit MSSQL $ TEST2008 comme nom du service, pour moi au moins. – onupdatecascade

Répondre

4

Vos données sont déjà pivotées, mais doivent être pivotées à un niveau différent. Je pense que la meilleure façon de gérer cela est de le déplier d'abord, puis de gérer le niveau de pivot correct en second.

Étape 1: Unpivot

Vous pouvez utiliser la commande SQL 2005 UNPIVOT, ou utiliser une technique CROSS JOIN. Voici des exemples des deux. Remarque J'ai laissé de côté des mois au milieu pour garder les choses simples. Il suffit de les ajouter.

-- CROSS JOIN method (also works in SQL 2000) 
SELECT 
    P.Project, 
    Mo = 
     DateAdd(mm, 
     X.MonthNum, 
     DateAdd(yy, P.[Year] - 1900, '19000101') 
    ), 
    Amount = 
     CASE X.MonthNum 
     WHEN 0 THEN Jan 
     WHEN 1 THEN Feb 
     WHEN 11 THEN Dec 
     END 
FROM 
    ProjectData P 
    CROSS JOIN (
     SELECT 0 UNION ALL SELECT 1 UNION ALL SELECT 11 
    ) X (MonthNum) 

Chaque ligne est répété 12 fois, puis une instruction CASE sort seulement un mois pour chaque ligne, en laissant les données bien pivotée.

-- UNPIVOT method 
SELECT 
    P.Project, 
    Mo = 
     DateAdd(mm, 
      Convert(int, P.MonthNum), 
      DateAdd(yy, P.[Year] - 1900, '19000101') 
     ), 
    P.Amount 
FROM 
    (
     SELECT Project, [Year], [0] = Jan, [1] = Feb, [11] = Dec 
     FROM ProjectData 
    ) X UNPIVOT (Amount FOR MonthNum IN ([0], [1], [11])) P 

DROP TABLE ProjectData 

Aucune des deux méthodes n'est un gagnant de performance clair tout le temps. Parfois, l'un fonctionne mieux que l'autre (en fonction des données pivotées). La méthode UNPIVOT utilise un filtre dans le plan d'exécution que le CROSS JOIN ne fait pas.

Étape 2: Faites pivoter à nouveau

Maintenant, comment utiliser les données non pivoté.Vous ne dites pas comment votre quelqu'un consommera, mais puisque vous aurez besoin de mettre les données dans un fichier de sortie de quelque sorte, je propose l'aide de SSRS (SQL Server Reporting Services), qui est livré avec SQL Server 2005 sans frais supplémentaires.

Il suffit d'utiliser l'objet du rapport Matrix pour faire pivoter l'une des requêtes ci-dessus. Cet objet détermine avec bonheur les valeurs de données à convertir en étiquettes de colonne lors de l'exécution du rapport et sonne exactement comme vous le souhaitez. Si vous ajoutez une colonne qui formate la date exactement comme vous le souhaitez, vous pouvez l'ordonner par la colonne Mo, mais utiliser la nouvelle expression comme libellé de colonne.

SSRS dispose également d'une grande variété de formats et options de planification disponibles. Par exemple, vous pouvez l'envoyer par courrier électronique à un fichier Excel ou enregistrer une page Web dans un partage de fichiers.

S'il vous plaît laissez-moi savoir si je l'ai oublié quelque chose.

Pour tous ceux qui voudraient voir le code ci-dessus dans l'action, voici un script de création pour vous:

USE tempdb 

CREATE TABLE ProjectData (
    Project varchar(10), 
    [Year] int, 
    Jan decimal(15, 2), 
    Feb decimal(15, 2), 
    Dec decimal(15, 2) 
) 

SET NOCOUNT ON 

INSERT ProjectData VALUES ('11-11079', 2008, 0.0, 0.0, 75244.90) 
INSERT ProjectData VALUES ('11-11079', 2009, 466.0, 0.0, 0.0) 
INSERT ProjectData VALUES ('11-11079', 2010, 855.0, 0.0, 0.0) 
INSERT ProjectData VALUES ('01-11052', 2009, 56131.0, 0.0, 0.0) 
+0

après avoir posté ma question initiale, j'ai passé un après-midi à regarder les données et il m'a sauté que les données sont déjà pivotées. Au moment où j'ai lancé un rapide pivot, la lumière s'est allumée et j'ai pu la construire comme je le voulais. Donc, votre technique est la façon dont j'ai fini par aller! – ichauvin

-1

Je pense que vous pourriez le faire avec une boucle while imbriquée et du SQL dynamique. Ce serait une solution lente si vous ne pouvez pas enregistrer la table finale ou si vous devez régénérer toutes les colonnes tous les mois. Cependant, si c'est juste additif, cela pourrait ne pas être mauvais. De toute façon, voici comment je le ferais:

  1. La boucle de sortie choisit l'année la plus ancienne. La boucle interne choisit le premier mois.
  2. Inside Inner loop - Ajoutez une colonne portant le nom de - à votre table.
  3. boucle à l'intérieur intérieure - Table de mise à jour avec toutes les informations pour la nouvelle - colonne avec SQL dynamique
  4. à travers la boucle Itérer intérieure pour chaque mois
  5. Itérer à travers la boucle externe pour chaque année.
+0

-1. Je suis désolé, RandomBen, mais une boucle n'est tout simplement pas supportable, quand des solutions basées sur des ensembles claires et faciles sont disponibles pour le pivotement et le pivotement à nouveau. La performance avec des boucles sera absolument terrible. – ErikE

0

I Wrote un proc nommé stocké pivot_query qui peuvent aider à cela, la source est here , exemples avec données brutes here.

With your data: 

create table ProjectData 
    (
    Project      varchar(20), 
    [Year]      Integer, 
    Jan       decimal(12,2), 
    Feb       decimal(12,2), 
    Mar       decimal(12,2), 
    Apr       decimal(12,2), 
    May       decimal(12,2), 
    Jun       decimal(12,2), 
    Jul       decimal(12,2), 
    Aug       decimal(12,2), 
    Sep       decimal(12,2), 
    Oct       decimal(12,2), 
    Nov       decimal(12,2), 
    Dec       decimal(12,2) 
    ); 

insert into ProjectData values ('11-11079',2008, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 75244.90); 
insert into ProjectData values ('11-11079',2009, 466.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00); 
insert into ProjectData values ('11-11079',2010, 855.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00) ; 
insert into ProjectData values ('01-11052',2009, 56131.00, 0.00, 36962.00, -61596.00, 2428.00, 84.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00); 

declare @mySQL varchar(MAX) 

set @mySQL = 'select * from ProjectData' 

exec pivot_query @mySQL, 'Project', 'Year', 'max(Jan) Jan,max(Feb) Feb,max(Mar) Mar,max(Apr) Apr,max(Jun) Jun,max(Jul) Jul,max(Aug) Aug,max(Sep) Sep,max(Oct) Oct,max(Nov) Nov,max(Dec) Dec' 

Results: 
Project    2008_Jan  2008_Feb  2008_Mar  2008_Apr  2008_Jun  2008_Jul  2008_Aug  2008_Sep  2008_Oct  2008_Nov  2008_Dec  2009_Jan  2009_Feb  2009_Mar  2009_Apr  2009_Jun  2009_Jul  2009_Aug  2009_Sep  2009_Oct  2009_Nov  2009_Dec  2010_Jan  2010_Feb  2010_Mar  2010_Apr  2010_Jun  2010_Jul  2010_Aug  2010_Sep  2010_Oct  2010_Nov  2010_Dec 
-------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ 
01-11052    NULL   NULL   NULL   NULL   NULL   NULL   NULL   NULL   NULL   NULL   NULL   56131.00  .00   36962.00  -61596.00 84.00  .00   .00   .00   .00   .00   .00   NULL   NULL   NULL   NULL   NULL   NULL   NULL   NULL   NULL   NULL   NULL 
11-11079    .00   .00   .00   .00   .00   .00   .00   .00   .00   .00   75244.90  466.00  .00   .00   .00   .00   .00   .00   .00   .00   .00   .00   855.00  .00   .00   .00   .00   .00   .00   .00   .00   .00   .00 

Non exacte mais sacrément proche. :-)

+0

J'ai oublié d'ajouter le mois de mai, mais SO ne me laissera pas éditer le post pour le mettre en. :-) –

+0

Holy crud, Ron. Pourquoi utiliseriez-vous cette énorme quantité de code de procédure lorsque le problème peut être résolu avec du code basé sur un ensemble et des outils SQL Server existants? – ErikE

+0

Il est générique et fonctionne pour toute requête, sans avoir à connaître les valeurs de vos colonnes pivot. Nous avons enregistré des tonnes de temps de développement dans de nombreux rapports de style "pivotés". Je ne l'ai pas écrit juste pour ça. :-) –