简体   繁体   中英

Make columns out of a date range

I have 2 tables.

The first table is a directory of my vehicles, and the second one is a table where I have the date and odometer reading of that vehicle (there might be more than 1 trip per day per vehicle). I want to have an output where I can have some analysis of whether a vehicle has odometer readings. Some of them could be reported as 0, which is what I want to be able to see or even maybe have no data at all.

TBLVehicles :

Vehicle       Group
-----------------
100             A
101             A
102             B
103             B
104             C
105             C

TBLTrips :

Vehicle    StartDate    Odometer
-----------------------------------------
100        2018-01-12    100
101        2018-05-12    1000
101        2018-05-12    1010
103        2018-05-12    500
103        2018-06-12    505
105        2018-06-12    0
105        2018-06-12    0

I would like getting an output like the one below where I can see which vehicle has a valid odometer per day in a specified date range. It would even be nice to have a total column at the end to see which ones have absolutely no odometer in that date range.

OUTPUT

Vehicle     Group   2018-01-12  2018-02-12  2018-03-12  2018-04-12  2018-05-12  2018-06-12
----------------------------------------------------------------------------------
100          A         1          0          0            0           0         0
101          A         0          0          0            0           2         0
102          B         0          0          0            0           0         0
103          B         0          0          0            0           1         1
104          C         0          0          0            0           0         0
105          C         0          0          0            0           0         2

This one would require DYNAMIC SQL.

The columns are MONTHS, but it is a small matter to switch to days.

Example

-- Generate Date Full Date Range
Declare @D1 date,@D2 date
Select @D1=Min(StartDate),@D2=Max(StartDate) from TBLTrips

Select Top (DateDiff(Month,@D1,@D2)+1) D=DateAdd(Month,-1+Row_Number() Over (Order By (Select Null)),@D1) 
 Into  #Dates
 From  master..spt_values n1


-- Generate Columns
Declare @Cols varchar(max) = stuff( (Select ','+QuoteName(D) From #Dates Order by 1 For XML Path('')),1,1,'')  

-- Generate Dynamic SQL
Declare @SQL varchar(max) = '
Select *
 From (
        Select Vehicle,StartDate,Odometer=sign(Odometer) From TBLTrips
        Union 
        Select A.Vehicle 
              ,B.D
              ,0
         From  TBLVehicles A
         Cross Join #Dates B
      ) Src
 Pivot (sum([Odometer]) For [StartDate] in (' + @Cols  + ') ) p
 Cross Apply ( Select Total = '+replace(@Cols,',','+')+' ) T 
'
--Print @SQL 
Exec(@SQL)

Returns

在此输入图像描述

EDIT - Requested UPDATE

Declare @D1 date,@D2 date
Select @D1=Min(convert(Date,TripStart)),@D2=Max(convert(Date,TripStart)) from EMS_trip_Data2

Select Top (DateDiff(DAY,@D1,@D2)+1) D=DateAdd(DAY,-1+Row_Number() Over (Order By (Select Null)),@D1) 
 Into  #Dates
 From  master..spt_values n1


-- Generate Columns
Declare @Cols varchar(max) = stuff( (Select ','+QuoteName(D) From #Dates Order by 1 For XML Path('')),1,1,'')  


-- Generate Dynamic SQL
Declare @SQL varchar(max) = '
Select *
 From (
        Select Vehicle,TripStart=convert(Date,TripStart),StartOdometer=sign(StartOdometer) From EMS_trip_Data2
        Union 
        Select A.Vehicle 
              ,B.D
              ,0
         From  VehicleSummary2 A
         Cross Join #Dates B
      ) Src
 Pivot (sum([StartOdometer]) For [TripStart] in (' + @Cols  + ') ) p
 Cross Apply ( Select Total = '+replace(@Cols,',','+')+' ) T 
'
--Print @SQL 
Exec(@SQL)

EDIT-2 With New Column

Declare @D1 date,@D2 date
Select @D1=Min(convert(Date,TripStart)),@D2=Max(convert(Date,TripStart)) from EMS_trip_Data2

Select Top (DateDiff(DAY,@D1,@D2)+1) D=DateAdd(DAY,-1+Row_Number() Over (Order By (Select Null)),@D1) 
 Into  #Dates
 From  master..spt_values n1


-- Generate Columns
Declare @Cols varchar(max) = stuff( (Select ','+QuoteName(D) From #Dates Order by 1 For XML Path('')),1,1,'')  


-- Generate Dynamic SQL
Declare @SQL varchar(max) = '
Select *
 From (
        Select Src1.*,VS.[Group]
         From  (
                Select Vehicle,TripStart=convert(Date,TripStart),StartOdometer=sign(StartOdometer) From EMS_trip_Data2
                Union 
                Select A.Vehicle 
                      ,B.D
                      ,0
                 From  VehicleSummary2 A
                 Cross Join #Dates B
                ) src1
         Join VehicleSummary2 VS on src1.Vehicle = VS.Vehicle
      ) Src
 Pivot (sum([StartOdometer]) For [TripStart] in (' + @Cols  + ') ) p
 Cross Apply ( Select Total = '+replace(@Cols,',','+')+' ) T 
'
--Print @SQL 
Exec(@SQL)

Returns

在此输入图像描述

I have prepared a script regarding what you want. I have used CTE and PIVOT to find what you want. Also, I have created "Dates" temp table. If you planning to run this report multiple time I advise you to create a static table to increase the performance. I hope this method can work for you.

IF OBJECT_ID('TEMPDB..#TBLVehicles') IS NOT NULL
    DROP TABLE #TBLVehicles;
CREATE TABLE #TBLVehicles
(
    VehicleId   INT
);

INSERT INTO #TBLVehicles
VALUES (100),(101),(102),(103),(104),(105);

IF OBJECT_ID('TEMPDB..#TBLTrips') IS NOT NULL
    DROP TABLE #TBLTrips;
CREATE TABLE #TBLTrips
(
     VehicleId  INT
    ,StartDate  DATE
    ,Odometer   INT
);

INSERT INTO #TBLTrips
VALUES
 (100,'2018-01-12',100  )
,(101,'2018-05-12',1000 )
,(101,'2018-05-12',1010 )
,(103,'2018-05-12',500  )
,(103,'2018-06-12',505  )
,(105,'2018-06-12',0    )
,(105,'2018-06-12',0    );

DECLARE @SQLString NVARCHAR(MAX)
DECLARE @DateArray VARCHAR(MAX)
DECLARE @StartDate DATE='2018-01-12'
DECLARE @EndDate DATE='2018-06-12'

IF OBJECT_ID('TEMPDB..#Dates') IS NOT NULL
    DROP TABLE #Dates;
CREATE TABLE #Dates
(
    Date    DATE
);

WHILE @StartDate<=@EndDate
BEGIN
INSERT INTO #Dates
SELECT @StartDate
SET @StartDate = DATEADD(DAY,1,@StartDate)
END

SELECT @DateArray=ISNULL(@DateArray+',','')+'['+CAST(Date AS VARCHAR)+']' FROM #Dates ;


SET @SQLString=
'
WITH CTE AS
(
SELECT V.VehicleId
    ,T1.Date
    ,CASE WHEN T2.Odometer IS NULL THEN 0 ELSE 1 END OdometerKey
FROM #TBLVehicles V
LEFT JOIN FROM #Dates T1 ON 1=1
LEFT JOIN #TBLTrips T2 ON V.VehicleId = T2.VehicleId AND T1.Date=T2.StartDate
)
SELECT * FROM CTE
PIVOT
(SUM(OdometerKey) FOR Date IN ('+@DateArray+')
) PVT
';

 EXEC SP_EXECUTESQL @SQLString
--PRINT(@SQLString)

Pivot with a left join from Vehicles to Odometer so all vehicles are represented in the output

Create Table Vehicle    
(
Id Int
)
Insert Into Vehicle Values
(100),
(101),        
(102),
(103),
(104),
(105)

Create Table Odometer
(
Vehicle_Id Int,
StartDT Date,
Odometer Int
)
Insert Into Odometer Values
(100,'2018-01-12',100),
(101,'2018-05-12',1000),
(101,'2018-05-12',1010),
(103,'2018-05-12',500),
(103,'2018-06-12',505),
(105,'2018-06-12',0),
(105,'2018-06-12',0);



Select 
   id ,
 [2018-01-12],
 [2018-02-12],
 [2018-03-12],
 [2018-04-12],
 [2018-05-12],
 [2018-06-12],
 [2018-07-12],
 [2018-08-12],
 [2018-09-12],
 [2018-10-12],
 [2018-11-12],
 [2018-12-12],
 (
    isnull([2018-01-12],0)+
    isnull([2018-02-12],0)+
    isnull([2018-03-12],0)+
    isnull([2018-04-12],0)+
    isnull([2018-05-12],0)+
    isnull([2018-06-12],0)+
    isnull([2018-07-12],0)+
    isnull([2018-08-12],0)+
    isnull([2018-09-12],0)+
    isnull([2018-10-12],0)+
    isnull([2018-11-12],0)+
    isnull([2018-12-12],0)
 ) As Total

From 
(
Select * From Vehicle v Left Join 
Odometer o On v.Id = o.Vehicle_Id 
) p
Pivot
(
count(Odometer)
For StartDT In ([2018-01-12],[2018-02-12],[2018-03-12],[2018-04-12],[2018-05-12],[2018-06-12],[2018-07-12],[2018-08-12],[2018-09-12],[2018-10-12],[2018-11-12],[2018-12-12])
) pv
order by pv.id

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM