在 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)
这是我使用动态交叉表的尝试:
示例数据:
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
我正在尝试让 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)
这是我使用动态交叉表的尝试:
示例数据:
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