在 SQL 个动态(日历)数据透视表上获取总计

Get Total on SQL Dynamic (Calendar) Pivot Tables

我正在尝试让 SQL 服务器动态枢轴 table 工作,它允许我对列数进行计数然后求和。枢轴 table 的目的是创建一个报告,其中包含个人在一个城市停留的所有天数和总天数(一个月)。因此,例如,A 人在 6 月每天入住 - 总数将是 30.Person B 仅在 6 月 3 日开始入住 - 总数将是 27 等等。数据 table 仅包含姓名, ArriveDate, DepartDate...一个月中的几天是通过 SQL 查询创建的。

+------+------------+------------+-------+-------+-------+-----+-------+-------+-------+
| Name | ArriveDate | DepartDate | 06-01 | 06-02 | 06-03 | ... | 06-29 | 06-30 | Total |
+------+------------+------------+-------+-------+-------+-----+-------+-------+-------+
| A    | 2014-06-01 | 2014-06-23 |     1 |     1 |     1 | ... |     1 |     1 |    30 |
| B    | 2014-06-02 | 2014-06-23 |     0 |     1 |     1 | ... |     1 |     1 |    27 |
| C    | 2014-06-02 | 2014-06-23 |     0 |     0 |     0 | ... |     1 |     1 |    16 |
+------+------------+------------+-------+-------+-------+-----+-------+-------+-------+

这是我目前的查询:

DROP TABLE #tempDates

DECLARE @cols AS NVARCHAR(MAX),
        @query  AS NVARCHAR(MAX)

;WITH cte (datelist, maxdate) AS
(
    SELECT MIN(ArriveDate) datelist,
           EOMONTH(GETDATE()) AS maxdate
    FROM Reservation
    UNION ALL
    SELECT dateadd(dd, 1, datelist), maxdate
    FROM cte
    WHERE datelist < maxdate
) 
SELECT c.datelist
INTO #tempDates
FROM cte c

SELECT @cols = STUFF(( SELECT distinct ',' + QUOTENAME(convert(CHAR(10), datelist, 120)) 
                       FROM #tempDates
                       FOR XML PATH(''), TYPE
                      ).value('.', 'NVARCHAR(MAX)') 
                     ,1,1,'')



SET @query = 'SELECT ID,
                     ArriveDate,
                     DepartDate,                     
                     ' + @cols + '
              FROM 
              (         
                SELECT r.ID,
                       r.ArriveDate,
                       r.DepartDate,
                       d.datelist, 
                       convert(CHAR(10), datelist, 120) PivotDate
                FROM 
                Reservation r       
                LEFT JOIN
                #tempDates d
                    ON d.datelist BETWEEN rg.ArriveDate AND GETDATE()
            ) x
            pivot 
            (
                COUNT(datelist)
                FOR PivotDate IN (' + @cols + ')
            ) p '

EXECUTE (@query)

这是我使用动态交叉表的尝试:

SQL Fiddle

示例数据:

Name ArriveDate DepartDate
---- ---------- ----------
A    2015-07-01 2015-07-23
B    2015-07-02 2015-07-04
C    2015-07-03 2015-07-31

动态交叉表解决方案:

DECLARE @minDate AS DATE,
        @maxDate AS DATE

SELECT  @minDate = DATEADD(DAY, 1, EOMONTH(GETDATE(), -1)),
        @maxDate = EOMONTH(GETDATE())

CREATE TABLE #dates(dt DATE)

DECLARE @sql1 VARCHAR(MAX) = '',
        @sql2 VARCHAR(MAX) = '',
        @sql3 VARCHAR(MAX) = '';

WITH E1(N) AS(
    SELECT 1 FROM(VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))t(N)
),
E2(N) AS(SELECT 1 FROM E1 a CROSS JOIN E1 b),
E4(N) AS(SELECT 1 FROM E2 a CROSS JOIN E2 b),
E8(N) AS(SELECT 1 FROM E4 a CROSS JOIN E4 b),
Tally(N) AS(
    SELECT TOP(DATEDIFF(DAY, @minDate, @maxDate) + 1)
        ROW_NUMBER() OVER(ORDER BY (SELECT NULL))
    FROM E8
)
INSERT INTO #dates
    SELECT DATEADD(DAY, N - 1, @minDate)
    FROM Tally

SELECT @sql1 =
'SELECT
    r.Name
    , r.ArriveDate
    , r.DepartDate' + CHAR(10)

SELECT @sql2 = @sql2 +
'   , SUM(CASE WHEN d.dt = CAST(''' + CONVERT(VARCHAR(8), dt, 112) + ''' AS DATE) THEN 1 ELSE 0 END) AS ' 
    + QUOTENAME(CONVERT(VARCHAR(10), dt, 120)) + CHAR(10)
FROM #dates
ORDER BY dt

SELECT @sql2 = @sql2 +
'   , COUNT(d.dt) AS [total]' + CHAR(10)

SELECT @sql3 =
'FROM Reservation r
LEFT JOIN #dates d
    ON d.dt BETWEEN r.ArriveDate AND r.DepartDate
GROUP BY
    r.Name, r.ArriveDate, r.DepartDate'

PRINT (@sql1 + @sql2 + @sql3)
EXEC (@sql1 + @sql2 + @sql3)

DROP TABLE #dates

结果:

Name ArriveDate DepartDate 2015-07-01  2015-07-02  2015-07-03  ..... 2015-07-29  2015-07-30  2015-07-31  total
---- ---------- ---------- ----------- ----------- ----------- ..... ----------- ----------- ----------- -----------
A    2015-07-01 2015-07-23 1           1           1           ..... 0           0           0           23
B    2015-07-02 2015-07-04 0           1           1           ..... 0           0           0           3
C    2015-07-03 2015-07-31 0           0           1           ..... 1           1           1           29