2017-08-04 2 views
1

Problème

Dans mon sql-server-2014 je stocke des projets dans une table avec les colonnes:Sélectionnez plusieurs lignes de Timespan

Startdate .. | Date limite .... | Nom du projet ................. | Volume
2017-02-13 | 2017-04-12 | GenerateRevenue ......... | 20.02
2017-04-02 | 2018-01-01 | BuildRevenueGenerator | 300.044
2017-05-23 | 2018-03-19 | HarvestRevenue ............ | 434.009

J'ai besoin d'un SELECT pour me donner une ligne par mois du projet pour chaque projet. les jours du mois ne doivent pas être considérés.

Date .......... | Nom du projet .................. | Volume
2017-02-01 | GenerateRevenue ......... | 20.02
2017-03-01 | GenerateRevenue ......... | 20.02
2017-04-01 | GenerateRevenue ......... | 20.02
2017-04-01 | BuildRevenueGenerator | 300.044
2017-05-01 | BuildRevenueGenerator | 300.044
2017-06-01 | BuildRevenueGenerator | 300,044
...

extra

Idéalement la logique du SELECT me permet à la fois de calculer le volume mensuel ainsi que la différence entre chaque mois et le précédent.

Date .......... | Nom du projet .................. | VolumeMonthly
2017-02-01 | GenerateRevenue ......... | 6.6733
2017-03-01 | GenerateRevenue ......... | 6.6733
2017-04-01 | GenerateRevenue ......... | 6.6733
2017-04-01 | BuildRevenueGenerator | 30.0044
2017-05-01 | BuildRevenueGenerator | 30.0044
2017-06-01 | BuildRevenueGenerator | 30,0044
...

aussi ...

Je sais que je peux la carte sur une table de calendrier temporaire, mais qui tend à se ballonnement et complexe très rapide. Je cherche vraiment une meilleure façon de résoudre ce problème.

Solution

solution Gordons a travaillé très bien et il ne nécessite pas une seconde table ou la cartographie sur un calendrier quelconque. Bien que je devais changer quelques choses, comme s'assurer que les deux côtés de l'union ont le même SELECT.

Voici ma version adaptée:

with cte as (
    select startdate as mondate, enddate, projectName, volume 
    from projects 
    union all 
    select dateadd(month, 1, mondate), enddate, projectName, volume 
    from cte 
    where eomonth(dateadd(month, 1, mondate)) <= eomonth(enddate) 
) 
select * from cte; 

volume peut être réalisé mensuellement par le remplacement de volume avec:

CAST(Cast(volume AS DECIMAL)/Cast(Datediff(month, 
startdate,enddate)+ 1 AS DECIMAL) AS DECIMAL(15, 2)) 
END AS [volumeMonthly] 
+3

Je recommande les étapes de bébé. –

+1

Des exemples de données et les résultats souhaités aideraient. Vous devriez également simplifier la question. –

+0

Merci Gordon, vous avez raison. J'ai ajouté quelques exemples de données comme vous l'avez suggéré. – LeComte

Répondre

0

Vous pouvez utiliser une sous-requête récursive pour développer les lignes pour chaque projet, sur la base table:

with cte as (
     select stardate as mondate, p.* 
     from projects 
     union all 
     select dateadd(month, 1, mondate), . . . -- whatever columns you want here 
     from cte 
     where eomonth(dateadd(month, 1, mondate)) <= eomonth(enddate) 
    ) 
select * 
from cte; 

Je ne sais pas si cela répond réellement à votre question. Quand j'ai lu la première fois la question, j'ai pensé que la table avait une rangée par projet.

+0

Oui, vous avez raison, il y a une ligne par projet – LeComte

1

Une autre option est avec une table de pointage ad hoc

Exemple

-- Some Sample Data 
Declare @YourTable table (StartDate date,EndDate date,ProjectName varchar(50), Volume float) 
Insert Into @YourTable values 
('2017-03-15','2017-07-25','Project X',25) 
,('2017-04-01','2017-06-30','Project Y',50) 

-- Set Your Desired Date Range 
Declare @Date1 date = '2017-01-01' 
Declare @Date2 date = '2017-12-31' 

Select Period = D 
     ,B.* 
     ,MonthlyVolume = sum(Volume) over (Partition By convert(varchar(6),D,112)) 
From (Select Top (DateDiff(MONTH,@Date1,@Date2)+1) D=DateAdd(MONTH,-1+Row_Number() Over (Order By (Select Null)),@Date1) 
     From master..spt_values n1 
    ) A 
Join @YourTable B on convert(varchar(6),D,112) between convert(varchar(6),StartDate,112) and convert(varchar(6),EndDate,112) 
Order by Period,ProjectName 

Retours

enter image description here

Note: Utilisez LEFT JOIN pour se lacunes e

0

En utilisant deux common table expressions, une table de calendrier adhoc pendant des mois et lag() (SQL Server 2012+) pour le calcul delta final:

create table projects (id int identity(1,1), StartDate date, EndDate date, ProjectName varchar(32), Volume float); 
insert into projects values ('20170101','20170330','SO Q1',240),('20170214','20170601','EX Q2',120) 

declare @StartDate date = '20170101' 
     , @EndDate date = '20170731'; 
;with Months as (
    select top (datediff(month,@startdate,@enddate)+1) 
     MonthStart = dateadd(month, row_number() over (order by number) -1, @StartDate) 
     , MonthEnd = dateadd(day,-1,dateadd(month, row_number() over (order by number), @StartDate)) 
    from master.dbo.spt_values 
) 
, ProjectMonthlyVolume as (
    select p.* 
    , MonthlyVolume = Volume/(datediff(month,p.StartDate,p.EndDate)+1) 
    , m.MonthStart 
    from Months m 
    left join Projects p 
     on p.EndDate >= m.MonthStart 
    and p.StartDate <= m.MonthEnd 
) 
select 
    MonthStart = convert(char(7),MonthStart,120) 
    , MonthlyVolume = isnull(sum(MonthlyVolume),0) 
    , Delta = isnull(sum(MonthlyVolume),0) - lag(Sum(MonthlyVolume)) over (order by MonthStart) 
from ProjectMonthlyVolume pmv 
group by MonthStart 

rextester Démo: http://rextester.com/DZL54787

retours:

+------------+---------------+-------+ 
| MonthStart | MonthlyVolume | Delta | 
+------------+---------------+-------+ 
| 2017-01 |   80 | NULL | 
| 2017-02 |   104 | 24 | 
| 2017-03 |   104 | 0  | 
| 2017-04 |   24 | -80 | 
| 2017-05 |   24 | 0  | 
| 2017-06 |   24 | 0  | 
| 2017-07 |    0 | -24 | 
+------------+---------------+-------+