2009-12-03 12 views
0

J'ai une table avec les données suivantes:auto JOIN sur des données variables

Fiscal Year | Fiscal Quarter | Fiscal Week | Data 
2009  | 2    | 22   | 9.5 
2009  | 2    | 24   | 8.8 
2009  | 2    | 26   | 8.8 
2009  | 3    | 28   | 8.8 
2009  | 3    | 31   | 9.1 
2009  | 3    | 33   | 8.8 

Je voudrais écrire une requête qui produirait les éléments suivants:

Fiscal Year | Fiscal Quarter | Fiscal Week | Data | Trend 
2009  | 2    | 22   | 9.5 | NULL 
2009  | 2    | 24   | 8.8 | -0.7 
2009  | 2    | 26   | 8.8 | 0 
2009  | 3    | 28   | 8.8 | 0 
2009  | 3    | 31   | 9.1 | 0.3 
2009  | 3    | 33   | 8.8 | -0.3 

Je sais que cela peut être facilement réalisé en faisant une simple jointure de la table à lui-même avec la semaine fiscale précédente, cependant ce ne sera pas toujours un simple t1.[Fiscal Week] = t2.[Fiscal Week] - 2 parce que parfois la différence est de 3 semaines.

je peux tirer le maximum dossier facilement avec quelque chose comme ceci:

SELECT 
    MAX(t1.[Fiscal Week]) "LastWeek" 
FROM t1 
WHERE t1.[Fiscal Year] = 2009 
    AND t1.[Fiscal Week] < 31 

Mais je suis à une perte quand il vient à abstraire pour faire le travail rejoindre. Comment puis-je faire une auto-jointure sur "la plus grande semaine fiscale qui est plus petite que l'enregistrement actuel"?

+0

Quelle plate-forme de SGBDR utilisez-vous (à savoir sql-server-2000, mysql, sql-server-2005, oracle, etc. – chadhoc

+0

SQL Server 2005. –

+0

Dommage que SQL Server ne prend pas en charge les fonctions LEAD/LAG. –

Répondre

3

manière la plus simple de le faire avec Sql 2005+ est d'utiliser la ranking and windowing functions - simplement partition/commander vos données et assigner une séquence appropriée à chaque enregistrement (dans ce cas, vous partitionnez par fiscalYear et commande par fiscalWeek sur cette fenêtre) - ressemblerait à quelque chose comme ceci:

with data as 
(
    select row_number() over (partition by a.fiscalYear order by a.fiscalWeek) as rn, 
      a.fiscalYear, a.fiscalQuarter, a.fiscalWeek, a.Data 
    from #TableName a 
) 
select a.fiscalYear, a.fiscalQuarter, a.fiscalWeek, a.Data, 
     a.Data - b.Data as Trend 
from data a 
left join data b 
on  a.fiscalYear = b.fiscalYear 
and  a.rn = b.rn + 1 
order by a.fiscalYear, a.rn 

cette requête particulière ne permettrait pas l'extension à travers les frontières fiscalYear - si vous vouliez étendre à traverser ces limites de l'année, vous voudriez simplement supprimer la clause "partition par" et « fiscalYear » condition de jointure et la place commander l'ensemble par une combinaison des fiscalYear et fiscalWeek, quelque chose comme ceci:

with data as 
(
    select row_number() over (order by a.fiscalYear + a.fiscalWeek) as rn, 
      a.fiscalYear, a.fiscalQuarter, a.fiscalWeek, a.Data 
    from #TableName a 
) 
select a.fiscalYear, a.fiscalQuarter, a.fiscalWeek, a.Data, 
     a.Data - b.Data as Trend 
from data a 
left join data b 
on  a.rn = b.rn + 1 
order by a.rn 
+0

+1 vous voyez, je savais qu'il y avait une façon plus élégante! – davek

+0

cela a bien fonctionné. Merci. –

1

Si vous utilisez SQL Server 2005, vous pouvez utiliser la clause CROSS APPLY.

Ici, vous pouvez avoir une fonction de valeur table, qui retournera la ligne pour la semaine fiscale, avant celle de la ligne courante.

CREATE FUNCTION dbo.fn_GetFiscalWeekBeforeThis(@Year AS int, 
    @CurrentWeek as int) 
    RETURNS TABLE 
AS 
RETURN 
    SELECT TOP 1 * 
    FROM t1 
    WHERE [Fiscal Year] = @Year 
    AND [Fiscal Week] < @CurrentWeek 
    ORDER BY [Fiscal Week] DESC 
GO 

Et puis,

SELECT A.*, 
A.Data - B.Data 
FROM 
t1 A 
CROSS APPLY 
    dbo.fn_GetFiscalWeekBeforeThis(A.[Fiscal Year], 
    A.[Fiscal Week]) AS B 

EDIT: Notez que je l'ai adapté le SQL en regardant un article & sans IDE.
Aussi, je ne sais pas - comment cela fonctionnera pour la première rangée.

Alors, s'il vous plaît être gentil :)

EDIT2: Permettez-moi, si cela ne fonctionne pas du tout.
Cela m'aidera à savoir - ne pas répondre à des choses, sans vérifier les résultats.

EDIT3: J'ai supprimé le besoin du paramètre Quarter de la fonction.

+0

Je pourrais avoir besoin d'utiliser la solution pour plusieurs tables, et je ne voulais pas avoir à créer une fonction pour chaque table. –

1

est-ce que cela fonctionne? La requête en ligne est un peu d'une bouchée et il y a probablement une meilleure façon de le faire ...

select 
    [fiscal year] 
, [fiscal quarter] 
, [fiscal week] 
, [data] 
, (
    select top 1 (i.data - t1.data) from t1 as i 
    where i.[fiscal year] >= t1.[fiscal year] 
    and i.[fiscal quarter] >= t1.[fiscal quarter] 
    and i.[fiscal week] >= t1.[fiscal week] 
    and (
      i.[fiscal year] <> t1.[fiscal year] 
     or i.[fiscal quarter] <> t1.[fiscal quarter] 
     or i.[fiscal week] <> t1.[fiscal week] 
    ) 
    order by [fiscal year] asc, [fiscal quarter] asc, [fiscal week] asc 
) as trend 
from t1 
1
DECLARE @Fiscal TABLE 
    ( 
    FiscalYear int 
    ,FiscalQuarter int 
    ,FiscalWeek int 
    ,[Data] decimal(4,1) 
) 

INSERT INTO @Fiscal 
      (FiscalYear, FiscalQuarter, FiscalWeek, [Data]) 
SELECT 2009, 2, 22, 9.5 UNION 
SELECT 2009, 2, 24, 8.8 UNION 
SELECT 2009, 2, 26, 8.8 UNION 
SELECT 2009, 3, 28, 8.8 UNION 
SELECT 2009, 3, 31, 9.1 UNION 
SELECT 2009, 3, 33, 8.8 UNION 
SELECT 2010, 1, 1, 9.0 UNION 
SELECT 2010, 1, 2, 9.2 

; 
WITH abcd 
     AS (SELECT FiscalYear 
        ,FiscalQuarter 
        ,FiscalWeek 
        ,[Data] 
        ,row_number() OVER (ORDER BY FiscalYear, FiscalWeek) AS rn 
      FROM @Fiscal 
      ) 
    SELECT a.FiscalYear 
     ,a.FiscalQuarter 
     ,a.FiscalWeek 
     ,a.[Data] 
     ,a.[Data] - b.[Data] AS [Trend] 
    FROM abcd AS a 
      LEFT JOIN abcd AS b ON b.rn = (a.rn - 1) 
    ORDER BY a.FiscalYear 
     ,a.FiscalWeek 
+0

yup, c'était la réponse de chadhoc ci-dessus. ça fonctionne très bien. –

+0

@Nathan; oui j'étais en retard, vous avez terminé en 30 minutes. –