2010-06-18 7 views
1

Basé sur le tableau suivantt-sql select requête

Title Jul-10 Aug-10 Sep-10 Oct-10 Nov-10 Dec-10 Jan-11 Feb-11 Mar-11 Apr-11 May-11 Jun-11 
-------------------------------------------------------------------------------------------- 
A  Null M1  Null M2  Null Null Null Null M3  Null Null Null 
B  Null M1  Null Null Null Null M2  Null Null Null Null Null 
C  Null Null Null Null Null M1  Null Null Null Null Null Null 

Comment puis-je sélectionner uniquement les colonnes entre une certaine plage.

For instance if input variables are: 
------------------------------------- 
@start = 'Oct-10' 
@end = 'Apr-11' 

Ensuite, la sortie sera:

Title Oct-10 Nov-10 Dec-10 Jan-11 Feb-11 Mar-11 Apr-11 
----------------------------------------------------------------- 
A  M2  Null Null  Null  Null  M3  Null 
B  Null Null Null  M2  Null  Null  Null 
C  Null Null M1  Null  Null  Null  Null 
+0

Il semble que les en-têtes de colonnes sont des mois en un an, sont-ils? –

+0

@marc_s: L'entrée (table source) provient de la réponse de "Aide avec requête t-sql". Je voulais ajouter la même question mais j'ai pensé qu'une question séparée permettrait de réduire la confusion en se concentrant sur la deuxième partie. – stackoverflowuser

+0

Avez-vous la possibilité de fournir des dates valides dans vos paramètres? Vous utilisez 'Oct-10', pouvez-vous envoyer 10-1-2010 à la place? – Kenneth

Répondre

1

est ici un moyen plus facile de faire votre pivot, en utilisant une procédure stockée pratique nommée pivot_query (code is here, examples here). De cette façon, vous utilisez vos critères de date de début et de fin pour limiter d'abord les données à faire pivoter, limitant ainsi les colonnes que vous obtenez après le pivot.

Le fn_MonthRange() function est un CTE récursif qui fournit un tableau de dates à un mois d'intervalle entre les dates de début et de fin, que vous joignez ensuite à vos données. Cela remplira tous les mois manquants.

(fn_DateRange() est similaire, mais fonctionne pour des segments de temps arbitraires comme "toutes les 15 minutes", toutes les heures, tous les 3 jours, etc.)

create table #testdata 
     (
     id   integer, 
     Title  varchar(20), 
     TheDate  datetime, 
     Metadata varchar(20) 
     ) 
    go 


    insert into #testdata values(1,'A','08/01/2010','M1') 
    insert into #testdata values(1,'A','10/05/2010','M2') 
    insert into #testdata values(1,'A','03/15/2011','M3') 
    insert into #testdata values(2,'B','09/20/2010','M1') 
    insert into #testdata values(2,'B','01/15/2011','M2') 
    insert into #testdata values(3,'C','12/15/2010','M1') 
    go 

    declare @mySQL  varchar(MAX); 
    declare @StartDate varchar(20); 
    declare @EndDate  varchar(20); 

    set @StartDate = '08/01/2010'; 
    set @EndDate = '03/15/2011'; 

    set @mySQL = ' 
    select 
     id, 
     Title, 
     Left(Datename(month, TheDate),3) + ''-'' + right(cast(Year(theDate) as varchar(4)),2) monyr, 
     Metadata 
    from 
    dbo.fn_MonthRange(''' + @StartDate + ''',''' + @EndDate + ''') dr 

    LEFT OUTER JOIN #testdata td 
     on (td.TheDate between dr.startdate and dr.enddate) 
where 
    dr.StartDate between ''' + @StartDate + ''' and ''' + @EndDate + ''''; 

    exec pivot_query @mySQL, 'Title', 'monyr','max(Metadata)' 
    go 

    Result: 
Title    Aug-10    Dec-10    Feb-11    Jan-11    Mar-11    Nov-10    Oct-10    Sep-10    
-------------------- -------------------- -------------------- -------------------- -------------------- -------------------- -------------------- -------------------- -------------------- 
A     M1     NULL     NULL     NULL     M3     NULL     M2     NULL     
B     NULL     NULL     NULL     M2     NULL     NULL     NULL     M1     
C     NULL     M1     NULL     NULL     NULL     NULL     NULL     NULL     
None     NULL     NULL     None     NULL     NULL     None     NULL     NULL     
+0

Merci. En fait, la sortie devrait contenir des colonnes Jan-09 à Dec-15 puisque la plage d'entrée était «01/01/2009» à «12/31/2015». – stackoverflowuser

+0

Ah oui, la fonction "remplisseur" - ajouté cela dans la requête aussi bien. –

1

Il ressemble à la table est pivotée, je veux dire que les colonnes devraient probablement être rangées. Ce type de conception est très lisible par l'homme, mais pas très interrogeable.

Jetez un coup d'œil à l'opérateur UNPIVOT. Vous devriez pouvoir l'utiliser pour obtenir un ensemble de données avec des lignes sur lesquelles vous pouvez filtrer, et lorsque vous avez terminé, vous pouvez revenir en arrière dans ce format si vous en avez besoin.

Voici un article: http://msdn.microsoft.com/en-us/library/ms177410.aspx

UNPIVOT réalise presque l'opération inverse de PIVOT, par des colonnes en rotation en lignes.

+0

@Biran MacKay: mais je ne veux pas faire pivoter les colonnes. Je veux juste filtrer les colonnes en fonction de la plage. Est-ce que c'est possible? – stackoverflowuser

+0

Pas à ma connaissance. Mais si vous les faites pivoter dans la requête, alors vous aurez des lignes au lieu de colonnes, et vous pouvez filtrer sur les lignes! :) –

+0

(et puis quand vous avez fini avec tout cela, vous pouvez le faire revenir dans cette forme si vous voulez - fondamentalement, cette conception vous oblige à effectuer une étape supplémentaire). –

2

C'est quelque chose qui a plus de sens pour l'application que SQl. Faire un:

select field1, field2 from your table between date1 and date2. 

Puis laissez l'application pivoter les données. Généralement, les applications peuvent pivoter les données plus efficacement que SQL. Espciellement quand le nombre de colonnes varie chaque fois que vous l'exécutez.

+0

Je suis complètement d'accord. – Kenneth

0

La réponse courte est non. J'ai fait des tests. Je vais vous donner le code pour jouer avec, mais à cause de la façon dont les NULL sont gérés avec unpivot, vous perdrez la gamme contiguë de mois. Ils pourraient être rajoutés avec le pivot, mais cela nécessiterait une table de nombres, etc. Trop pour que je fasse un 5:02! =)

WITH datepivot 
    AS (SELECT title, 
       Convert(DATETIME, Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(datecol, 'Jan', '01-01'), 'Feb', '02-01'), 'Mar', '03-01'), 'Apr', '04-01'), 'May', '05-01'), 'Jun', '06-01'), 'Jul', '07-01'), 'Aug', '08-01'), 'Sep', '09-01'), 'Oct', '10-01'), 'Nov', '11-01'), 'Dec', '12-01')) datecol, 
       datecol                                                                                origdatecol, 
       Isnull(code, 0)                                                                              code 
     FROM (SELECT title, 
         [Jul-10], 
         [Aug-10], 
         [Sep-10], 
         [Oct-10], 
         [Nov-10], 
         [Dec-10], 
         [Jan-11], 
         [Feb-11], 
         [Mar-11], 
         [Apr-11], 
         [May-11], 
         [Jun-11] 
       FROM test) test UNPIVOT (code FOR datecol IN ([Jul-10], [Aug-10], [Sep-10], [Oct-10], [Nov-10], [Dec-10], [Jan-11], [Feb-11], [Mar-11], [Apr-11], [May-11], [Jun-11]))AS dateunpivot) 
--SELECT * from datepivot --uncomment to view unpivoted data 
SELECT * 
FROM (SELECT title, 
       code, 
       origdatecol 
     FROM datepivot 
     WHERE datecol BETWEEN '10-01-2010' AND '04-01-2011') a PIVOT (Max(code) FOR origdatecol IN ([Jul-10], [Aug-10], [Sep-10], [Oct-10], [Nov-10], [Dec-10], [Jan-11], [Feb-11], [Mar-11], [Apr-11], [May-11], [Jun-11])) b 
0

Vous pouvez générer l'instruction select SQL nécessaire, utilisez sp_executesql pour exécuter la requête.

Voici un exemple de la façon de générer les noms de colonnes mois:

DECLARE @nextMonth TABLE(
    currentMonth CHAR(3), 
    nextMonth CHAR(3) 
); 

INSERT INTO @nextMonth(currentMonth, nextMonth) 
SELECT 'Jan', 'Feb' UNION ALL 
SELECT 'Feb', 'Mar' UNION ALL 
SELECT 'Mar', 'Apr' UNION ALL 
SELECT 'Apr', 'May' UNION ALL 
SELECT 'May', 'Jun' UNION ALL 
SELECT 'Jun', 'Jul' UNION ALL 
SELECT 'Jul', 'Aug' UNION ALL 
SELECT 'Aug', 'Sep' UNION ALL 
SELECT 'Sep', 'Oct' UNION ALL 
SELECT 'Oct', 'Nov' UNION ALL 
SELECT 'Nov', 'Dec' UNION ALL 
SELECT 'Dec', 'Jan' 

DECLARE @start char(6), @end char(6), @current char(6), @columnNames varchar(1000) 
SELECT @start = 'Oct-10', @end = 'Apr-11' 

SET @current = @start; 
WHILE (@current <> @end) 
BEGIN 
    IF (@columnNames IS NULL) 
     SET @columnNames = @current; 
    ELSE 
     SET @columnNames = @columnNames + ', ' + @current; 

    SELECT @current = nextMonth + '-' + LTRIM(STR(case LEFT(@current, 3) WHEN 'Dec' THEN 1+RIGHT(@current, 2) ELSE RIGHT(@current, 2) END)) 
    FROM @nextMonth 
    WHERE currentMonth = left(@current, 3) 
END 

SELECT @columnNames 

Cela me donne:

Oct-10, Nov-10, Dec-10, Jan-11, Feb-11, Mar-11 

incrémenter l'année pourrait se faire de manière plus explicite. J'ai profité du casting automatique.

Je n'avoir accès à SQL Server 2005, donc il peut-être une bien meilleure façon en 2008.