【问题标题】:Make columns out of a date range使列超出日期范围
【发布时间】:2019-01-15 23:32:50
【问题描述】:

我有 2 张桌子。

第一个表是我的车辆目录,第二个表是我有该车辆的日期和里程表读数的表(每辆车每天可能有超过 1 次行程)。我想有一个输出,我可以对车辆是否有里程表读数进行一些分析。其中一些可能被报告为 0,这是我希望能够看到的,甚至可能根本没有数据。

TBLV 车辆

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

我想获得如下图所示的输出,在该输出中我可以看到在指定日期范围内每天哪辆车有有效的里程表。如果在末尾有一个总列来查看哪些在该日期范围内绝对没有里程表,那就太好了。

输出

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

【问题讨论】:

  • 您好,我认为您必须将数据透视表与动态查询一起使用。
  • 是 DAYS 或 Months 列。如果几天我们谈论的是 152 列。

标签: sql sql-server sql-server-2012


【解决方案1】:

这需要 DYNAMIC SQL。

列是MONTHS,但切换到天是小事。

示例

-- 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)

退货

编辑 - 请求更新

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

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)

退货

【讨论】:

  • 感谢@John,这种输出正是我想要的,出于某种原因,我的表格全都是 0。我试图用我的数据库在 SQLfiddle 中做这个例子,但它不会让我建立我的模式。可能是因为我的 Vehicle 列是 varchar 吗?
  • @pato.llaguno 应该没关系。列是 MONTHS 还是 DAYS ?如果是月份,所有里程表都在 12 日读取吗?
  • 我已经尝试了两种方式,pastebin.com/ZZAAkzU2 是数据库的一小部分,日期从 2018 年 12 月 1 日到 2019 年 1 月 14 日
  • @pato.llaguno 给我一点时间
  • @pato.llaguno 好的,我明白了问题所在。你有日期时间而不是日期。同样,您想要按 DAY 还是 MONTH 列?
【解决方案2】:

我已经准备了一个关于你想要什么的脚本。我用CTEPIVOT 来找到你想要的。另外,我创建了“日期”临时表。如果您计划多次运行此报告,我建议您创建一个静态表以提高性能。 我希望这种方法对你有用。

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)

【讨论】:

    【解决方案3】:

    从 Vehicles 到 Odometer 的左连接枢轴,因此所有车辆都显示在输出中

    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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-12
      • 1970-01-01
      • 1970-01-01
      • 2017-07-29
      • 1970-01-01
      相关资源
      最近更新 更多